- Move completed review/plan artifacts to archive - Update control-plane main, wire, edge components - Update edge bootstrap runtime and controlplane connector - Update generated proto files
295 lines
7.8 KiB
Go
295 lines
7.8 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"iop/packages/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()
|
|
}
|