Pulse/pkg/aicontracts/contracts_test.go
rcourtman ee8a24e14a backend and governance: MCP contract, agent capabilities, API, and release-control
Manifest-backed MCP tools, prompts, and resources with surface affordance contracts; agent capability manifest and governance projection; API contract tests and capability route projection; operations-loop and intelligence-funnel telemetry; release-control subsystem documentation, registry, and tooling; licensing and configuration.
2026-06-23 17:26:15 +01:00

153 lines
5.8 KiB
Go

package aicontracts
import (
"encoding/json"
"strings"
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/agentcapabilities"
)
func TestEmptyOrchestratorMessage_UsesCanonicalEmptyCollections(t *testing.T) {
payload, err := json.Marshal(EmptyOrchestratorMessage())
if err != nil {
t.Fatalf("marshal empty orchestrator message: %v", err)
}
if !strings.Contains(string(payload), `"tool_calls":[]`) {
t.Fatalf("expected empty orchestrator message to retain tool_calls array, got %s", payload)
}
}
func TestEmptyOrchestratorToolCallInfo_UsesCanonicalEmptyCollections(t *testing.T) {
payload, err := json.Marshal(EmptyOrchestratorToolCallInfo())
if err != nil {
t.Fatalf("marshal empty orchestrator tool call info: %v", err)
}
if !strings.Contains(string(payload), `"input":{}`) {
t.Fatalf("expected empty orchestrator tool call info to retain input object, got %s", payload)
}
payload, err = json.Marshal(OrchestratorMessage{
ToolCalls: []OrchestratorToolCallInfo{{
ID: "call-1",
Name: "diagnose",
}},
}.NormalizeCollections())
if err != nil {
t.Fatalf("marshal normalized orchestrator message with tool call: %v", err)
}
if !strings.Contains(string(payload), `"input":{}`) {
t.Fatalf("expected normalized orchestrator tool call to retain input object, got %s", payload)
}
}
func TestOrchestratorToolCallInfoUsesSharedProviderProjection(t *testing.T) {
input := map[string]interface{}{
"resource_id": "vm/100",
"body": map[string]interface{}{
"tags": []interface{}{"prod"},
},
}
signature := json.RawMessage(`{"provider":"gemini"}`)
info := OrchestratorToolCallInfoFromProvider(agentcapabilities.ProviderToolCall{
ID: "call-1",
Name: "diagnose",
Input: input,
ThoughtSignature: signature,
})
if info.ID != "call-1" || info.Name != "diagnose" || info.Input == nil {
t.Fatalf("orchestrator tool call info = %+v", info)
}
info.Input["resource_id"] = "vm/101"
info.Input["body"].(map[string]interface{})["tags"].([]interface{})[0] = "changed"
info.ThoughtSignature[0] = '['
if input["resource_id"] != "vm/100" || input["body"].(map[string]interface{})["tags"].([]interface{})[0] != "prod" {
t.Fatalf("orchestrator projection must not alias provider input: source=%#v info=%#v", input, info.Input)
}
if string(signature) != `{"provider":"gemini"}` {
t.Fatalf("orchestrator projection must not alias provider thought signature: source=%s info=%s", signature, info.ThoughtSignature)
}
orchestratorInput := map[string]interface{}{"resource_id": "vm/300"}
infoForProvider := OrchestratorToolCallInfo{
ID: "call-2",
Name: "inspect",
Input: orchestratorInput,
ThoughtSignature: json.RawMessage(`{"provider":"gemini"}`),
}.NormalizeCollections()
provider := infoForProvider.ProviderToolCall()
provider.Input["resource_id"] = "vm/102"
provider.ThoughtSignature[0] = '['
if infoForProvider.Input["resource_id"] != "vm/300" || string(infoForProvider.ThoughtSignature) != `{"provider":"gemini"}` {
t.Fatalf("provider projection must not alias orchestrator tool call info: info=%#v provider=%#v", infoForProvider, provider)
}
}
func TestOrchestratorToolResultInfoUsesSharedProviderProjection(t *testing.T) {
info := OrchestratorToolResultInfoFromProvider(agentcapabilities.NewProviderToolResult("call-1", "done", true))
if info.ToolUseID != "call-1" || info.Content != "done" || !info.IsError {
t.Fatalf("orchestrator tool result info = %+v", info)
}
var shared agentcapabilities.ProviderToolResult = info.ProviderToolResult()
if shared.ToolUseID != "call-1" || shared.Content != "done" || !shared.IsError {
t.Fatalf("shared provider tool result = %+v", shared)
}
}
func TestEmptyInvestigationSession_UsesCanonicalEmptyCollections(t *testing.T) {
payload, err := json.Marshal(EmptyInvestigationSession())
if err != nil {
t.Fatalf("marshal empty investigation session: %v", err)
}
if !strings.Contains(string(payload), `"tools_available":[]`) {
t.Fatalf("expected empty investigation session to retain tools_available array, got %s", payload)
}
if !strings.Contains(string(payload), `"tools_used":[]`) {
t.Fatalf("expected empty investigation session to retain tools_used array, got %s", payload)
}
if !strings.Contains(string(payload), `"evidence_ids":[]`) {
t.Fatalf("expected empty investigation session to retain evidence_ids array, got %s", payload)
}
}
func TestEmptyInvestigationRecord_UsesCanonicalEmptyCollections(t *testing.T) {
payload, err := json.Marshal(EmptyInvestigationRecord())
if err != nil {
t.Fatalf("marshal empty investigation record: %v", err)
}
if !strings.Contains(string(payload), `"evidence":[]`) {
t.Fatalf("expected empty investigation record to retain evidence array, got %s", payload)
}
if !strings.Contains(string(payload), `"verification":[]`) {
t.Fatalf("expected empty investigation record to retain verification array, got %s", payload)
}
if !strings.Contains(string(payload), `"tools_used":[]`) {
t.Fatalf("expected empty investigation record to retain tools_used array, got %s", payload)
}
}
func TestInvestigationOutcomeFixRejected_IsCanonicalWireValue(t *testing.T) {
payload, err := json.Marshal(struct {
Outcome InvestigationOutcome `json:"outcome"`
}{
Outcome: OutcomeFixRejected,
})
if err != nil {
t.Fatalf("marshal rejected investigation outcome: %v", err)
}
if !strings.Contains(string(payload), `"outcome":"fix_rejected"`) {
t.Fatalf("expected rejected investigation outcome wire value, got %s", payload)
}
}
func TestEmptyFix_UsesCanonicalEmptyCollections(t *testing.T) {
payload, err := json.Marshal(EmptyFix())
if err != nil {
t.Fatalf("marshal empty fix: %v", err)
}
if !strings.Contains(string(payload), `"commands":[]`) {
t.Fatalf("expected empty fix to retain commands array, got %s", payload)
}
}