Files
worker/internal/distworker/config_test.go
Gleb Tv 2c7a0236da feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
2026-07-13 17:55:14 +03:00

198 строки
6.6 KiB
Go

package distworker
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestHTTPConfigFromEnv_Defaults verifies the documented defaults when no
// HTTP-related env vars are set: 0.0.0.0:27401 and empty URL / login /
// password. The default port was bumped from 7401 to 27401 to avoid
// colliding with the main RSMon app when the worker is co-located.
func TestHTTPConfigFromEnv_Defaults(t *testing.T) {
t.Setenv("WORKER_HOST", "")
t.Setenv("WORKER_PORT", "")
t.Setenv("WORKER_URL", "")
t.Setenv("WORKER_LOGIN", "")
t.Setenv("WORKER_PASSWORD", "")
cfg := HTTPConfigFromEnv()
assert.Equal(t, DefaultHTTPHost, cfg.Host, "host should default to 0.0.0.0")
assert.Equal(t, DefaultHTTPPort, cfg.Port, "port should default to 27401")
assert.Equal(t, "", cfg.URL)
assert.Equal(t, "", cfg.Login)
assert.Equal(t, "", cfg.Password)
assert.False(t, cfg.IsAuthConfigured())
}
// TestHTTPConfigFromEnv_Overrides verifies the env vars win over the
// defaults when explicitly set, including a non-default port.
func TestHTTPConfigFromEnv_Overrides(t *testing.T) {
t.Setenv("WORKER_HOST", "127.0.0.1")
t.Setenv("WORKER_PORT", "9100")
t.Setenv("WORKER_URL", "https://worker.example.com")
t.Setenv("WORKER_LOGIN", "ops")
t.Setenv("WORKER_PASSWORD", "s3cret")
cfg := HTTPConfigFromEnv()
assert.Equal(t, "127.0.0.1", cfg.Host)
assert.Equal(t, 9100, cfg.Port)
assert.Equal(t, "https://worker.example.com", cfg.URL)
assert.Equal(t, "ops", cfg.Login)
assert.Equal(t, "s3cret", cfg.Password)
assert.True(t, cfg.IsAuthConfigured())
}
// TestHTTPConfigFromEnv_PortGarbageFallsBackToDefault covers the "operator
// fat-fingered WORKER_PORT" case: the helper must not panic and must
// fall back to DefaultHTTPPort instead of starting on a zero port.
func TestHTTPConfigFromEnv_PortGarbageFallsBackToDefault(t *testing.T) {
t.Setenv("WORKER_PORT", "not-a-port")
cfg := HTTPConfigFromEnv()
assert.Equal(t, DefaultHTTPPort, cfg.Port,
"unparseable WORKER_PORT should fall back to the default")
}
// TestHTTPConfigFromEnv_PortOutOfRangeFallsBackToDefault covers zero and
// out-of-range port values which are rejected by url/strconv semantics.
func TestHTTPConfigFromEnv_PortOutOfRangeFallsBackToDefault(t *testing.T) {
for _, v := range []string{"0", "-1", "70000"} {
t.Run("port="+v, func(t *testing.T) {
t.Setenv("WORKER_PORT", v)
cfg := HTTPConfigFromEnv()
assert.Equal(t, DefaultHTTPPort, cfg.Port,
"WORKER_PORT=%q should fall back to the default", v)
})
}
}
// TestValidateHTTPConfig_LoginXORPasswordRejected covers the central
// invariant: a mixed login/password state is a config bug and must
// fail fast.
func TestValidateHTTPConfig_LoginXORPasswordRejected(t *testing.T) {
t.Run("only login set", func(t *testing.T) {
err := ValidateHTTPConfig(HTTPConfig{
Host: "0.0.0.0", Port: 27401,
Login: "ops", Password: "",
}, false)
require.Error(t, err)
assert.Contains(t, err.Error(), "WORKER_LOGIN and WORKER_PASSWORD")
})
t.Run("only password set", func(t *testing.T) {
err := ValidateHTTPConfig(HTTPConfig{
Host: "0.0.0.0", Port: 27401,
Login: "", Password: "s3cret",
}, false)
require.Error(t, err)
assert.Contains(t, err.Error(), "WORKER_LOGIN and WORKER_PASSWORD")
})
}
// TestValidateHTTPConfig_BothEmptyAllowedWhenNotListening reflects the
// Task 2 deferral: while the HTTP listener is not started (Task 3),
// leaving both unset is permitted so a worker that does not yet need
// the web app can still start.
func TestValidateHTTPConfig_BothEmptyAllowedWhenNotListening(t *testing.T) {
err := ValidateHTTPConfig(HTTPConfig{
Host: "0.0.0.0", Port: 27401,
Login: "", Password: "",
}, false)
assert.NoError(t, err)
}
// TestValidateHTTPConfig_BothEmptyRejectedWhenListening confirms that
// when Task 3 actually starts the listener, an unauthenticated listener
// is refused.
func TestValidateHTTPConfig_BothEmptyRejectedWhenListening(t *testing.T) {
err := ValidateHTTPConfig(HTTPConfig{
Host: "0.0.0.0", Port: 27401,
Login: "", Password: "",
}, true)
require.Error(t, err)
assert.Contains(t, err.Error(), "refused to start")
}
// TestValidateHTTPConfig_URLAbsoluteRequired covers the relative-URL
// rejection: peer workers and the main app need a concrete origin.
func TestValidateHTTPConfig_URLAbsoluteRequired(t *testing.T) {
cases := []struct {
name string
url string
}{
{"relative path", "/worker"},
{"missing scheme", "example.com"},
{"missing host", "https://"},
{"empty after trim", ""},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cfg := HTTPConfig{Host: "0.0.0.0", Port: 27401, URL: tc.url}
err := ValidateHTTPConfig(cfg, false)
// empty url is allowed
if tc.url == "" {
assert.NoError(t, err)
return
}
require.Error(t, err)
})
}
}
// TestValidateHTTPConfig_URLSchemeAllowed covers the two accepted schemes.
func TestValidateHTTPConfig_URLSchemeAllowed(t *testing.T) {
for _, scheme := range []string{"http", "https"} {
t.Run(scheme, func(t *testing.T) {
cfg := HTTPConfig{
Host: "0.0.0.0", Port: 27401,
URL: scheme + "://localhost:27401",
}
assert.NoError(t, ValidateHTTPConfig(cfg, false))
})
}
}
// TestValidateHTTPConfig_URLSchemeRejected covers non-http(s) schemes.
func TestValidateHTTPConfig_URLSchemeRejected(t *testing.T) {
for _, scheme := range []string{"ftp", "file", "ws", "wss"} {
t.Run(scheme, func(t *testing.T) {
cfg := HTTPConfig{
Host: "0.0.0.0", Port: 27401,
URL: scheme + "://localhost:27401",
}
err := ValidateHTTPConfig(cfg, false)
require.Error(t, err)
assert.Contains(t, strings.ToLower(err.Error()), "scheme must be http or https")
})
}
}
// TestWarnInsecurePublicURL exercises the warning helper: only http on
// non-loopback hosts should warn. The returned host is the parsed host
// (host:port when present) so the caller can log a useful target.
func TestWarnInsecurePublicURL(t *testing.T) {
cases := []struct {
name string
url string
wantWarn bool
wantHost string
}{
{"empty", "", false, ""},
{"https production", "https://worker.example.com", false, "worker.example.com"},
{"http loopback", "http://localhost:27401", false, "localhost:27401"},
{"http 127.0.0.1", "http://127.0.0.1:27401", false, "127.0.0.1:27401"},
{"http 0.0.0.0", "http://0.0.0.0:27401", false, "0.0.0.0:27401"},
{"http production", "http://worker.example.com", true, "worker.example.com"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
host, warn := WarnInsecurePublicURL(tc.url)
assert.Equal(t, tc.wantWarn, warn)
assert.Equal(t, tc.wantHost, host)
})
}
}