Files
monlet/agent/internal/config/config_test.go
2026-05-27 10:01:59 +04:00

348 lines
6.7 KiB
Go

package config
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
)
func writeTmp(t *testing.T, body string) string {
t.Helper()
p := filepath.Join(t.TempDir(), "c.toml")
if err := os.WriteFile(p, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
return p
}
const minimal = `
agent_id = "host-1"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`
func TestLoadMinimal(t *testing.T) {
c, err := Load(writeTmp(t, minimal))
if err != nil {
t.Fatal(err)
}
if !c.PushesToServer() || c.ExposesMetrics() {
t.Errorf("features: push=%v metrics=%v", c.PushesToServer(), c.ExposesMetrics())
}
if c.Server.HeartbeatInterval.Duration == 0 {
t.Error("default heartbeat not applied")
}
if !c.Checks[0].NotificationsOn() {
t.Error("notifications should default on")
}
}
func TestLoadMultilineCommand(t *testing.T) {
c, err := Load(writeTmp(t, `
agent_id = "host-1"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = '''
set -eu
echo ok
'''
interval = "10s"
timeout = "5s"
`))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(c.Checks[0].Command, "echo ok") {
t.Fatalf("command not loaded: %q", c.Checks[0].Command)
}
if got := c.Checks[0].Argv(); len(got) != 3 || got[0] != "/bin/sh" || got[1] != "-c" {
t.Fatalf("argv: %#v", got)
}
}
func TestCommandArrayRejected(t *testing.T) {
_, err := Load(writeTmp(t, `
agent_id = "host-1"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = ["true"]
interval = "10s"
timeout = "5s"
`))
if err == nil {
t.Fatal("expected command array to be rejected")
}
}
func TestLoadLabels(t *testing.T) {
c, err := Load(writeTmp(t, `
agent_id = "host-1"
state_dir = "/tmp/x"
[labels]
env = "prod"
role = "api"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err != nil {
t.Fatal(err)
}
labels := c.HeartbeatLabels("1.2.3")
if labels["env"] != "prod" || labels["role"] != "api" {
t.Fatalf("configured labels missing: %#v", labels)
}
if labels[AgentVersionLabel] != "1.2.3" {
t.Fatalf("version label missing: %#v", labels)
}
}
func TestHeartbeatLabelsVersionLabelWins(t *testing.T) {
c := &Config{Labels: map[string]string{AgentVersionLabel: "manual"}}
labels := c.HeartbeatLabels("real")
if labels[AgentVersionLabel] != "real" {
t.Fatalf("version label must be generated from binary version, got %q", labels[AgentVersionLabel])
}
}
func TestValidateBadID(t *testing.T) {
body := minimal + "\n"
_, err := Load(writeTmp(t, body[:0]+`
agent_id = "bad id!"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err == nil {
t.Fatal("expected error for bad agent_id")
}
}
func TestValidateTimeoutExceedsInterval(t *testing.T) {
_, err := Load(writeTmp(t, `
agent_id = "x"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "5s"
timeout = "10s"
`))
if err == nil {
t.Fatal("expected error for timeout > interval")
}
}
func TestValidateBadLabelKey(t *testing.T) {
_, err := Load(writeTmp(t, `
agent_id = "x"
state_dir = "/tmp/x"
[labels]
"bad key" = "x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err == nil {
t.Fatal("expected bad label key error")
}
}
func TestValidateSensitiveLabelKey(t *testing.T) {
for _, key := range []string{"token", "api_key", "api-key", "password", "credential", "authorization", "cookie"} {
if err := validateLabels(map[string]string{key: "x"}); err == nil {
t.Fatalf("expected sensitive label key error for %q", key)
}
}
}
func TestValidateLabelsReservedSlot(t *testing.T) {
labels := make(map[string]string, maxLabels)
for i := 0; i < maxLabels; i++ {
labels[fmt.Sprintf("k%d", i)] = "v"
}
if err := validateLabels(labels); err == nil {
t.Fatal("expected reserved label slot error")
}
delete(labels, "k0")
labels[AgentVersionLabel] = "manual"
if err := validateLabels(labels); err != nil {
t.Fatal(err)
}
}
func TestValidateLabelValueMaxLength(t *testing.T) {
if err := validateLabels(map[string]string{"note": strings.Repeat("ю", maxLabelValueLen)}); err != nil {
t.Fatal(err)
}
if err := validateLabels(map[string]string{"note": strings.Repeat("x", maxLabelValueLen+1)}); err == nil {
t.Fatal("expected label value length error")
}
}
func TestPrometheusOnlyAllowsMissingServer(t *testing.T) {
c, err := Load(writeTmp(t, `
state_dir = "/tmp/x"
[server]
enabled = false
[metrics]
enabled = true
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err != nil {
t.Fatal(err)
}
if c.PushesToServer() {
t.Fatal("metrics-only config must not push")
}
if !c.ExposesMetrics() {
t.Fatal("metrics-only config must expose metrics")
}
}
func TestPushOnlyRequiresServer(t *testing.T) {
_, err := Load(writeTmp(t, `
state_dir = "/tmp/x"
[server]
enabled = true
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err == nil {
t.Fatal("push_only must require server.url/token")
}
}
func TestUnknownConfigKeyRejected(t *testing.T) {
_, err := Load(writeTmp(t, `
mode = "hybrid"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err == nil {
t.Fatal("expected unknown key error")
}
}
func TestNotificationsEnabledCanDisable(t *testing.T) {
c, err := Load(writeTmp(t, minimal+`
notifications_enabled = false
`))
if err != nil {
t.Fatal(err)
}
if c.Checks[0].NotificationsOn() {
t.Fatal("notifications_enabled=false was not applied")
}
}
func TestExposesMetricsGate(t *testing.T) {
cases := []struct {
server bool
metrics bool
want bool
}{
{true, true, true},
{true, false, false},
{false, true, true},
{false, false, false},
}
for _, tc := range cases {
c := &Config{Server: ServerConfig{Enabled: &tc.server}, Metrics: MetricsConfig{Enabled: tc.metrics}}
if got := c.ExposesMetrics(); got != tc.want {
t.Errorf("server=%v metrics=%v want=%v got=%v", tc.server, tc.metrics, tc.want, got)
}
}
}
func TestValidateDuplicateCheckID(t *testing.T) {
_, err := Load(writeTmp(t, `
agent_id = "x"
state_dir = "/tmp/x"
[server]
enabled = true
url = "http://localhost"
token = "t"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
[[checks]]
id = "c1"
command = "true"
interval = "10s"
timeout = "5s"
`))
if err == nil {
t.Fatal("expected duplicate id error")
}
}