package promptkit import ( "context" "reflect" "strconv" "sync" "testing" "gitea.maximumdirect.net/eric/promptkit/internal/domain" ) func TestPublicLLMClientAdapterGivesClientOwnedNestedValues(t *testing.T) { source := adapterOwnershipRequest() want := adapterOwnershipRequest() client := &retainingMutatingLLMClient{} adapter := publicLLMClientAdapter{client: client} if _, err := adapter.Generate(context.Background(), source); err != nil { t.Fatalf("generate: %v", err) } if !reflect.DeepEqual(source, want) { t.Fatalf("client mutation changed prepared source:\ngot %#v\nwant %#v", source, want) } var mutations sync.WaitGroup mutations.Add(1) go func() { defer mutations.Done() for i := 0; i < 10_000; i++ { mutateGenerateRequest(&client.retained, strconv.Itoa(i)) } }() for i := 0; i < 10_000; i++ { laterRequest := fromDomainGenerateRequest(source) if laterRequest.Prompt.Messages[0].Content != "source-message" || laterRequest.Prompt.Messages[0].CacheControl.TTL != "source-ttl" || laterRequest.Target.ExtraParams["nested"].([]any)[0] != "source-extra" || laterRequest.StructuredOutput.JSONSchema.Schema.(map[string]any)["enum"].([]any)[0] != "source-schema" { t.Fatal("retained client mutation reached a later execution request") } } mutations.Wait() if !reflect.DeepEqual(source, want) { t.Fatalf("retained client mutation changed prepared source:\ngot %#v\nwant %#v", source, want) } } type retainingMutatingLLMClient struct { retained GenerateRequest } func (c *retainingMutatingLLMClient) Generate(_ context.Context, request GenerateRequest) (*GenerateResponse, error) { c.retained = request mutateGenerateRequest(&c.retained, "client-mutation") return &GenerateResponse{Content: "generated"}, nil } func adapterOwnershipRequest() domain.GenerateRequest { return domain.GenerateRequest{ Prompt: domain.RenderedPrompt{ SessionID: "source-session", Messages: []domain.RenderedMessage{{ Role: "user", Content: "source-message", CacheControl: &domain.CacheControl{ Type: domain.CacheControlEphemeral, TTL: "source-ttl", }, }}, }, Target: domain.ExecutionTarget{ Model: "source-model", APIKey: "source-api-key", ExtraParams: map[string]any{ "nested": []any{"source-extra"}, }, }, StructuredOutput: &domain.StructuredOutputSpec{ Type: domain.StructuredOutputJSONSchema, JSONSchema: &domain.StructuredOutputJSONSpec{ Name: "source-schema-name", Strict: true, Schema: map[string]any{"enum": []any{"source-schema"}}, }, }, } } func mutateGenerateRequest(request *GenerateRequest, value string) { request.Prompt.Messages[0].Content = value request.Prompt.Messages[0].CacheControl.TTL = value request.Target.ExtraParams["nested"].([]any)[0] = value request.StructuredOutput.JSONSchema.Schema.(map[string]any)["enum"].([]any)[0] = value }