From 475ab9f35cd7681a125b73641dd5bd5a94f54b27 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 17 Mar 2026 21:30:17 +0000 Subject: [PATCH] test: cover openai responses params --- providers/openai/responses_params_test.go | 275 ++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 providers/openai/responses_params_test.go diff --git a/providers/openai/responses_params_test.go b/providers/openai/responses_params_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c7d161630f9c86a9e4fdfb19fbd4a3c74f28bf86 --- /dev/null +++ b/providers/openai/responses_params_test.go @@ -0,0 +1,275 @@ +package openai + +import ( + "encoding/json" + "testing" + + "charm.land/fantasy" + "github.com/stretchr/testify/require" +) + +func TestPrepareParams_Store(t *testing.T) { + t.Parallel() + + lm := testResponsesLM() + prompt := fantasy.Prompt{testTextMessage(fantasy.MessageRoleUser, "hello")} + + tests := []struct { + name string + opts *ResponsesProviderOptions + wantStore bool + }{ + { + name: "store true", + opts: &ResponsesProviderOptions{Store: fantasy.Opt(true)}, + wantStore: true, + }, + { + name: "store false", + opts: &ResponsesProviderOptions{Store: fantasy.Opt(false)}, + wantStore: false, + }, + { + name: "store default", + opts: &ResponsesProviderOptions{}, + wantStore: false, + }, + { + name: "no provider options", + opts: nil, + wantStore: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + params, warnings, err := lm.prepareParams(testCall(prompt, tt.opts)) + require.NoError(t, err) + require.Empty(t, warnings) + require.True(t, params.Store.Valid()) + require.Equal(t, tt.wantStore, params.Store.Value) + }) + } +} + +func TestPrepareParams_PreviousResponseID(t *testing.T) { + t.Parallel() + + lm := testResponsesLM() + prompt := fantasy.Prompt{testTextMessage(fantasy.MessageRoleUser, "hello")} + + t.Run("forwarded", func(t *testing.T) { + t.Parallel() + + params, warnings, err := lm.prepareParams(testCall(prompt, &ResponsesProviderOptions{ + PreviousResponseID: fantasy.Opt("resp_abc123"), + })) + require.NoError(t, err) + require.Empty(t, warnings) + require.True(t, params.PreviousResponseID.Valid()) + require.Equal(t, "resp_abc123", params.PreviousResponseID.Value) + }) + + t.Run("not set", func(t *testing.T) { + t.Parallel() + + params, warnings, err := lm.prepareParams(testCall(prompt, &ResponsesProviderOptions{})) + require.NoError(t, err) + require.Empty(t, warnings) + require.False(t, params.PreviousResponseID.Valid()) + }) +} + +func TestPrepareParams_PreviousResponseID_Validation(t *testing.T) { + t.Parallel() + + lm := testResponsesLM() + opts := &ResponsesProviderOptions{PreviousResponseID: fantasy.Opt("resp_abc123")} + + t.Run("rejects with assistant messages", func(t *testing.T) { + t.Parallel() + + _, _, err := lm.prepareParams(testCall(fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleUser, "hello"), + testTextMessage(fantasy.MessageRoleAssistant, "hi there"), + }, opts)) + require.EqualError(t, err, previousResponseIDHistoryError) + }) + + t.Run("allows user-only prompt", func(t *testing.T) { + t.Parallel() + + _, warnings, err := lm.prepareParams(testCall(fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleUser, "hello"), + testTextMessage(fantasy.MessageRoleUser, "follow up"), + }, opts)) + require.NoError(t, err) + require.Empty(t, warnings) + }) + + t.Run("allows system + user prompt", func(t *testing.T) { + t.Parallel() + + _, warnings, err := lm.prepareParams(testCall(fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleSystem, "be concise"), + testTextMessage(fantasy.MessageRoleUser, "hello"), + }, opts)) + require.NoError(t, err) + require.Empty(t, warnings) + }) + + t.Run("allows tool messages without assistant history", func(t *testing.T) { + t.Parallel() + + _, warnings, err := lm.prepareParams(testCall(fantasy.Prompt{ + testToolResultMessage("done"), + testTextMessage(fantasy.MessageRoleUser, "hello"), + }, opts)) + require.NoError(t, err) + require.Empty(t, warnings) + }) +} + +func TestValidatePreviousResponseIDPrompt(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + prompt fantasy.Prompt + wantErr bool + }{ + { + name: "empty prompt", + prompt: nil, + }, + { + name: "user-only messages", + prompt: fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleUser, "hello"), + testTextMessage(fantasy.MessageRoleUser, "follow up"), + }, + }, + { + name: "system + user messages", + prompt: fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleSystem, "be concise"), + testTextMessage(fantasy.MessageRoleUser, "hello"), + }, + }, + { + name: "contains assistant message", + prompt: fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleAssistant, "hi there"), + }, + wantErr: true, + }, + { + name: "assistant in the middle", + prompt: fantasy.Prompt{ + testTextMessage(fantasy.MessageRoleUser, "hello"), + testTextMessage(fantasy.MessageRoleAssistant, "hi there"), + testTextMessage(fantasy.MessageRoleUser, "follow up"), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := validatePreviousResponseIDPrompt(tt.prompt) + if tt.wantErr { + require.EqualError(t, err, previousResponseIDHistoryError) + return + } + + require.NoError(t, err) + }) + } +} + +func TestResponsesProviderMetadata_Helper(t *testing.T) { + t.Parallel() + + t.Run("non-empty id", func(t *testing.T) { + t.Parallel() + + metadata := responsesProviderMetadata("resp_123") + require.Len(t, metadata, 1) + + providerMetadata, ok := metadata[Name].(*ResponsesProviderMetadata) + require.True(t, ok) + require.Equal(t, "resp_123", providerMetadata.ResponseID) + }) + + t.Run("empty id", func(t *testing.T) { + t.Parallel() + + metadata := responsesProviderMetadata("") + require.Empty(t, metadata) + }) +} + +func TestResponsesProviderMetadata_JSON(t *testing.T) { + t.Parallel() + + encoded, err := json.Marshal(ResponsesProviderMetadata{ResponseID: "resp_123"}) + require.NoError(t, err) + require.Contains(t, string(encoded), `"response_id":"resp_123"`) + + decoded, err := fantasy.UnmarshalProviderMetadata(map[string]json.RawMessage{ + Name: encoded, + }) + require.NoError(t, err) + + providerMetadata, ok := decoded[Name].(*ResponsesProviderMetadata) + require.True(t, ok) + require.Equal(t, "resp_123", providerMetadata.ResponseID) +} + +func testCall(prompt fantasy.Prompt, opts *ResponsesProviderOptions) fantasy.Call { + call := fantasy.Call{ + Prompt: prompt, + } + if opts != nil { + call.ProviderOptions = fantasy.ProviderOptions{ + Name: opts, + } + } + return call +} + +func testResponsesLM() responsesLanguageModel { + return responsesLanguageModel{ + provider: Name, + modelID: "gpt-4o", + } +} + +func testTextMessage(role fantasy.MessageRole, text string) fantasy.Message { + return fantasy.Message{ + Role: role, + Content: []fantasy.MessagePart{ + fantasy.TextPart{Text: text}, + }, + } +} + +func testToolResultMessage(text string) fantasy.Message { + return fantasy.Message{ + Role: fantasy.MessageRoleTool, + Content: []fantasy.MessagePart{ + fantasy.ToolResultPart{ + ToolCallID: "call_123", + Output: fantasy.ToolResultOutputContentText{ + Text: text, + }, + }, + }, + } +}