Files
creator-hub/internal/creator/ai_settings_test.go
T
rogee f345e368df
douyin-release-gate / verify (push) Failing after 19m29s
feat: configure OpenAI-compatible AI services and discover models
2026-10-07 17:17:33 +08:00

116 lines
4.7 KiB
Go

package creator
import (
"errors"
"reflect"
"testing"
"time"
)
func TestAISettingsValidation(t *testing.T) {
if _, err := validateAISettings(SettingsUpdate{}); err != nil {
t.Fatalf("unconfigured settings: %v", err)
}
for _, input := range []SettingsUpdate{
{AIBaseURL: "https://example.com/v1"},
{AIAPIKey: "key"},
{AIModel: "model"},
{AIBaseURL: "https://example.com/v1", AIAPIKey: "key"},
{AIBaseURL: "bad-url", AIAPIKey: "key", AIModel: "model"},
} {
if _, err := validateAISettings(input); !errors.Is(err, ErrInvalid) {
t.Fatalf("incomplete settings accepted: %+v err=%v", input, err)
}
}
input, err := validateAISettings(SettingsUpdate{AIBaseURL: " https://example.com/custom/v1/ ", AIAPIKey: " key ", AIModel: " model "})
if err != nil || input.AIBaseURL != "https://example.com/custom/v1" || input.AIAPIKey != "key" || input.AIModel != "model" {
t.Fatalf("normalization=%+v err=%v", input, err)
}
}
func TestAISettingsDoNotResetActiveCollectionOrMetrics(t *testing.T) {
store, phaseAStore, ctx := openCreatorIntegrationStore(t)
accountID := createIntegrationAccount(t, ctx, phaseAStore, "ai-settings-owner")
now := time.Now().UTC().Truncate(time.Second)
start, end, err := NewCollectionWindow(now, 30)
if err != nil {
t.Fatal(err)
}
lease, err := store.beginCheckpoint(ctx, SourceOwned, accountID, "works", start, end)
if err != nil {
t.Fatal(err)
}
if lease == "" {
t.Fatal("checkpoint lease missing")
}
if _, err := store.db.ExecContext(ctx, `UPDATE creator_collection_checkpoint SET cursor='page-4',last_error='keep this',last_completed_at=$1 WHERE source_type=$2 AND source_id=$3`, now.Add(-time.Hour), SourceOwned, accountID); err != nil {
t.Fatal(err)
}
before, err := store.checkpoint(ctx, SourceOwned, accountID, "works")
if err != nil {
t.Fatal(err)
}
work, _, err := store.UpsertWork(ctx, WorkInput{Platform: "douyin", WorkKey: "ai-settings-work", SourceType: SourceOwned, SourceID: accountID, Title: "work", PublishedAt: &now, PublishedAtStatus: "verified"}, now)
if err != nil {
t.Fatal(err)
}
var nextBefore, nextAfter *time.Time
if err := store.db.QueryRowContext(ctx, `SELECT metric_plan_next_at FROM creator_work WHERE work_id=$1`, work.ID).Scan(&nextBefore); err != nil {
t.Fatal(err)
}
settings, err := store.GetSettings(ctx)
if err != nil {
t.Fatal(err)
}
input := SettingsUpdate{LookbackDays: settings.LookbackDays, NewWorkIntervalSeconds: settings.NewWorkIntervalSeconds, MetricInitialIntervalSeconds: settings.MetricInitialIntervalSeconds, MetricMultiplier: settings.MetricMultiplier, MetricMaxIntervalSeconds: settings.MetricMaxIntervalSeconds, MetricAgeSeconds: settings.MetricAgeSeconds, AIBaseURL: "https://example.com/v1", AIAPIKey: "key", AIModel: "model"}
if _, err := store.UpdateSettings(ctx, input); err != nil {
t.Fatalf("AI-only save must work during collection: %v", err)
}
after, err := store.checkpoint(ctx, SourceOwned, accountID, "works")
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(before, after) {
t.Fatalf("checkpoint changed: before=%+v after=%+v", before, after)
}
if err := store.db.QueryRowContext(ctx, `SELECT metric_plan_next_at FROM creator_work WHERE work_id=$1`, work.ID).Scan(&nextAfter); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(nextBefore, nextAfter) {
t.Fatalf("metric schedule changed: %v -> %v", nextBefore, nextAfter)
}
if _, err := store.UpdateSettings(ctx, input); err != nil {
t.Fatalf("unchanged save: %v", err)
}
input.AIBaseURL, input.AIAPIKey, input.AIModel = "", "", ""
if _, err := store.UpdateSettings(ctx, input); err != nil {
t.Fatalf("clear AI: %v", err)
}
saved, err := store.GetSettings(ctx)
if err != nil || saved.AIBaseURL != "" || saved.AIAPIKey != "" || saved.AIModel != "" {
t.Fatalf("clear was not persisted: %+v err=%v", saved, err)
}
input.LookbackDays++
if _, err := store.UpdateSettings(ctx, input); !errors.Is(err, ErrConflict) {
t.Fatalf("collection change while running: %v", err)
}
if err := store.finishCheckpoint(ctx, SourceOwned, accountID, "works", lease, "succeeded", "", nil); err != nil {
t.Fatal(err)
}
if _, err := store.UpdateSettings(ctx, input); err != nil {
t.Fatalf("collection change after completion: %v", err)
}
if err := store.db.QueryRowContext(ctx, `SELECT metric_plan_next_at FROM creator_work WHERE work_id=$1`, work.ID).Scan(&nextAfter); err != nil || nextAfter == nil {
t.Fatalf("metric plan not recalculated: %v err=%v", nextAfter, err)
}
input.AIBaseURL = "not-a-url"
if _, err := store.UpdateSettings(ctx, input); !errors.Is(err, ErrInvalid) {
t.Fatalf("invalid URL: %v", err)
}
input.AIBaseURL = ""
input.LookbackDays = 0
if _, err := store.UpdateSettings(ctx, input); !errors.Is(err, ErrInvalid) {
t.Fatalf("invalid collection setting: %v", err)
}
}