246 lines
7.8 KiB
Go
246 lines
7.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
acp "github.com/coder/acp-go-sdk"
|
|
)
|
|
|
|
func TestSetSessionConfigOptionReturnsAuthoritativeState(t *testing.T) {
|
|
sessionID := acp.SessionId("session-config-test")
|
|
agent := &mockAgent{
|
|
sessionConfig: map[acp.SessionId][]acp.SessionConfigOption{
|
|
sessionID: mockSessionConfigOptions(),
|
|
},
|
|
}
|
|
|
|
response, err := agent.SetSessionConfigOption(context.Background(), acp.SetSessionConfigOptionRequest{
|
|
ValueId: &acp.SetSessionConfigOptionValueId{
|
|
SessionId: sessionID,
|
|
ConfigId: "effort",
|
|
Value: reasoningEffortLow,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("SetSessionConfigOption() error = %v", err)
|
|
}
|
|
|
|
values := make(map[string]string, len(response.ConfigOptions))
|
|
for _, option := range response.ConfigOptions {
|
|
if option.Select != nil {
|
|
values[string(option.Select.Id)] = string(option.Select.CurrentValue)
|
|
}
|
|
}
|
|
if values["model"] != modelFast || values["effort"] != reasoningEffortLow {
|
|
t.Fatalf("config values = %#v, want model=mock-fast and effort=low", values)
|
|
}
|
|
}
|
|
|
|
func TestSetSessionConfigOptionRejectsUnknownValue(t *testing.T) {
|
|
sessionID := acp.SessionId("session-config-test")
|
|
agent := &mockAgent{
|
|
sessionConfig: map[acp.SessionId][]acp.SessionConfigOption{
|
|
sessionID: mockSessionConfigOptions(),
|
|
},
|
|
}
|
|
|
|
_, err := agent.SetSessionConfigOption(context.Background(), acp.SetSessionConfigOptionRequest{
|
|
ValueId: &acp.SetSessionConfigOptionValueId{
|
|
SessionId: sessionID,
|
|
ConfigId: "effort",
|
|
Value: "unadvertised",
|
|
},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("SetSessionConfigOption() error = nil, want invalid value error")
|
|
}
|
|
|
|
values := make(map[string]string)
|
|
for _, option := range agent.sessionConfig[sessionID] {
|
|
if option.Select != nil {
|
|
values[string(option.Select.Id)] = string(option.Select.CurrentValue)
|
|
}
|
|
}
|
|
if values["effort"] != reasoningEffortMed {
|
|
t.Fatalf("effort = %q, want unchanged medium", values["effort"])
|
|
}
|
|
}
|
|
|
|
func TestSetSessionConfigOptionModelChangesAvailableOptions(t *testing.T) {
|
|
agent := &mockAgent{
|
|
sessions: make(map[acp.SessionId]bool),
|
|
sessionConfig: make(map[acp.SessionId][]acp.SessionConfigOption),
|
|
commandsEmitted: make(map[acp.SessionId]bool),
|
|
}
|
|
session, err := agent.NewSession(context.Background(), acp.NewSessionRequest{})
|
|
if err != nil {
|
|
t.Fatalf("NewSession: %v", err)
|
|
}
|
|
|
|
response, err := agent.SetSessionConfigOption(context.Background(), acp.SetSessionConfigOptionRequest{
|
|
ValueId: &acp.SetSessionConfigOptionValueId{
|
|
SessionId: session.SessionId,
|
|
ConfigId: "model",
|
|
Value: modelSmart,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("set model: %v", err)
|
|
}
|
|
|
|
values := make(map[string]string)
|
|
for _, option := range response.ConfigOptions {
|
|
if option.Select != nil {
|
|
values[string(option.Select.Id)] = string(option.Select.CurrentValue)
|
|
}
|
|
}
|
|
if values["model"] != modelSmart {
|
|
t.Fatalf("model = %q, want mock-smart", values["model"])
|
|
}
|
|
if values["effort"] != reasoningEffortHigh {
|
|
t.Fatalf("effort = %q, want high for mock-smart", values["effort"])
|
|
}
|
|
var hasMax, hasMedium bool
|
|
for _, option := range response.ConfigOptions {
|
|
if option.Select == nil || option.Select.Id != "effort" || option.Select.Options.Ungrouped == nil {
|
|
continue
|
|
}
|
|
for _, choice := range *option.Select.Options.Ungrouped {
|
|
hasMax = hasMax || choice.Value == "max"
|
|
hasMedium = hasMedium || choice.Value == reasoningEffortMed
|
|
}
|
|
}
|
|
if !hasMax || hasMedium {
|
|
t.Fatalf("response = %#v, want smart-only effort choices", response.ConfigOptions)
|
|
}
|
|
}
|
|
|
|
func TestSessionConfigIsIsolatedAcrossNewSessions(t *testing.T) {
|
|
agent := &mockAgent{
|
|
sessions: make(map[acp.SessionId]bool),
|
|
sessionConfig: make(map[acp.SessionId][]acp.SessionConfigOption),
|
|
commandsEmitted: make(map[acp.SessionId]bool),
|
|
}
|
|
first, err := agent.NewSession(context.Background(), acp.NewSessionRequest{})
|
|
if err != nil {
|
|
t.Fatalf("first NewSession: %v", err)
|
|
}
|
|
second, err := agent.NewSession(context.Background(), acp.NewSessionRequest{})
|
|
if err != nil {
|
|
t.Fatalf("second NewSession: %v", err)
|
|
}
|
|
if first.SessionId == second.SessionId {
|
|
t.Fatalf("session IDs must be unique: %q", first.SessionId)
|
|
}
|
|
|
|
_, err = agent.SetSessionConfigOption(context.Background(), acp.SetSessionConfigOptionRequest{
|
|
ValueId: &acp.SetSessionConfigOptionValueId{
|
|
SessionId: first.SessionId,
|
|
ConfigId: "effort",
|
|
Value: reasoningEffortLow,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("set first effort: %v", err)
|
|
}
|
|
response, err := agent.SetSessionConfigOption(context.Background(), acp.SetSessionConfigOptionRequest{
|
|
ValueId: &acp.SetSessionConfigOptionValueId{
|
|
SessionId: second.SessionId,
|
|
ConfigId: "model",
|
|
Value: "mock-fast",
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("read second state: %v", err)
|
|
}
|
|
for _, option := range response.ConfigOptions {
|
|
if option.Select != nil && option.Select.Id == "effort" && option.Select.CurrentValue != reasoningEffortMed {
|
|
t.Fatalf("second effort = %q, want medium", option.Select.CurrentValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSetSessionConfigOptionRejectsClosedSession(t *testing.T) {
|
|
agent := &mockAgent{
|
|
sessions: make(map[acp.SessionId]bool),
|
|
sessionConfig: make(map[acp.SessionId][]acp.SessionConfigOption),
|
|
commandsEmitted: make(map[acp.SessionId]bool),
|
|
}
|
|
session, err := agent.NewSession(context.Background(), acp.NewSessionRequest{})
|
|
if err != nil {
|
|
t.Fatalf("NewSession: %v", err)
|
|
}
|
|
if _, err = agent.CloseSession(context.Background(), acp.CloseSessionRequest{SessionId: session.SessionId}); err != nil {
|
|
t.Fatalf("CloseSession: %v", err)
|
|
}
|
|
|
|
_, err = agent.SetSessionConfigOption(context.Background(), acp.SetSessionConfigOptionRequest{
|
|
ValueId: &acp.SetSessionConfigOptionValueId{
|
|
SessionId: session.SessionId,
|
|
ConfigId: "effort",
|
|
Value: reasoningEffortLow,
|
|
},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("SetSessionConfigOption() error = nil, want unknown session error")
|
|
}
|
|
if _, ok := agent.sessionConfig[session.SessionId]; ok {
|
|
t.Fatal("SetSessionConfigOption() recreated closed session config")
|
|
}
|
|
}
|
|
|
|
// TestMockSessionConfigOptionsForModelAdvertisesSlowModel verifies the mock
|
|
// agent advertises "mock-slow" in the model selector. E2E fixtures use the
|
|
// slow tier for delay-timing assertions, and the no-silent-model-fallback
|
|
// strict policy fails session start when the profile model is absent from the
|
|
// advertised list — so dropping the advertisement would break both.
|
|
func TestMockSessionConfigOptionsForModelAdvertisesSlowModel(t *testing.T) {
|
|
for _, model := range []string{modelFast, modelSmart, modelSlow} {
|
|
options := mockSessionConfigOptionsForModel(model)
|
|
values := make(map[string]string, len(options))
|
|
for _, option := range options {
|
|
if option.Select != nil {
|
|
values[string(option.Select.Id)] = string(option.Select.CurrentValue)
|
|
}
|
|
}
|
|
if values["model"] != model {
|
|
t.Errorf("mockSessionConfigOptionsForModel(%q) current model = %q, want %q", model, values["model"], model)
|
|
}
|
|
}
|
|
|
|
modelOption := findModelOption(t)
|
|
advertised := make([]string, 0, len(*modelOption.Select.Options.Ungrouped))
|
|
for _, option := range *modelOption.Select.Options.Ungrouped {
|
|
advertised = append(advertised, string(option.Value))
|
|
}
|
|
if !containsString(advertised, modelSlow) {
|
|
t.Fatalf("advertised models = %v, missing mock-slow (E2E + strict policy depend on it)", advertised)
|
|
}
|
|
for _, want := range []string{modelFast, modelSmart, modelSlow} {
|
|
if !containsString(advertised, want) {
|
|
t.Errorf("advertised models = %v, missing %s", advertised, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func findModelOption(t *testing.T) acp.SessionConfigOption {
|
|
t.Helper()
|
|
for _, option := range mockSessionConfigOptionsForModel(modelFast) {
|
|
if option.Select != nil && string(option.Select.Id) == "model" {
|
|
return option
|
|
}
|
|
}
|
|
t.Fatal("no model config option advertised")
|
|
return acp.SessionConfigOption{}
|
|
}
|
|
|
|
func containsString(values []string, want string) bool {
|
|
for _, v := range values {
|
|
if v == want {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|