Add comparison profile preflight

This commit is contained in:
2026-08-02 05:25:03 +00:00
parent 6173e50d25
commit de1ae896b3
2 changed files with 184 additions and 14 deletions

View File

@@ -3,6 +3,7 @@ package app
import (
"context"
"errors"
"reflect"
"strings"
"testing"
"time"
@@ -131,6 +132,95 @@ func TestInspectPromptExecutionsReusesEffectiveProfile(t *testing.T) {
}
}
func TestInspectComparisonExecutionPreservesOrderedExplicitProfiles(t *testing.T) {
resolved := inspectionResolved(t)
executor := &inspectionExecutor{
prompt: validPromptInspection(resolved.Definition),
profiles: map[string]promptexec.ProfileInspection{
"weather-light": {ProfileID: "weather-light", BackendID: "local", ModelName: "light-model"},
"weather-deep": {ProfileID: "weather-deep", BackendID: "cloud", ModelName: "deep-model"},
},
}
profileIDs := []string{"weather-light", "weather-deep"}
result, err := InspectComparisonExecution(context.Background(), ComparisonInspectionRequest{
Resolved: resolved, ProfileIDs: profileIDs, Executor: executor,
})
if err != nil {
t.Fatalf("InspectComparisonExecution() error = %v", err)
}
if result.PromptID != resolved.Definition.PromptID || result.PromptVersion != resolved.Definition.PromptVersion || result.PromptHash != "prompt-hash" {
t.Fatalf("prompt result = %#v", result)
}
if !reflect.DeepEqual(executor.profileRequests, profileIDs) || len(executor.promptRequests) != 1 || executor.executeRequests != 0 {
t.Fatalf("prompt/profile/execute requests = %#v/%#v/%d", executor.promptRequests, executor.profileRequests, executor.executeRequests)
}
wantProfiles := []ComparisonProfileInspection{
{ProfileID: "weather-light", BackendID: "local", ModelName: "light-model"},
{ProfileID: "weather-deep", BackendID: "cloud", ModelName: "deep-model"},
}
if !reflect.DeepEqual(result.Profiles, wantProfiles) {
t.Fatalf("profiles = %#v, want %#v", result.Profiles, wantProfiles)
}
}
func TestInspectComparisonExecutionRejectsInvalidProfilesBeforeInspection(t *testing.T) {
resolved := inspectionResolved(t)
for _, profileIDs := range [][]string{
{"weather-light"},
{"weather-light", " \t"},
{"weather-light", "weather-light"},
} {
t.Run(strings.Join(profileIDs, ","), func(t *testing.T) {
executor := &inspectionExecutor{prompt: validPromptInspection(resolved.Definition)}
_, err := InspectComparisonExecution(context.Background(), ComparisonInspectionRequest{
Resolved: resolved, ProfileIDs: profileIDs, Executor: executor,
})
if err == nil || promptexec.CategoryOf(err) != promptexec.InvalidRequest {
t.Fatalf("error/category = %v/%q, want invalid request", err, promptexec.CategoryOf(err))
}
if len(executor.promptRequests) != 0 || len(executor.profileRequests) != 0 || executor.executeRequests != 0 {
t.Fatalf("invalid profile selection performed prompt/profile/execution work: %#v/%#v/%d", executor.promptRequests, executor.profileRequests, executor.executeRequests)
}
})
}
}
func TestInspectComparisonExecutionStopsAtFirstProfileFailure(t *testing.T) {
resolved := inspectionResolved(t)
executor := &inspectionExecutor{
prompt: validPromptInspection(resolved.Definition),
profiles: map[string]promptexec.ProfileInspection{
"weather-light": {ProfileID: "weather-light", BackendID: "local", ModelName: "light-model"},
"missing-key": {ProfileID: "missing-key", APIKeyEnv: "PROMPT_API_KEY"},
"weather-deep": {ProfileID: "weather-deep", BackendID: "cloud", ModelName: "deep-model"},
},
}
_, err := InspectComparisonExecution(context.Background(), ComparisonInspectionRequest{
Resolved: resolved, ProfileIDs: []string{"weather-light", "missing-key", "weather-deep"}, Executor: executor,
LookupEnv: func(string) (string, bool) { return "", false },
})
if err == nil || promptexec.CategoryOf(err) != promptexec.MissingCredential {
t.Fatalf("error/category = %v/%q, want missing credential", err, promptexec.CategoryOf(err))
}
if !reflect.DeepEqual(executor.profileRequests, []string{"weather-light", "missing-key"}) || len(executor.promptRequests) != 1 || executor.executeRequests != 0 {
t.Fatalf("prompt/profile/execute requests = %#v/%#v/%d", executor.promptRequests, executor.profileRequests, executor.executeRequests)
}
}
func TestInspectComparisonExecutionStopsBeforeProfileInspectionWhenPromptFails(t *testing.T) {
resolved := inspectionResolved(t)
executor := &inspectionExecutor{promptErr: promptexec.NewError(promptexec.PromptNotFound, "prompt is unavailable", nil)}
_, err := InspectComparisonExecution(context.Background(), ComparisonInspectionRequest{
Resolved: resolved, ProfileIDs: []string{"weather-light", "weather-deep"}, Executor: executor,
})
if err == nil || promptexec.CategoryOf(err) != promptexec.PromptNotFound || !strings.Contains(err.Error(), "comparison prompt") {
t.Fatalf("error/category = %v/%q, want prompt-context prompt not found", err, promptexec.CategoryOf(err))
}
if len(executor.promptRequests) != 1 || len(executor.profileRequests) != 0 || executor.executeRequests != 0 {
t.Fatalf("prompt/profile/execute requests = %#v/%#v/%d", executor.promptRequests, executor.profileRequests, executor.executeRequests)
}
}
type inspectionPromptRequest struct {
id string
version string
@@ -143,6 +233,7 @@ type inspectionExecutor struct {
promptErr error
promptRequests []inspectionPromptRequest
profileRequests []string
executeRequests int
}
func (e *inspectionExecutor) InspectPrompt(_ context.Context, id string, version string) (promptexec.PromptInspection, error) {
@@ -166,6 +257,7 @@ func (e *inspectionExecutor) InspectProfile(_ context.Context, id string) (promp
}
func (e *inspectionExecutor) Execute(context.Context, promptexec.ExecuteRequest, promptexec.PreparationCallback) (*promptexec.Execution, error) {
e.executeRequests++
return nil, errors.New("unexpected execution")
}