package config import ( "os" "path/filepath" "slices" "strings" "testing" ) func TestLoadRuntimeConfigurationIncludesCapabilitiesAndBridge(t *testing.T) { workspacePath := t.TempDir() runtimeConfigurationPath := filepath.Join(workspacePath, "runtime.json") runtimeConfigurationDocument := `{ "baseURL": "http://127.0.0.1:8080", "capabilities": { "endpoint": "http://127.0.0.1:7781", "unixSocketPath": "/run/internkim/capability.sock", "timeoutSecond": 15, "protocolVersion": "0.4.0", "aggregateProtocolHash": "58ff1977989bacbf2db3fdce08fd57c9b52f344ca747a3322f4e60bdf6052a78", "toolDescriptors": [ { "name": "browser_open", "inputSchema": {"type": "object"}, "inputIntentSchema": {"type": "object"}, "sideEffectClass": "browser" }, { "name": "user_confirm", "requiresApproval": true } ] }, "languageModel": { "contextWindowTokens": 1048576, "capability": { "model": "gemma-4-E4B-it", "executionMode": "auto" } }, "database": { "driver": "postgres", "connectionString": "postgres://blueclaw@/blueclaw?host=/var/run/postgresql&sslmode=disable", "migrationDirectoryPath": "/workspace/.blueclaw/runtime/current/migrations" }, "memory": { "embeddingModel": "google/embeddinggemma-2", "embeddingExecutionMode": "remote", "extractionDisabled": true }, "agent": { "intake": { "enabled": true, "executionMode": "auto" }, "generationOptions": { "seed": 41, "temperature": 0 }, "failureRecovery": { "failureDebtFinalizationGate": true, "attemptFingerprint": "tool_input_error_code", "recoveryBudget": { "correctedRetry": 1, "alternateRoute": 1, "adjacentTool": 2, "noToolFallback": 1 } } }, "agentProfiles": [ { "name": "default", "allowedToolNames": ["conversation_history", "memory_search", "echo"] } ], "connectors": { "chatd": { "endpoint": "http://127.0.0.1:8090", "enabledPlatforms": ["buzz"] } }, "terminal": { "mode": "native" }, "logging": { "directoryPath": "/workspace/.blueclaw/logs", "retentionDays": 7 }, "scheduler": { "retentionCheckIntervalMinute": 60, "taskSchedulePollIntervalSecond": 30 } }` errorValue := os.WriteFile(runtimeConfigurationPath, []byte(runtimeConfigurationDocument), 0o600) if errorValue != nil { t.Fatalf("expected runtime configuration to be written: %v", errorValue) } runtimeConfiguration, errorValue := LoadRuntimeConfiguration(runtimeConfigurationPath) if errorValue != nil { t.Fatalf("expected runtime configuration to load: %v", errorValue) } if runtimeConfiguration.Capabilities.Endpoint != "http://127.0.0.1:7781" { t.Fatalf("expected capability endpoint to match, got %q", runtimeConfiguration.Capabilities.Endpoint) } if runtimeConfiguration.Capabilities.ProtocolVersion != "0.4.0" { t.Fatalf("expected protocol version to match, got %q", runtimeConfiguration.Capabilities.ProtocolVersion) } if runtimeConfiguration.Capabilities.AggregateProtocolHash != "58ff1977989bacbf2db3fdce08fd57c9b52f344ca747a3322f4e60bdf6052a78" { t.Fatalf("expected aggregate protocol hash to match, got %q", runtimeConfiguration.Capabilities.AggregateProtocolHash) } if len(runtimeConfiguration.Capabilities.ToolDescriptors) != 2 { t.Fatalf("expected capability descriptors to load, got %+v", runtimeConfiguration.Capabilities.ToolDescriptors) } if string(runtimeConfiguration.Capabilities.ToolDescriptors[0].InputSchema) != `{"type": "object"}` && string(runtimeConfiguration.Capabilities.ToolDescriptors[0].InputSchema) != `{"type":"object"}` { t.Fatalf("expected descriptor input schema to load, got %s", runtimeConfiguration.Capabilities.ToolDescriptors[0].InputSchema) } if len(runtimeConfiguration.Capabilities.ToolDescriptors[0].InputIntentSchema) == 0 { t.Fatal("expected descriptor input intent schema to load") } if !runtimeConfiguration.Capabilities.ToolDescriptors[1].RequiresApproval { t.Fatalf("expected descriptor approval flag to load, got %+v", runtimeConfiguration.Capabilities.ToolDescriptors[1]) } if runtimeConfiguration.Capabilities.UnixSocketPath != "/run/internkim/capability.sock" { t.Fatalf("expected capability unix socket path to match, got %q", runtimeConfiguration.Capabilities.UnixSocketPath) } if runtimeConfiguration.Capabilities.TimeoutSecond != 15 { t.Fatalf("expected capability timeout to match, got %d", runtimeConfiguration.Capabilities.TimeoutSecond) } if runtimeConfiguration.Connectors.Chatd.Endpoint != "http://127.0.0.1:8090" { t.Fatalf("expected chatd endpoint to match, got %q", runtimeConfiguration.Connectors.Chatd.Endpoint) } if !slices.Equal(runtimeConfiguration.Connectors.Chatd.EnabledPlatforms, []string{"buzz"}) { t.Fatalf("expected chatd to name the enabled platforms, got %v", runtimeConfiguration.Connectors.Chatd.EnabledPlatforms) } if runtimeConfiguration.LanguageModel.Capability.Model != "gemma-4-E4B-it" { t.Fatalf("expected capability model to match, got %q", runtimeConfiguration.LanguageModel.Capability.Model) } if runtimeConfiguration.LanguageModel.Capability.ExecutionMode != "auto" { t.Fatalf("expected capability execution mode to match, got %q", runtimeConfiguration.LanguageModel.Capability.ExecutionMode) } if runtimeConfiguration.LanguageModel.ContextWindowTokens != 1048576 { t.Fatalf("expected the context window to match, got %d", runtimeConfiguration.LanguageModel.ContextWindowTokens) } if runtimeConfiguration.Database.Driver != "postgres" { t.Fatalf("expected database driver to match, got %q", runtimeConfiguration.Database.Driver) } if runtimeConfiguration.Database.MigrationDirectoryPath != "/workspace/.blueclaw/runtime/current/migrations" { t.Fatalf("expected migration directory to match, got %q", runtimeConfiguration.Database.MigrationDirectoryPath) } if runtimeConfiguration.Memory.EmbeddingModel != "google/embeddinggemma-2" || runtimeConfiguration.Memory.EmbeddingExecutionMode != "remote" || !runtimeConfiguration.Memory.ExtractionDisabled { t.Fatalf("expected the memory settings to match, got %+v", runtimeConfiguration.Memory) } if !runtimeConfiguration.Agent.Intake.Enabled { t.Fatal("expected agent intake to be enabled") } if runtimeConfiguration.Agent.Intake.ExecutionMode != "auto" { t.Fatalf("expected agent intake execution mode to match, got %q", runtimeConfiguration.Agent.Intake.ExecutionMode) } if runtimeConfiguration.Agent.GenerationOptions.Seed == nil || *runtimeConfiguration.Agent.GenerationOptions.Seed != 41 { t.Fatalf("expected agent generation seed to load, got %+v", runtimeConfiguration.Agent.GenerationOptions) } if runtimeConfiguration.Agent.GenerationOptions.Temperature == nil || *runtimeConfiguration.Agent.GenerationOptions.Temperature != 0 { t.Fatalf("expected agent generation temperature to load, got %+v", runtimeConfiguration.Agent.GenerationOptions) } if !runtimeConfiguration.Agent.FailureRecovery.FailureDebtFinalizationGate { t.Fatal("expected agent failure debt finalization gate to load") } if runtimeConfiguration.Agent.FailureRecovery.AttemptFingerprint != "tool_input_error_code" { t.Fatalf("expected attempt fingerprint mode to match, got %q", runtimeConfiguration.Agent.FailureRecovery.AttemptFingerprint) } if runtimeConfiguration.Agent.FailureRecovery.RecoveryBudget.CorrectedRetry != 1 || runtimeConfiguration.Agent.FailureRecovery.RecoveryBudget.AdjacentTool != 2 { t.Fatalf("expected recovery budget to load, got %+v", runtimeConfiguration.Agent.FailureRecovery.RecoveryBudget) } if len(runtimeConfiguration.AgentProfiles) != 1 || runtimeConfiguration.AgentProfiles[0].AllowedToolNames[2] != "echo" { t.Fatalf("expected agent profile tool allowlist to load, got %+v", runtimeConfiguration.AgentProfiles) } if runtimeConfiguration.Logging.RetentionDays != 7 { t.Fatalf("expected log retention to match, got %d", runtimeConfiguration.Logging.RetentionDays) } if runtimeConfiguration.Scheduler.SchedulePollIntervalSecond != 30 { t.Fatalf("expected task schedule poll interval to match, got %d", runtimeConfiguration.Scheduler.SchedulePollIntervalSecond) } } func TestLoadRuntimeConfigurationRejectsMissingOrInvalidCapabilityProtocolIdentity(t *testing.T) { testCases := []struct { name string identityDocument string errorFragment string }{ {name: "missing version", identityDocument: `"aggregateProtocolHash":"` + strings.Repeat("a", 64) + `"`, errorFragment: "protocolVersion must be"}, {name: "missing hash", identityDocument: `"protocolVersion":"0.4.0"`, errorFragment: "aggregateProtocolHash"}, {name: "invalid hash", identityDocument: `"protocolVersion":"0.4.0","aggregateProtocolHash":"not-a-hash"`, errorFragment: "aggregateProtocolHash"}, {name: "uppercase hash", identityDocument: `"protocolVersion":"0.4.0","aggregateProtocolHash":"` + strings.Repeat("A", 64) + `"`, errorFragment: "aggregateProtocolHash"}, {name: "padded version", identityDocument: `"protocolVersion":" 0.4.0","aggregateProtocolHash":"` + strings.Repeat("a", 64) + `"`, errorFragment: "protocolVersion must be"}, {name: "padded hash", identityDocument: `"protocolVersion":"0.4.0","aggregateProtocolHash":"` + strings.Repeat("a", 64) + ` "`, errorFragment: "aggregateProtocolHash"}, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { runtimeConfigurationPath := filepath.Join(t.TempDir(), "runtime.json") document := `{"capabilities":{"endpoint":"http://internkim-capability",` + testCase.identityDocument + `}}` if errorValue := os.WriteFile(runtimeConfigurationPath, []byte(document), 0o600); errorValue != nil { t.Fatal(errorValue) } _, errorValue := LoadRuntimeConfiguration(runtimeConfigurationPath) if errorValue == nil || !strings.Contains(errorValue.Error(), testCase.errorFragment) { t.Fatalf("expected %q error, got %v", testCase.errorFragment, errorValue) } }) } } func TestLoadRuntimeConfigurationTakesACapabilityEndpointThatStampsNothing(t *testing.T) { runtimeConfigurationPath := filepath.Join(t.TempDir(), "runtime.json") document := `{"capabilities":{"endpoint":"http://internkim-capability","unixSocketPath":"/run/internkim/capability.sock","timeoutSecond":30}}` if errorValue := os.WriteFile(runtimeConfigurationPath, []byte(document), 0o600); errorValue != nil { t.Fatal(errorValue) } runtimeConfiguration, errorValue := LoadRuntimeConfiguration(runtimeConfigurationPath) if errorValue != nil { t.Fatalf("expected an unstamped capability endpoint to load, got %v", errorValue) } if len(runtimeConfiguration.Capabilities.ToolDescriptors) != 0 { t.Fatalf("expected no stamped descriptors, got %+v", runtimeConfiguration.Capabilities.ToolDescriptors) } }