package openai import ( "testing" "iop/packages/go/config" ) func TestHotPathSelectorDecisionMatrix(t *testing.T) { issued := newReservedPaths("req_test_123") preset := hotPathSelectorPreset([]string{config.ModeDirect, config.ModeLight}) directOnly := hotPathSelectorPreset([]string{config.ModeDirect}) validGate := hotPathTestGate(preset) tests := []struct { name string preset config.ExecutionPreset output normalizedStageOutput gate hotPathSelectorGate wantMode string wantReason string wantErr bool }{ {name: "ContentTextOnly", preset: preset, output: normalizedStageOutput{Content: "Hello"}, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls}, {name: "HighThinkingText", preset: preset, output: normalizedStageOutput{Content: "Result", Reasoning: "Reasoning"}, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls}, { name: "GeneralTools", preset: preset, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{ {ID: "call_read", Name: "read_file", Arguments: map[string]any{"path": "src/main.go"}}, }}, }, { name: "ExactPrepare", preset: preset, gate: validGate, wantMode: modeLight, wantReason: reasonLightExactPrepare, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{ {ID: "call_prepare", Name: "mkdir_p", Arguments: map[string]any{"path": issued.JobDir}}, }}, }, { name: "ExactPairWithMaskedPath", preset: preset, gate: validGate, wantMode: modeLight, wantReason: reasonLightExactPair, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{ {ID: "call_plan", Name: "write_file", RawArgs: `{"path":".iop\/job\/req_test_123\/plan.md"}`}, {ID: "call_review", Name: "write_file", Arguments: map[string]any{"path": issued.ReviewPath}}, }}, }, { name: "PartialPair", preset: preset, gate: validGate, wantReason: reasonMalformedPartialPair, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_plan", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}}}}, }, { name: "MixedCalls", preset: preset, gate: validGate, wantReason: reasonMalformedMixedCalls, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{ {ID: "call_prepare", Name: "mkdir_p", Arguments: map[string]any{"path": issued.JobDir}}, {ID: "call_general", Name: "read_file", Arguments: map[string]any{"path": "README.md"}}, }}, }, { name: "DuplicateCalls", preset: preset, gate: validGate, wantReason: reasonMalformedDuplicateCalls, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{ {ID: "call_plan_1", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}}, {ID: "call_plan_2", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}}, {ID: "call_review", Name: "write_file", Arguments: map[string]any{"path": issued.ReviewPath}}, }}, }, { name: "WrongIssuedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_wrong", Name: "write_file", Arguments: map[string]any{"path": ".iop/job/another/plan.md"}}}}, }, { name: "PrefixedMappedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_prefix", Name: "write_file", Arguments: map[string]any{"path": "prefix/" + issued.PlanPath}}}}, }, { name: "AbsoluteMappedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_absolute", Name: "write_file", Arguments: map[string]any{"path": "/" + issued.PlanPath}}}}, }, { name: "SuffixedMappedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_suffix", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath + ".bak"}}}}, }, { name: "SamePathExtraSource", preset: preset, gate: validGate, wantReason: reasonMalformedConflictingPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_extra", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath, "shadow": issued.PlanPath}}}}, }, { name: "DecodedAndRawConflictingReservedPaths", preset: preset, gate: validGate, wantReason: reasonMalformedConflictingPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_raw_conflict", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}, RawArgs: `{"path":".iop/job/req_test_123/review.md"}`}}}, }, { name: "ArbitraryControlRole", preset: preset, gate: validGate, wantReason: reasonMalformedControlRole, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_wrong_role", Name: "shell", Arguments: map[string]any{"path": issued.PlanPath}}}}, }, { name: "CanonicalRoleWrongReservedShape", preset: preset, gate: validGate, wantReason: reasonMalformedControlRole, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_wrong_shape", Name: "mkdir_p", Arguments: map[string]any{"path": issued.PlanPath}}}}, }, { name: "ConflictingReservedPathSources", preset: preset, gate: validGate, wantReason: reasonMalformedConflictingPath, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_conflict", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath, "shadow": issued.ReviewPath}}}}, }, { name: "LightDisabled", preset: directOnly, gate: hotPathTestGate(directOnly), wantReason: reasonModeDisabled, wantErr: true, output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_prepare", Name: "mkdir_p", Arguments: map[string]any{"path": issued.JobDir}}}}, }, {name: "UnhealthyPinnedGate", preset: preset, output: normalizedStageOutput{Content: "text"}, gate: withGateHealth(validGate, false), wantReason: reasonUnhealthyRoute, wantErr: true}, {name: "MissingCapabilityEvidence", preset: preset, output: normalizedStageOutput{Content: "text"}, gate: withoutGateCapability(validGate), wantReason: reasonUnhealthyRoute, wantErr: true}, {name: "MismatchedPresetBinding", preset: preset, output: normalizedStageOutput{Content: "text"}, gate: withGateSelector(validGate, "other-model"), wantReason: reasonUnhealthyRoute, wantErr: true}, { name: "ProseNeverSelectsMode", preset: preset, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls, output: normalizedStageOutput{Content: "I would choose light and mention .iop/job/req_test_123/plan.md in prose."}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { decision, err := classifyHotPathOutput(test.preset, issued, test.output, test.gate) if (err != nil) != test.wantErr { t.Fatalf("classifyHotPathOutput() error = %v, wantErr %v", err, test.wantErr) } if decision.Reason != test.wantReason { t.Errorf("decision.Reason = %q, want %q", decision.Reason, test.wantReason) } if !test.wantErr && decision.Mode != test.wantMode { t.Errorf("decision.Mode = %q, want %q", decision.Mode, test.wantMode) } }) } } func hotPathSelectorPreset(modes []string) config.ExecutionPreset { routes := make(map[string]config.ExecutionRoute, len(modes)) for _, mode := range modes { switch mode { case config.ModeDirect: routes[mode] = config.ExecutionRoute{} case config.ModeLight: routes[mode] = config.ExecutionRoute{Stages: []config.ExecutionRouteStage{ {Role: "local", Model: "local-model"}, {Role: "review", Model: "review-model"}, }} } } return config.ExecutionPreset{ ID: "preset-standard", Selector: config.ExecutionModelBinding{Model: "selector-model"}, AllowedModes: modes, Routes: routes, WorkspaceTools: []config.ExecutionWorkspaceToolAlternative{{ Name: "canonical-fs", Operations: map[string]config.ExecutionWorkspaceOperation{ "prepare": {ToolName: "mkdir_p", ArgumentMap: map[string]any{"path": "path"}}, "write": {ToolName: "write_file", ArgumentMap: map[string]any{"path": "path"}}, }, }}, } } func hotPathTestGate(preset config.ExecutionPreset) hotPathSelectorGate { return hotPathSelectorGate{ PresetID: preset.ID, SelectorModel: preset.Selector.Model, ModelGroupKey: preset.Selector.Model, ProviderID: "provider-1", RunID: "run-1", NodeID: "node-1", ExecutionPath: "provider_tunnel", ProfileDriver: "openai_chat", ProfileCapabilities: []string{"chat"}, Healthy: true, CapabilitySatisfied: true, } } func withGateHealth(gate hotPathSelectorGate, healthy bool) hotPathSelectorGate { gate.Healthy = healthy return gate } func withoutGateCapability(gate hotPathSelectorGate) hotPathSelectorGate { gate.CapabilitySatisfied = false return gate } func withGateSelector(gate hotPathSelectorGate, model string) hotPathSelectorGate { gate.SelectorModel = model return gate }