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
208 lines
9.6 KiB
Go
208 lines
9.6 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
|
|
automationrulebiz "github.com/tutti-os/tutti/services/tuttid/biz/automationrule"
|
|
collabrunbiz "github.com/tutti-os/tutti/services/tuttid/biz/collabrun"
|
|
preferencesbiz "github.com/tutti-os/tutti/services/tuttid/biz/preferences"
|
|
automationruleservice "github.com/tutti-os/tutti/services/tuttid/service/automationrule"
|
|
collabrunservice "github.com/tutti-os/tutti/services/tuttid/service/collabrun"
|
|
)
|
|
|
|
func gateTestPreferences(flags map[string]bool, err error) stubPreferencesService {
|
|
return stubPreferencesService{
|
|
getFn: func(context.Context) (preferencesbiz.DesktopPreferences, error) {
|
|
if err != nil {
|
|
return preferencesbiz.DesktopPreferences{}, err
|
|
}
|
|
return preferencesbiz.DesktopPreferences{FeatureFlags: flags}, nil
|
|
},
|
|
}
|
|
}
|
|
|
|
type gateStubAutomationRuleService struct{}
|
|
|
|
func (gateStubAutomationRuleService) ListRules(context.Context, string) ([]automationrulebiz.Rule, error) {
|
|
return []automationrulebiz.Rule{}, nil
|
|
}
|
|
func (gateStubAutomationRuleService) GetRule(context.Context, string, string) (automationrulebiz.Rule, error) {
|
|
return automationrulebiz.Rule{}, nil
|
|
}
|
|
func (gateStubAutomationRuleService) CreateRule(context.Context, automationruleservice.PutRuleInput) (automationrulebiz.Rule, error) {
|
|
return automationrulebiz.Rule{}, nil
|
|
}
|
|
func (gateStubAutomationRuleService) UpdateRule(context.Context, automationruleservice.PutRuleInput) (automationrulebiz.Rule, error) {
|
|
return automationrulebiz.Rule{}, nil
|
|
}
|
|
func (gateStubAutomationRuleService) DeleteRule(context.Context, string, string) error {
|
|
return nil
|
|
}
|
|
func (gateStubAutomationRuleService) GetSessionOverride(context.Context, string, string) (automationrulebiz.SessionOverride, bool, error) {
|
|
return automationrulebiz.SessionOverride{}, false, nil
|
|
}
|
|
func (gateStubAutomationRuleService) SetSessionOverride(_ context.Context, input automationrulebiz.SessionOverride) (automationrulebiz.SessionOverride, error) {
|
|
return input, nil
|
|
}
|
|
|
|
type gateStubCollaborationRunService struct{}
|
|
|
|
func (gateStubCollaborationRunService) StartConsult(context.Context, collabrunservice.StartConsultInput) (collabrunbiz.Run, error) {
|
|
return collabrunbiz.Run{}, nil
|
|
}
|
|
func (gateStubCollaborationRunService) RecordRun(context.Context, collabrunservice.RecordRunInput) (collabrunbiz.Run, error) {
|
|
return collabrunbiz.Run{}, nil
|
|
}
|
|
func (gateStubCollaborationRunService) SetAdoption(context.Context, string, string, string) (collabrunbiz.Run, error) {
|
|
return collabrunbiz.Run{}, nil
|
|
}
|
|
func (gateStubCollaborationRunService) CancelConsult(context.Context, string, string) (collabrunbiz.Run, error) {
|
|
return collabrunbiz.Run{}, nil
|
|
}
|
|
func (gateStubCollaborationRunService) ListRuns(context.Context, string, string, int) ([]collabrunbiz.Run, error) {
|
|
return []collabrunbiz.Run{}, nil
|
|
}
|
|
|
|
func TestAutomationRulesWriteGateRejectsWritesWhenFlagOff(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
workspaceID := tuttigenerated.WorkspaceID("ws-1")
|
|
|
|
cases := []struct {
|
|
name string
|
|
flags map[string]bool
|
|
err error
|
|
}{
|
|
{name: "flag explicitly false", flags: map[string]bool{AutomationRulesFeatureFlag: false}},
|
|
{name: "flag absent", flags: map[string]bool{}},
|
|
{name: "preferences unreadable", err: errors.New("preferences store down")},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
api := DaemonAPI{
|
|
AutomationRuleService: gateStubAutomationRuleService{},
|
|
CollaborationRunService: gateStubCollaborationRunService{},
|
|
PreferencesService: gateTestPreferences(tc.flags, tc.err),
|
|
}
|
|
createResponse, err := api.CreateAutomationRule(ctx, tuttigenerated.CreateAutomationRuleRequestObject{WorkspaceID: workspaceID})
|
|
if err != nil {
|
|
t.Fatalf("CreateAutomationRule() error = %v", err)
|
|
}
|
|
rejected, ok := createResponse.(tuttigenerated.CreateAutomationRule400JSONResponse)
|
|
if !ok {
|
|
t.Fatalf("CreateAutomationRule() response = %T, want 400 rejection", createResponse)
|
|
}
|
|
if reason := tuttigenerated.ApiErrorResponse(rejected.InvalidRequestErrorJSONResponse).Error.Reason; reason == nil || *reason != "automation_rules_disabled" {
|
|
t.Fatalf("CreateAutomationRule() rejection reason = %v, want automation_rules_disabled", reason)
|
|
}
|
|
|
|
updateResponse, err := api.UpdateAutomationRule(ctx, tuttigenerated.UpdateAutomationRuleRequestObject{WorkspaceID: workspaceID, AutomationRuleID: "automation-rule:one"})
|
|
if err != nil {
|
|
t.Fatalf("UpdateAutomationRule() error = %v", err)
|
|
}
|
|
if _, ok := updateResponse.(tuttigenerated.UpdateAutomationRule400JSONResponse); !ok {
|
|
t.Fatalf("UpdateAutomationRule() response = %T, want 400 rejection", updateResponse)
|
|
}
|
|
|
|
deleteResponse, err := api.DeleteAutomationRule(ctx, tuttigenerated.DeleteAutomationRuleRequestObject{WorkspaceID: workspaceID, AutomationRuleID: "automation-rule:one"})
|
|
if err != nil {
|
|
t.Fatalf("DeleteAutomationRule() error = %v", err)
|
|
}
|
|
if _, ok := deleteResponse.(tuttigenerated.DeleteAutomationRule400JSONResponse); !ok {
|
|
t.Fatalf("DeleteAutomationRule() response = %T, want 400 rejection", deleteResponse)
|
|
}
|
|
|
|
overrideResponse, err := api.SetAgentSessionAutomationRuleOverride(ctx, tuttigenerated.SetAgentSessionAutomationRuleOverrideRequestObject{WorkspaceID: workspaceID, AgentSessionID: "session-1"})
|
|
if err != nil {
|
|
t.Fatalf("SetAgentSessionAutomationRuleOverride() error = %v", err)
|
|
}
|
|
if _, ok := overrideResponse.(tuttigenerated.SetAgentSessionAutomationRuleOverride400JSONResponse); !ok {
|
|
t.Fatalf("SetAgentSessionAutomationRuleOverride() response = %T, want 400 rejection", overrideResponse)
|
|
}
|
|
|
|
collabCreateResponse, err := api.CreateCollaborationRun(ctx, tuttigenerated.CreateCollaborationRunRequestObject{WorkspaceID: workspaceID})
|
|
if err != nil {
|
|
t.Fatalf("CreateCollaborationRun() error = %v", err)
|
|
}
|
|
if _, ok := collabCreateResponse.(tuttigenerated.CreateCollaborationRun400JSONResponse); !ok {
|
|
t.Fatalf("CreateCollaborationRun() response = %T, want 400 rejection", collabCreateResponse)
|
|
}
|
|
|
|
adoptionResponse, err := api.SetCollaborationRunAdoption(ctx, tuttigenerated.SetCollaborationRunAdoptionRequestObject{WorkspaceID: workspaceID, CollaborationRunID: "run-1"})
|
|
if err != nil {
|
|
t.Fatalf("SetCollaborationRunAdoption() error = %v", err)
|
|
}
|
|
if _, ok := adoptionResponse.(tuttigenerated.SetCollaborationRunAdoption400JSONResponse); !ok {
|
|
t.Fatalf("SetCollaborationRunAdoption() response = %T, want 400 rejection", adoptionResponse)
|
|
}
|
|
|
|
cancelResponse, err := api.CancelCollaborationRun(ctx, tuttigenerated.CancelCollaborationRunRequestObject{WorkspaceID: workspaceID, CollaborationRunID: "run-1"})
|
|
if err != nil {
|
|
t.Fatalf("CancelCollaborationRun() error = %v", err)
|
|
}
|
|
if _, ok := cancelResponse.(tuttigenerated.CancelCollaborationRun400JSONResponse); !ok {
|
|
t.Fatalf("CancelCollaborationRun() response = %T, want 400 rejection", cancelResponse)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAutomationRulesWriteGateKeepsReadsWorkingWhenFlagOff(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
api := DaemonAPI{
|
|
AutomationRuleService: gateStubAutomationRuleService{},
|
|
CollaborationRunService: gateStubCollaborationRunService{},
|
|
PreferencesService: gateTestPreferences(map[string]bool{}, nil),
|
|
}
|
|
rulesResponse, err := api.ListAutomationRules(context.Background(), tuttigenerated.ListAutomationRulesRequestObject{WorkspaceID: "ws-1"})
|
|
if err != nil {
|
|
t.Fatalf("ListAutomationRules() error = %v", err)
|
|
}
|
|
if _, ok := rulesResponse.(tuttigenerated.ListAutomationRules200JSONResponse); !ok {
|
|
t.Fatalf("ListAutomationRules() response = %T, want 200 with writes gated", rulesResponse)
|
|
}
|
|
runsResponse, err := api.ListCollaborationRuns(context.Background(), tuttigenerated.ListCollaborationRunsRequestObject{WorkspaceID: "ws-1"})
|
|
if err != nil {
|
|
t.Fatalf("ListCollaborationRuns() error = %v", err)
|
|
}
|
|
if _, ok := runsResponse.(tuttigenerated.ListCollaborationRuns200JSONResponse); !ok {
|
|
t.Fatalf("ListCollaborationRuns() response = %T, want 200 with writes gated", runsResponse)
|
|
}
|
|
overrideResponse, err := api.GetAgentSessionAutomationRuleOverride(context.Background(), tuttigenerated.GetAgentSessionAutomationRuleOverrideRequestObject{WorkspaceID: "ws-1", AgentSessionID: "session-1"})
|
|
if err != nil {
|
|
t.Fatalf("GetAgentSessionAutomationRuleOverride() error = %v", err)
|
|
}
|
|
if _, ok := overrideResponse.(tuttigenerated.GetAgentSessionAutomationRuleOverride200JSONResponse); !ok {
|
|
t.Fatalf("GetAgentSessionAutomationRuleOverride() response = %T, want 200 with writes gated", overrideResponse)
|
|
}
|
|
}
|
|
|
|
func TestAutomationRulesWriteGateAllowsWritesWhenFlagOn(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
// Nil services: reaching the 503 service-unavailable branch proves the
|
|
// gate passed the request through to the handler.
|
|
api := DaemonAPI{
|
|
PreferencesService: gateTestPreferences(map[string]bool{AutomationRulesFeatureFlag: true}, nil),
|
|
}
|
|
createResponse, err := api.CreateAutomationRule(ctx, tuttigenerated.CreateAutomationRuleRequestObject{WorkspaceID: "ws-1"})
|
|
if err != nil {
|
|
t.Fatalf("CreateAutomationRule() error = %v", err)
|
|
}
|
|
if _, ok := createResponse.(tuttigenerated.CreateAutomationRule503JSONResponse); !ok {
|
|
t.Fatalf("CreateAutomationRule() response = %T, want 503 passthrough with flag on", createResponse)
|
|
}
|
|
collabCreateResponse, err := api.CreateCollaborationRun(ctx, tuttigenerated.CreateCollaborationRunRequestObject{WorkspaceID: "ws-1"})
|
|
if err != nil {
|
|
t.Fatalf("CreateCollaborationRun() error = %v", err)
|
|
}
|
|
if _, ok := collabCreateResponse.(tuttigenerated.CreateCollaborationRun503JSONResponse); !ok {
|
|
t.Fatalf("CreateCollaborationRun() response = %T, want 503 passthrough with flag on", collabCreateResponse)
|
|
}
|
|
}
|