Files
tutti/services/tuttid/api/daemon_model_bindings_test.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

125 lines
4.5 KiB
Go

package api
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
modelbindingbiz "github.com/tutti-os/tutti/services/tuttid/biz/modelbinding"
modelbindingservice "github.com/tutti-os/tutti/services/tuttid/service/modelbinding"
)
// policyRejectingBindingService reports a binding write against a missing
// policy exactly as the production service does.
type policyRejectingBindingService struct{}
func (policyRejectingBindingService) ListBindings(context.Context, string) ([]modelbindingbiz.Binding, error) {
return nil, nil
}
func (policyRejectingBindingService) SetBinding(context.Context, modelbindingservice.SetBindingInput) (modelbindingbiz.Binding, error) {
return modelbindingbiz.Binding{}, fmt.Errorf("%w: policy not found", modelbindingservice.ErrPolicyNotUsable)
}
// referenceUnusableBindingService reports the post-validation foreign-key
// backstop firing (a reference disappeared mid-write), the way the production
// service maps a storage foreign-key rejection.
type referenceUnusableBindingService struct{}
func (referenceUnusableBindingService) ListBindings(context.Context, string) ([]modelbindingbiz.Binding, error) {
return nil, nil
}
func (referenceUnusableBindingService) SetBinding(context.Context, modelbindingservice.SetBindingInput) (modelbindingbiz.Binding, error) {
return modelbindingbiz.Binding{}, modelbindingservice.ErrBindingReferenceUnusable
}
func TestSetAgentModelBindingReferenceRaceMapsToNeutralInvalidRequest(t *testing.T) {
t.Parallel()
ctx := context.Background()
api := DaemonAPI{
AgentModelBindingService: referenceUnusableBindingService{},
}
response, err := api.SetAgentModelBinding(ctx, tuttigenerated.SetAgentModelBindingRequestObject{
WorkspaceID: "ws-1",
AgentTargetID: "local:codex",
Body: &tuttigenerated.SetAgentModelBindingRequest{ModelPolicyId: stringPointer("pol-x")},
})
if err != nil {
t.Fatalf("SetAgentModelBinding() error = %v", err)
}
rejected, ok := response.(tuttigenerated.SetAgentModelBinding400JSONResponse)
if !ok {
t.Fatalf("response = %T, want 400", response)
}
recorder := httptest.NewRecorder()
if err := rejected.VisitSetAgentModelBindingResponse(recorder); err != nil {
t.Fatalf("visit error = %v", err)
}
if recorder.Code != http.StatusBadRequest {
t.Fatalf("http status = %d, want 400", recorder.Code)
}
var body tuttigenerated.ApiErrorResponse
decodeGeneratedRouteResponse(t, recorder, &body)
if body.Error.Code != tuttigenerated.InvalidRequest {
t.Fatalf("error.code = %q, want invalid_request", body.Error.Code)
}
if body.Error.Reason == nil || *body.Error.Reason != "invalid_agent_model_binding" {
t.Fatalf("error.reason = %v, want invalid_agent_model_binding", body.Error.Reason)
}
// The developer message must stay neutral: it must not blame the plan when
// SQLite cannot say which reference failed.
if body.Error.DeveloperMessage == nil {
t.Fatalf("developer message = nil, want the neutral reference message")
}
if strings.Contains(*body.Error.DeveloperMessage, "plan") {
t.Fatalf("developer message = %q, must not claim the plan is unusable", *body.Error.DeveloperMessage)
}
}
func TestSetAgentModelBindingRejectsUnknownPolicyAsInvalidRequest(t *testing.T) {
t.Parallel()
ctx := context.Background()
api := DaemonAPI{
AgentModelBindingService: policyRejectingBindingService{},
}
response, err := api.SetAgentModelBinding(ctx, tuttigenerated.SetAgentModelBindingRequestObject{
WorkspaceID: "ws-1",
AgentTargetID: "local:codex",
Body: &tuttigenerated.SetAgentModelBindingRequest{ModelPolicyId: stringPointer("pol-missing")},
})
if err != nil {
t.Fatalf("SetAgentModelBinding() error = %v", err)
}
rejected, ok := response.(tuttigenerated.SetAgentModelBinding400JSONResponse)
if !ok {
t.Fatalf("response = %T, want 400", response)
}
// Assert the real production wire shape, not just the Go type.
recorder := httptest.NewRecorder()
if err := rejected.VisitSetAgentModelBindingResponse(recorder); err != nil {
t.Fatalf("visit error = %v", err)
}
if recorder.Code != http.StatusBadRequest {
t.Fatalf("http status = %d, want 400", recorder.Code)
}
var body tuttigenerated.ApiErrorResponse
decodeGeneratedRouteResponse(t, recorder, &body)
if body.Error.Code != tuttigenerated.InvalidRequest {
t.Fatalf("error.code = %q, want invalid_request", body.Error.Code)
}
if body.Error.Reason == nil || *body.Error.Reason != "invalid_agent_model_binding" {
t.Fatalf("error.reason = %v, want invalid_agent_model_binding", body.Error.Reason)
}
}