package worker import ( "context" "net/http" "strings" "testing" "time" ) // TestClassifyDramaErrorTreatsTimeoutAsUpstreamFailure 验证超时不会再进入提交未知状态。 func TestClassifyDramaErrorTreatsTimeoutAsUpstreamFailure(t *testing.T) { if code := classifyDramaError(context.DeadlineExceeded); code != "upstream_timeout" { t.Fatalf("timeout code = %q, want upstream_timeout", code) } } // TestDramaModelRetryLimit 验证剧本解析调用的自动重试次数。 func TestDramaModelRetryLimit(t *testing.T) { if maxDramaModelAttempts != 5 { t.Fatalf("retry attempts = %d, want 5", maxDramaModelAttempts) } } func TestDramaHTTPClientAllowsLongTextResponses(t *testing.T) { sharedTransport := &http.Transport{TLSHandshakeTimeout: 10 * time.Second, ResponseHeaderTimeout: time.Minute} shared := &http.Client{Transport: sharedTransport} client := dramaHTTPClient(shared) transport, ok := client.Transport.(*http.Transport) if !ok { t.Fatal("drama client transport was not preserved") } if transport.TLSHandshakeTimeout != 30*time.Second || transport.ResponseHeaderTimeout != 10*time.Minute { t.Fatalf("unexpected drama timeouts: TLS=%s response=%s", transport.TLSHandshakeTimeout, transport.ResponseHeaderTimeout) } if sharedTransport.TLSHandshakeTimeout != 10*time.Second || sharedTransport.ResponseHeaderTimeout != time.Minute { t.Fatal("shared AI transport was modified") } } func TestParseDramaJSONNormalizesStoryboard(t *testing.T) { result, err := parseDramaJSON("hidden```json\n{\"entities\":{\"roles\":[{\"name\":\"林夏\",\"image_prompt\":\"二十五岁女性,黑色长发\"}]},\"storyboards\":[{\"content\":\"走进房间\",\"duration_seconds\":99}]}\n```") if err != nil { t.Fatalf("parseDramaJSON returned error: %v", err) } if len(result.Entities.Characters) != 1 || len(result.Storyboards) != 1 { t.Fatalf("unexpected normalized result: %#v", result) } if result.Storyboards[0].ScriptContent != "走进房间" || result.Storyboards[0].DurationSeconds != 5 { t.Fatalf("storyboard was not normalized: %#v", result.Storyboards[0]) } if result.Entities.Characters[0].ImagePrompt != "二十五岁女性,黑色长发" { t.Fatalf("asset image prompt was not preserved: %#v", result.Entities.Characters[0]) } } func TestEstimateTextTokens(t *testing.T) { if estimateTextTokens("中文ABCD") != 3 { t.Fatalf("unexpected token estimate") } } func TestBuildDramaUserPromptUsesOnlyCurrentContext(t *testing.T) { snapshot := dramaParseSnapshot{ ProjectName: "测试项目", StyleName: "国风水墨", EraType: "ancient_xianxia", EpisodeNo: 4, EpisodeName: "再入山门", } prompt := buildDramaUserPrompt(snapshot, "当前集正文", []map[string]any{{"name": "林夏"}}) for _, expected := range []string{ "视觉风格:国风水墨", "故事时代:古代仙侠", "项目资产:", "林夏", "当前原文:\n当前集正文", "按上述视觉风格和故事时代解析角色、场景、道具及分镜", } { if !strings.Contains(prompt, expected) { t.Fatalf("prompt does not contain %q: %s", expected, prompt) } } for _, forbidden := range []string{"continuity", "解析分段", "前序"} { if strings.Contains(prompt, forbidden) { t.Fatalf("prompt contains forbidden content %q: %s", forbidden, prompt) } } } func TestDramaEraNameUsesCustomEra(t *testing.T) { if got := dramaEraName("other", " 民国时期 "); got != "民国时期" { t.Fatalf("unexpected custom era: %q", got) } }