134 lines
4.4 KiB
Go
134 lines
4.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"scm.bstein.dev/bstein/ananke/internal/config"
|
|
"scm.bstein.dev/bstein/ananke/internal/metrics"
|
|
"scm.bstein.dev/bstein/ananke/internal/state"
|
|
)
|
|
|
|
// TestDaemonRunRejectsDisabledUPS runs one orchestration or CLI step.
|
|
// Signature: TestDaemonRunRejectsDisabledUPS(t *testing.T).
|
|
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
|
|
func TestDaemonRunRejectsDisabledUPS(t *testing.T) {
|
|
d := &Daemon{
|
|
cfg: config.Config{
|
|
UPS: config.UPS{Enabled: false},
|
|
},
|
|
log: log.New(io.Discard, "", 0),
|
|
}
|
|
if err := d.Run(context.Background()); err == nil {
|
|
t.Fatalf("expected UPS-disabled run to fail")
|
|
}
|
|
}
|
|
|
|
// TestDaemonRunRejectsMissingTargets runs one orchestration or CLI step.
|
|
// Signature: TestDaemonRunRejectsMissingTargets(t *testing.T).
|
|
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
|
|
func TestDaemonRunRejectsMissingTargets(t *testing.T) {
|
|
d := &Daemon{
|
|
cfg: config.Config{
|
|
UPS: config.UPS{Enabled: true},
|
|
},
|
|
log: log.New(io.Discard, "", 0),
|
|
}
|
|
if err := d.Run(context.Background()); err == nil {
|
|
t.Fatalf("expected empty-target run to fail")
|
|
}
|
|
}
|
|
|
|
// TestDaemonTargetList runs one orchestration or CLI step.
|
|
// Signature: TestDaemonTargetList(t *testing.T).
|
|
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
|
|
func TestDaemonTargetList(t *testing.T) {
|
|
d := &Daemon{
|
|
targets: []Target{
|
|
{Name: "Pyrphoros", Target: "pyrphoros@localhost"},
|
|
{Name: "Statera", Target: "statera@localhost"},
|
|
},
|
|
}
|
|
got := d.targetList()
|
|
if !strings.Contains(got, "Pyrphoros=pyrphoros@localhost") || !strings.Contains(got, "Statera=statera@localhost") {
|
|
t.Fatalf("unexpected target list: %q", got)
|
|
}
|
|
}
|
|
|
|
// TestDaemonResolveSSHPathsPreferConfigured runs one orchestration or CLI step.
|
|
// Signature: TestDaemonResolveSSHPathsPreferConfigured(t *testing.T).
|
|
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
|
|
func TestDaemonResolveSSHPathsPreferConfigured(t *testing.T) {
|
|
d := &Daemon{
|
|
cfg: config.Config{
|
|
SSHConfigFile: "/tmp/custom-ssh-config",
|
|
SSHIdentityFile: "/tmp/custom-ssh-key",
|
|
},
|
|
}
|
|
if got := d.resolveSSHConfigFile(); got != "/tmp/custom-ssh-config" {
|
|
t.Fatalf("unexpected config path: %q", got)
|
|
}
|
|
if got := d.resolveSSHIdentityFile(); got != "/tmp/custom-ssh-key" {
|
|
t.Fatalf("unexpected identity path: %q", got)
|
|
}
|
|
}
|
|
|
|
// TestStartMetricsServerRequiresBindAddress runs one orchestration or CLI step.
|
|
// Signature: TestStartMetricsServerRequiresBindAddress(t *testing.T).
|
|
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
|
|
func TestStartMetricsServerRequiresBindAddress(t *testing.T) {
|
|
d := &Daemon{
|
|
cfg: config.Config{
|
|
Metrics: config.Metrics{
|
|
Enabled: true,
|
|
BindAddr: "",
|
|
Path: "/metrics",
|
|
},
|
|
},
|
|
log: log.New(io.Discard, "", 0),
|
|
exporter: nil,
|
|
}
|
|
d.exporter = d.ensureExporterForTest()
|
|
if err := d.startMetricsServer(); err == nil {
|
|
t.Fatalf("expected missing bind address error")
|
|
}
|
|
}
|
|
|
|
// TestTriggerShutdownSkipsDuplicateWhenIntentActive runs one orchestration or CLI step.
|
|
// Signature: TestTriggerShutdownSkipsDuplicateWhenIntentActive(t *testing.T).
|
|
// Why: keeps behavior explicit so startup/shutdown workflows remain maintainable as services evolve.
|
|
func TestTriggerShutdownSkipsDuplicateWhenIntentActive(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
intentPath := filepath.Join(tmp, "intent.json")
|
|
if err := state.MustWriteIntent(intentPath, state.IntentShuttingDown, "already-running", "test"); err != nil {
|
|
t.Fatalf("seed intent: %v", err)
|
|
}
|
|
d := &Daemon{
|
|
cfg: config.Config{
|
|
State: config.State{
|
|
IntentPath: intentPath,
|
|
},
|
|
},
|
|
log: log.New(io.Discard, "", 0),
|
|
exporter: nil,
|
|
}
|
|
d.exporter = d.ensureExporterForTest()
|
|
if err := d.triggerShutdown(context.Background(), "duplicate-check"); err != nil {
|
|
t.Fatalf("expected duplicate shutdown trigger to be ignored: %v", err)
|
|
}
|
|
}
|
|
|
|
// ensureExporterForTest runs one orchestration or CLI step.
|
|
// Signature: (d *Daemon) ensureExporterForTest() *metrics.Exporter.
|
|
// Why: local helper keeps setup concise while preserving explicit behavior in each test.
|
|
func (d *Daemon) ensureExporterForTest() *metrics.Exporter {
|
|
if d.exporter == nil {
|
|
d.exporter = metrics.New()
|
|
}
|
|
return d.exporter
|
|
}
|