package aiprovider import ( "context" "testing" "github.com/descrybe/descrybe-v2/apps/api/internal/aiaudit" "github.com/descrybe/descrybe-v2/apps/api/internal/processing" "github.com/google/uuid" ) type optionsStub struct{ withOptions, plain int } func (s *optionsStub) Complete(context.Context, string, string) (processing.Completion, error) { s.plain++ return processing.Completion{Text: "{}"}, nil } func (s *optionsStub) CompleteWithOptions(context.Context, string, string, processing.CompleteOptions) (processing.Completion, error) { s.withOptions++ return processing.Completion{Text: "{}"}, nil } // Regression: processing.CompleteOnce prefers CompleterWithOptions and only falls // back to Complete. A wrapper that does not forward the options interface silently // drops MaxTokens / Temperature / ReasoningEffort for every enhance call. func TestAuditingCompleter_forwardsCompleteWithOptions(t *testing.T) { t.Parallel() inner := &optionsStub{} svc := &Service{Audit: &aiaudit.Recorder{}} wrapped := svc.WrapAudit(inner, uuid.New(), RoleProcessing, "internal") if _, ok := wrapped.(processing.CompleterWithOptions); !ok { t.Fatal("wrapped completer must still satisfy CompleterWithOptions") } if _, err := processing.CompleteOnce(context.Background(), wrapped, "sys", "user", processing.CompleteOptions{MaxTokens: 1234}); err != nil { t.Fatal(err) } if inner.withOptions != 1 || inner.plain != 0 { t.Fatalf("options path not forwarded: withOptions=%d plain=%d", inner.withOptions, inner.plain) } } // A plain Completer must not gain the options interface just by being wrapped — // that would send options to a client that cannot honour them. func TestAuditingCompleter_plainCompleterStaysPlain(t *testing.T) { t.Parallel() inner := plainStub{} svc := &Service{Audit: &aiaudit.Recorder{}} wrapped := svc.WrapAudit(inner, uuid.New(), RoleProcessing, "internal") if _, ok := wrapped.(processing.CompleterWithOptions); ok { t.Fatal("plain completer must not advertise CompleterWithOptions") } } type plainStub struct{} func (plainStub) Complete(context.Context, string, string) (processing.Completion, error) { return processing.Completion{Text: "{}"}, nil } func TestWrapAudit_noopWhenCaptureOff(t *testing.T) { t.Parallel() inner := plainStub{} var svc *Service if got := svc.WrapAudit(inner, uuid.Nil, RoleProcessing, ""); got != processing.Completer(inner) { t.Fatal("nil service must return the completer untouched") } svc = &Service{} if got := svc.WrapAudit(inner, uuid.Nil, RoleProcessing, ""); got != processing.Completer(inner) { t.Fatal("capture off must return the completer untouched") } }