package bootstrap import ( "context" "net" "net/http" "os" "path/filepath" "strings" "testing" "time" "iop/packages/go/config" ) func newTestConfig() *config.EdgeConfig { return &config.EdgeConfig{ Server: config.EdgeServerConf{Listen: "127.0.0.1:0"}, Logging: config.LoggingConf{Level: "error"}, Metrics: config.MetricsConf{Port: 0}, Nodes: []config.NodeDefinition{ {ID: "node-1", Alias: "alias-1", Token: "tok-1"}, }, } } func TestNewRuntimeSeedsNodeStore(t *testing.T) { cfg := newTestConfig() rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if rt.NodeStore == nil { t.Fatal("NodeStore is nil") } rec, ok := rt.NodeStore.FindByToken("tok-1") if !ok { t.Fatal("expected node record seeded by token tok-1") } if rec.ID != "node-1" || rec.Alias != "alias-1" { t.Errorf("unexpected record: id=%q alias=%q", rec.ID, rec.Alias) } if rt.Registry == nil || rt.EventBus == nil || rt.Service == nil || rt.Server == nil || rt.Logger == nil { t.Error("runtime dependencies must be wired by NewRuntime") } } func TestRuntimeWiresTransportHandlers(t *testing.T) { cfg := newTestConfig() rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if rt.Server.HasRunEventHandler() || rt.Server.HasNodeEventHandler() { t.Fatal("handlers must not be wired before wireHandlers") } rt.wireHandlers() if !rt.Server.HasRunEventHandler() { t.Error("run event handler not wired") } if !rt.Server.HasNodeEventHandler() { t.Error("node event handler not wired") } } func TestNewRuntimeWiresInputManager(t *testing.T) { cfg := newTestConfig() rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if rt.Input == nil { t.Fatal("Input manager is nil") } if rt.Input.OpenAI == nil { t.Fatal("Input.OpenAI is nil") } if rt.Input.A2A == nil { t.Fatal("Input.A2A is nil") } } func TestRuntimeKeepsListenersAfterStartupContextCancel(t *testing.T) { edgeAddr := freeTCPAddr(t) openAIAddr := freeTCPAddr(t) cfg := newTestConfig() cfg.Server.Listen = edgeAddr cfg.OpenAI = config.EdgeOpenAIConf{ Enabled: true, Listen: openAIAddr, } rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } startupCtx, cancelStartup := context.WithCancel(context.Background()) if err := rt.Start(startupCtx); err != nil { t.Fatalf("Start: %v", err) } defer func() { if err := rt.Stop(); err != nil { t.Fatalf("Stop: %v", err) } }() cancelStartup() conn, err := net.DialTimeout("tcp", edgeAddr, time.Second) if err != nil { t.Fatalf("edge listener closed with startup context: %v", err) } _ = conn.Close() client := &http.Client{Timeout: time.Second} resp, err := client.Get("http://" + openAIAddr + "/healthz") if err != nil { t.Fatalf("openai listener closed with startup context: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("openai health status = %d, want %d", resp.StatusCode, http.StatusOK) } } func TestNewRuntimeWritesLogsToConfiguredPath(t *testing.T) { dir := t.TempDir() logPath := filepath.Join(dir, "logs", "edge.log") cfg := newTestConfig() cfg.Logging.Level = "info" cfg.Logging.Path = logPath rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } rt.Logger.Info("runtime-log-entry") _ = rt.Logger.Sync() info, err := os.Stat(logPath) if err != nil { t.Fatalf("stat log path: %v", err) } if info.Size() == 0 { t.Fatalf("log file empty: %s", logPath) } contents, err := os.ReadFile(logPath) if err != nil { t.Fatalf("read log file: %v", err) } if !strings.Contains(string(contents), "runtime-log-entry") { t.Fatalf("expected runtime-log-entry in log file; contents=%q", string(contents)) } } func TestNewRuntimeRejectsDuplicateToken(t *testing.T) { cfg := &config.EdgeConfig{ Server: config.EdgeServerConf{Listen: "127.0.0.1:0"}, Logging: config.LoggingConf{Level: "error"}, Nodes: []config.NodeDefinition{ {ID: "node-1", Alias: "a1", Token: "dup"}, {ID: "node-2", Alias: "a2", Token: "dup"}, }, } if _, err := NewRuntime(cfg); err == nil { t.Fatal("expected error on duplicate token seed") } } func freeTCPAddr(t *testing.T) string { t.Helper() ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen free tcp addr: %v", err) } addr := ln.Addr().String() if err := ln.Close(); err != nil { t.Fatalf("close free tcp addr listener: %v", err) } return addr } // TestNewRuntimeWiresControlPlaneConnector verifies that NewRuntime always // wires a non-nil ControlPlane connector regardless of config. func TestNewRuntimeWiresControlPlaneConnector(t *testing.T) { cfg := newTestConfig() rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if rt.ControlPlane == nil { t.Fatal("expected non-nil ControlPlane connector") } } // TestNewRuntimeWiresStatusProviderIntoConnector verifies that NewRuntime wires // the edge service as the Control Plane connector's status provider, so status // requests are answered from the Edge-owned node snapshot surface. func TestNewRuntimeWiresStatusProviderIntoConnector(t *testing.T) { cfg := newTestConfig() rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if !rt.ControlPlane.StatusProviderConfigured() { t.Fatal("expected NewRuntime to wire a status provider into the connector") } } // TestNewRuntimeWiresNodeEventBusIntoConnector verifies that node lifecycle // events are relayed through the shared events.Bus boundary. func TestNewRuntimeWiresNodeEventBusIntoConnector(t *testing.T) { cfg := newTestConfig() rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if !rt.ControlPlane.NodeEventBusConfigured() { t.Fatal("expected NewRuntime to wire the node event bus into the connector") } } // TestRuntimeStartsControlPlaneConnectorWhenEnabled verifies that Start does // not fail when a Control Plane connector is configured with a local fake // server accepting hello. Uses a local TCP fake endpoint. func TestRuntimeStartsControlPlaneConnectorWhenEnabled(t *testing.T) { // Start a minimal fake Control Plane server. ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen fake cp: %v", err) } cpAddr := ln.Addr().String() go func() { // Accept and immediately close; connector will fail hello and retry, // but Start() itself must succeed (connector errors are async). conn, err := ln.Accept() if err != nil { return } _ = conn.Close() _ = ln.Close() }() cfg := newTestConfig() cfg.ControlPlane = config.EdgeControlPlaneConf{ Enabled: true, WireAddr: cpAddr, ReconnectIntervalSec: 60, } rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } if !rt.ControlPlane.IsEnabled() { t.Fatal("expected connector IsEnabled()==true") } ctx := context.Background() if err := rt.Start(ctx); err != nil { t.Fatalf("Start with enabled connector: %v", err) } if err := rt.Stop(); err != nil { t.Fatalf("Stop: %v", err) } } // TestRuntimeStopsControlPlaneConnector verifies that Stop() reaches the // ControlPlane connector. A disabled connector is used so no real network // activity occurs. func TestRuntimeStopsControlPlaneConnector(t *testing.T) { cfg := newTestConfig() // disabled connector; Start/Stop must be no-ops on the connector side. cfg.ControlPlane = config.EdgeControlPlaneConf{ Enabled: false, WireAddr: "", } rt, err := NewRuntime(cfg) if err != nil { t.Fatalf("NewRuntime: %v", err) } ctx := context.Background() if err := rt.Start(ctx); err != nil { t.Fatalf("Start: %v", err) } if err := rt.Stop(); err != nil { t.Fatalf("Stop: %v", err) } // After Stop, the connector must be in a stopped-or-no-op state. // For disabled connector, CurrentState is Disconnected (never started). // Calling Stop again must not panic. rt.ControlPlane.Stop() }