| package chat |
|
|
| import ( |
| "context" |
| "net/http" |
| "net/http/httptest" |
| "strings" |
| "testing" |
|
|
| "ds2api/internal/auth" |
| dsclient "ds2api/internal/deepseek/client" |
| ) |
|
|
| type autoDeleteModeDSStub struct { |
| resp *http.Response |
| singleCalls int |
| allCalls int |
| lastSessionID string |
| lastCtxErr error |
| } |
|
|
| func (m *autoDeleteModeDSStub) CreateSession(_ context.Context, _ *auth.RequestAuth, _ int) (string, error) { |
| return "session-id", nil |
| } |
|
|
| func (m *autoDeleteModeDSStub) GetPow(_ context.Context, _ *auth.RequestAuth, _ int) (string, error) { |
| return "pow", nil |
| } |
|
|
| func (m *autoDeleteModeDSStub) UploadFile(_ context.Context, _ *auth.RequestAuth, _ dsclient.UploadFileRequest, _ int) (*dsclient.UploadFileResult, error) { |
| return &dsclient.UploadFileResult{ID: "file-id", Filename: "file.txt", Bytes: 1, Status: "uploaded"}, nil |
| } |
|
|
| func (m *autoDeleteModeDSStub) CallCompletion(_ context.Context, _ *auth.RequestAuth, _ map[string]any, _ string, _ int) (*http.Response, error) { |
| return m.resp, nil |
| } |
|
|
| func (m *autoDeleteModeDSStub) DeleteSessionForToken(_ context.Context, _ string, sessionID string) (*dsclient.DeleteSessionResult, error) { |
| m.singleCalls++ |
| m.lastSessionID = sessionID |
| return &dsclient.DeleteSessionResult{SessionID: sessionID, Success: true}, nil |
| } |
|
|
| func (m *autoDeleteModeDSStub) DeleteAllSessionsForToken(_ context.Context, _ string) error { |
| m.allCalls++ |
| return nil |
| } |
|
|
| func (m *autoDeleteModeDSStub) DeleteSessionForTokenCtx(ctx context.Context, _ string, sessionID string) (*dsclient.DeleteSessionResult, error) { |
| m.singleCalls++ |
| m.lastSessionID = sessionID |
| m.lastCtxErr = ctx.Err() |
| return &dsclient.DeleteSessionResult{SessionID: sessionID, Success: true}, nil |
| } |
|
|
| func TestChatCompletionsAutoDeleteModes(t *testing.T) { |
| tests := []struct { |
| name string |
| mode string |
| wantSingle int |
| wantAll int |
| }{ |
| {name: "none", mode: "none"}, |
| {name: "single", mode: "single", wantSingle: 1}, |
| {name: "all", mode: "all", wantAll: 1}, |
| } |
|
|
| for _, tc := range tests { |
| t.Run(tc.name, func(t *testing.T) { |
| ds := &autoDeleteModeDSStub{ |
| resp: makeOpenAISSEHTTPResponse( |
| `data: {"p":"response/content","v":"hello"}`, |
| "data: [DONE]", |
| ), |
| } |
| h := &Handler{ |
| Store: mockOpenAIConfig{ |
| autoDeleteMode: tc.mode, |
| }, |
| Auth: streamStatusAuthStub{}, |
| DS: ds, |
| } |
|
|
| reqBody := `{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"stream":false}` |
| req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(reqBody)) |
| req.Header.Set("Authorization", "Bearer direct-token") |
| req.Header.Set("Content-Type", "application/json") |
| rec := httptest.NewRecorder() |
|
|
| h.ChatCompletions(rec, req) |
|
|
| if rec.Code != http.StatusOK { |
| t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) |
| } |
| if ds.singleCalls != tc.wantSingle { |
| t.Fatalf("single delete calls=%d want=%d", ds.singleCalls, tc.wantSingle) |
| } |
| if ds.allCalls != tc.wantAll { |
| t.Fatalf("all delete calls=%d want=%d", ds.allCalls, tc.wantAll) |
| } |
| if tc.wantSingle > 0 && ds.lastSessionID != "session-id" { |
| t.Fatalf("expected single delete for session-id, got %q", ds.lastSessionID) |
| } |
| }) |
| } |
| } |
|
|
| type autoDeleteCtxDSStub struct { |
| autoDeleteModeDSStub |
| } |
|
|
| func (m *autoDeleteCtxDSStub) DeleteSessionForToken(ctx context.Context, token string, sessionID string) (*dsclient.DeleteSessionResult, error) { |
| return m.DeleteSessionForTokenCtx(ctx, token, sessionID) |
| } |
|
|
| func (m *autoDeleteCtxDSStub) DeleteAllSessionsForToken(_ context.Context, _ string) error { |
| m.allCalls++ |
| return nil |
| } |
|
|
| func TestAutoDeleteRemoteSessionIgnoresCanceledParentContext(t *testing.T) { |
| ds := &autoDeleteCtxDSStub{} |
| h := &Handler{ |
| Store: mockOpenAIConfig{ |
| autoDeleteMode: "single", |
| }, |
| DS: ds, |
| } |
| a := &auth.RequestAuth{DeepSeekToken: "token", AccountID: "acct"} |
| ctx, cancel := context.WithCancel(context.Background()) |
| cancel() |
|
|
| h.autoDeleteRemoteSession(ctx, a, "session-id") |
|
|
| if ds.singleCalls != 1 { |
| t.Fatalf("single delete calls=%d want=1", ds.singleCalls) |
| } |
| if ds.lastCtxErr != nil { |
| t.Fatalf("delete ctx should not inherit cancellation, got %v", ds.lastCtxErr) |
| } |
| } |
|
|