package node_test import ( "strings" "testing" edgenode "iop/apps/edge/internal/node" "iop/packages/go/config" ) func TestLoadFromConfig_WorkspaceRefValidation(t *testing.T) { workspace := func(ref string) config.WorkspaceDefinition { return config.WorkspaceDefinition{Ref: ref} } for _, tc := range []struct { name string defs []config.NodeDefinition wantErr string }{ { name: "empty ref rejected", defs: []config.NodeDefinition{{ Alias: "alpha", Token: "token-alpha", Workspaces: []config.WorkspaceDefinition{workspace(" ")}, }}, wantErr: "ref must not be empty after trim", }, { name: "duplicate ref within node rejected", defs: []config.NodeDefinition{{ Alias: "alpha", Token: "token-alpha", Workspaces: []config.WorkspaceDefinition{workspace("ws-a"), workspace("ws-a")}, }}, wantErr: "duplicate workspace ref", }, { name: "whitespace canonical duplicate within node rejected", defs: []config.NodeDefinition{{ Alias: "alpha", Token: "token-alpha", Workspaces: []config.WorkspaceDefinition{workspace(" ws-a "), workspace("ws-a")}, }}, wantErr: "duplicate workspace ref", }, { name: "duplicate ref across nodes rejected", defs: []config.NodeDefinition{ {Alias: "alpha", Token: "token-alpha", Workspaces: []config.WorkspaceDefinition{workspace("ws-a")}}, {Alias: "beta", Token: "token-beta", Workspaces: []config.WorkspaceDefinition{workspace("ws-a")}}, }, wantErr: "duplicate workspace ref", }, { name: "unique refs are trimmed and accepted", defs: []config.NodeDefinition{ {Alias: "alpha", Token: "token-alpha", Workspaces: []config.WorkspaceDefinition{workspace(" ws-a ")}}, {Alias: "beta", Token: "token-beta", Workspaces: []config.WorkspaceDefinition{workspace("ws-b")}}, }, }, } { t.Run(tc.name, func(t *testing.T) { store, err := edgenode.LoadFromConfig(tc.defs) if tc.wantErr != "" { if err == nil { t.Fatal("expected LoadFromConfig error") } if !strings.Contains(err.Error(), tc.wantErr) { t.Fatalf("expected error containing %q, got %v", tc.wantErr, err) } return } if err != nil { t.Fatalf("LoadFromConfig: %v", err) } _, resolved, err := store.ResolveWorkspace("ws-a") if err != nil { t.Fatalf("ResolveWorkspace: %v", err) } if resolved.Ref != "ws-a" { t.Fatalf("resolved ref = %q, want ws-a", resolved.Ref) } }) } } func TestNodeStore_ResolveWorkspaceImmutableCopies(t *testing.T) { defs := []config.NodeDefinition{{ ID: "node-alpha", Alias: "alpha", Token: "token-alpha", Workspaces: []config.WorkspaceDefinition{{ Ref: "ws-alpha", Platform: "darwin", Root: "/Users/operator/projects/alpha", Operations: []config.WorkspaceOperation{config.WorkspaceOpRead, config.WorkspaceOpCommand}, Commands: []config.WorkspaceCommandDefinition{{ID: "read-file", Executable: "/usr/bin/cat", Args: []string{"README.md"}}}, EnvironmentAllowlist: []string{"HOME"}, MaxReadBytes: 1024, MaxOutputBytes: 2048, MaxCommandTimeoutMS: 3000, }}, }} store, err := edgenode.LoadFromConfig(defs) if err != nil { t.Fatalf("LoadFromConfig: %v", err) } // Mutate the caller-owned config after loading; NodeStore must retain its snapshot. defs[0].Workspaces[0].Ref = "source-mutated" defs[0].Workspaces[0].Operations[0] = config.WorkspaceOpWrite defs[0].Workspaces[0].Commands[0].Args[0] = "source-mutated.md" defs[0].Workspaces[0].EnvironmentAllowlist[0] = "SOURCE_MUTATED" owner, workspace, err := store.ResolveWorkspace("ws-alpha") if err != nil { t.Fatalf("ResolveWorkspace: %v", err) } if owner.ID != "node-alpha" { t.Fatalf("owner ID = %q, want node-alpha", owner.ID) } // Mutate every returned catalog surface, then resolve again to ensure no // store-owned workspace data escaped after the read lock was released. workspace.Ref = "returned-mutated" workspace.Operations[0] = config.WorkspaceOpWrite workspace.Commands[0].Args[0] = "returned-mutated.md" workspace.EnvironmentAllowlist[0] = "RETURNED_MUTATED" owner.Workspaces[0].Ref = "owner-mutated" owner.Workspaces[0].Operations[0] = config.WorkspaceOpWrite owner.Workspaces[0].Commands[0].Args[0] = "owner-mutated.md" owner.Workspaces[0].EnvironmentAllowlist[0] = "OWNER_MUTATED" owner, workspace, err = store.ResolveWorkspace("ws-alpha") if err != nil { t.Fatalf("ResolveWorkspace after mutation: %v", err) } if owner.ID != "node-alpha" || owner.Alias != "alpha" { t.Fatalf("owner identity changed: %+v", owner) } if workspace.Ref != "ws-alpha" || owner.Workspaces[0].Ref != "ws-alpha" { t.Fatalf("stored ref mutated: workspace=%q owner=%q", workspace.Ref, owner.Workspaces[0].Ref) } if workspace.Operations[0] != config.WorkspaceOpRead || owner.Workspaces[0].Operations[0] != config.WorkspaceOpRead { t.Fatalf("stored operations mutated: workspace=%v owner=%v", workspace.Operations, owner.Workspaces[0].Operations) } if workspace.Commands[0].Args[0] != "README.md" || owner.Workspaces[0].Commands[0].Args[0] != "README.md" { t.Fatalf("stored command args mutated: workspace=%v owner=%v", workspace.Commands[0].Args, owner.Workspaces[0].Commands[0].Args) } if workspace.EnvironmentAllowlist[0] != "HOME" || owner.Workspaces[0].EnvironmentAllowlist[0] != "HOME" { t.Fatalf("stored environment allowlist mutated: workspace=%v owner=%v", workspace.EnvironmentAllowlist, owner.Workspaces[0].EnvironmentAllowlist) } if _, _, err := store.ResolveWorkspace("missing"); err == nil { t.Fatal("expected missing workspace lookup to fail") } } func TestLoadFromConfig_Success(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {Alias: "beta", Token: "token-beta"}, }) if err != nil { t.Fatalf("load from config: %v", err) } rec, ok := store.FindByToken("token-beta") if !ok { t.Fatal("expected record for token-beta") } if rec.Alias != "beta" { t.Fatalf("expected alias %q, got %q", "beta", rec.Alias) } if rec.ID == "" { t.Fatal("expected non-empty ID") } if _, ok := store.FindByToken("missing"); ok { t.Fatal("expected missing token lookup to fail") } } func TestLoadFromConfig_DuplicateToken(t *testing.T) { _, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {Alias: "alpha", Token: "token"}, {Alias: "beta", Token: "token"}, }) if err == nil { t.Fatal("expected duplicate token error") } } func TestLoadFromConfig_DuplicateAlias(t *testing.T) { _, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {Alias: "alpha", Token: "token-alpha"}, {Alias: "alpha", Token: "token-beta"}, }) if err == nil { t.Fatal("expected duplicate alias error") } } func TestLoadFromConfig_EmptyToken(t *testing.T) { _, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {Alias: "alpha"}, }) if err == nil { t.Fatal("expected empty token error") } } func TestLoadFromConfig_ExplicitID(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {ID: "my-custom-id", Alias: "alpha", Token: "token-alpha"}, }) if err != nil { t.Fatalf("load from config: %v", err) } rec, ok := store.FindByToken("token-alpha") if !ok { t.Fatal("expected record for token-alpha") } if rec.ID != "my-custom-id" { t.Fatalf("expected id %q, got %q", "my-custom-id", rec.ID) } } func TestLoadFromConfig_AutoID(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {Alias: "alpha", Token: "token-alpha"}, }) if err != nil { t.Fatalf("load from config: %v", err) } rec, ok := store.FindByToken("token-alpha") if !ok { t.Fatal("expected record for token-alpha") } if rec.ID == "" { t.Fatal("expected auto-generated non-empty ID") } if rec.ID == "node-alpha" { t.Fatal("expected UUID, not alias-derived ID") } } func TestLoadFromConfig_DuplicateID(t *testing.T) { _, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {ID: "node-dup", Alias: "alpha", Token: "token-alpha"}, {ID: "node-dup", Alias: "beta", Token: "token-beta"}, }) if err == nil { t.Fatal("expected duplicate id error") } } func TestLoadFromConfig_LegacyOllamaInPayload(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ { ID: "node-ollama", Alias: "ollama-node", Token: "token-ollama", Adapters: config.AdaptersConf{ Ollama: config.OllamaConf{Enabled: true, BaseURL: "http://127.0.0.1:11434"}, }, }, }) if err != nil { t.Fatalf("load from config: %v", err) } rec, ok := store.FindByToken("token-ollama") if !ok { t.Fatal("expected record for token-ollama") } payload, err := edgenode.BuildConfigPayload(rec) if err != nil { t.Fatalf("build config payload: %v", err) } var found bool for _, a := range payload.GetAdapters() { if a.GetType() == "ollama" && a.GetOllama().GetBaseUrl() == "http://127.0.0.1:11434" { found = true } } if !found { t.Fatalf("expected ollama adapter in payload with correct base_url, got %v", payload.GetAdapters()) } } func TestLoadFromConfig_LegacyVllmInPayload(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ { ID: "node-vllm", Alias: "vllm-node", Token: "token-vllm", Adapters: config.AdaptersConf{ Vllm: config.VllmConf{Enabled: true, Endpoint: "http://127.0.0.1:8000"}, }, }, }) if err != nil { t.Fatalf("load from config: %v", err) } rec, ok := store.FindByToken("token-vllm") if !ok { t.Fatal("expected record for token-vllm") } payload, err := edgenode.BuildConfigPayload(rec) if err != nil { t.Fatalf("build config payload: %v", err) } var found bool for _, a := range payload.GetAdapters() { if a.GetType() == "vllm" && a.GetVllm().GetEndpoint() == "http://127.0.0.1:8000" { found = true } } if !found { t.Fatalf("expected vllm adapter in payload with correct endpoint, got %v", payload.GetAdapters()) } } func TestLoadFromConfig_AutoIDUnique(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ {Alias: "alpha", Token: "token-alpha"}, {Alias: "beta", Token: "token-beta"}, }) if err != nil { t.Fatalf("load from config: %v", err) } recA, ok := store.FindByToken("token-alpha") if !ok { t.Fatal("expected record for token-alpha") } recB, ok := store.FindByToken("token-beta") if !ok { t.Fatal("expected record for token-beta") } if recA.ID == recB.ID { t.Fatalf("expected unique IDs, both are %q", recA.ID) } } func TestLoadFromConfig_LegacyOpenAICompatInPayload(t *testing.T) { store, err := edgenode.LoadFromConfig([]config.NodeDefinition{ { ID: "node-oai", Alias: "oai-node", Token: "token-oai", Adapters: config.AdaptersConf{ OpenAICompat: config.OpenAICompatConf{ Enabled: true, Provider: "lemonade", Endpoint: "http://127.0.0.1:13305", Headers: map[string]string{"authorization": "Bearer token"}, }, }, }, }) if err != nil { t.Fatalf("load from config: %v", err) } rec, ok := store.FindByToken("token-oai") if !ok { t.Fatal("expected record for token-oai") } payload, err := edgenode.BuildConfigPayload(rec) if err != nil { t.Fatalf("build config payload: %v", err) } var found bool for _, a := range payload.GetAdapters() { if a.GetType() == "openai_compat" { oai := a.GetOpenaiCompat() if oai != nil && oai.GetProvider() == "lemonade" && oai.GetEndpoint() == "http://127.0.0.1:13305" && oai.GetHeaders()["authorization"] == "Bearer token" { found = true } } } if !found { t.Fatalf("expected openai_compat adapter in payload with correct config, got %v", payload.GetAdapters()) } }