517 lines
18 KiB
Go
517 lines
18 KiB
Go
package controller
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/topfans/backend/pkg/models"
|
||
)
|
||
|
||
// TestMain 已统一在 asset_controller_test.go 中声明,此处不再重复.
|
||
// (两文件同属 controller 包,TestMain 全局唯一)
|
||
|
||
// fakeLaserRepo LaserCardPersister 的 in-memory fake 实现
|
||
// 记录每次调用,便于断言
|
||
type fakeLaserRepo struct {
|
||
templates map[string]*models.LaserCardTemplate
|
||
createErr error
|
||
snapshotErr error
|
||
instances []*models.LaserCardInstance
|
||
snapshotsByID map[int64]models.MaterialsSnapshot
|
||
logs []fakeOpLog
|
||
nextInstanceID int64
|
||
}
|
||
|
||
type fakeOpLog struct {
|
||
instanceID int64
|
||
instanceNo string
|
||
userID int64
|
||
action string
|
||
statusBefore string
|
||
statusAfter string
|
||
}
|
||
|
||
func newFakeRepo() *fakeLaserRepo {
|
||
return &fakeLaserRepo{
|
||
templates: map[string]*models.LaserCardTemplate{},
|
||
snapshotsByID: map[int64]models.MaterialsSnapshot{},
|
||
}
|
||
}
|
||
|
||
func (f *fakeLaserRepo) FindTemplateByCode(code string) (*models.LaserCardTemplate, error) {
|
||
if t, ok := f.templates[code]; ok {
|
||
return t, nil
|
||
}
|
||
return nil, fmt.Errorf("template %q not found", code)
|
||
}
|
||
|
||
func (f *fakeLaserRepo) CreateInstance(inst *models.LaserCardInstance) error {
|
||
if f.createErr != nil {
|
||
return f.createErr
|
||
}
|
||
f.nextInstanceID++
|
||
inst.ID = f.nextInstanceID
|
||
inst.InstanceNo = fmt.Sprintf("LC%010d", f.nextInstanceID)
|
||
f.instances = append(f.instances, inst)
|
||
return nil
|
||
}
|
||
|
||
func (f *fakeLaserRepo) UpdateMaterialsSnapshot(instanceID int64, snapshot models.MaterialsSnapshot) error {
|
||
if f.snapshotErr != nil {
|
||
return f.snapshotErr
|
||
}
|
||
f.snapshotsByID[instanceID] = snapshot
|
||
return nil
|
||
}
|
||
|
||
func (f *fakeLaserRepo) CreateOperationLogSimple(instanceID int64, instanceNo string, userID int64, action, statusBefore, statusAfter string) error {
|
||
f.logs = append(f.logs, fakeOpLog{
|
||
instanceID: instanceID,
|
||
instanceNo: instanceNo,
|
||
userID: userID,
|
||
action: action,
|
||
statusBefore: statusBefore,
|
||
statusAfter: statusAfter,
|
||
})
|
||
return nil
|
||
}
|
||
|
||
// ============================================================
|
||
// persistGeneratedInstance 测试
|
||
// ============================================================
|
||
|
||
// 分支 1:laserRepo 为 nil → 直接返回 (0, "")
|
||
func TestPersistGeneratedInstance_LaserRepoNil(t *testing.T) {
|
||
ctrl := &LaserGenerateController{} // laserRepo 是 nil
|
||
id, no := ctrl.persistGeneratedInstance(123, 456, "dream", "job-1")
|
||
if id != 0 || no != "" {
|
||
t.Errorf("expected (0, \"\"), got (%d, %q)", id, no)
|
||
}
|
||
}
|
||
|
||
// 分支 2:CreateInstance 失败 → 返回 (0, ""),不写日志
|
||
func TestPersistGeneratedInstance_CreateFailed(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
fake.createErr = errors.New("db connection lost")
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
id, no := ctrl.persistGeneratedInstance(123, 456, "dream", "job-1")
|
||
|
||
if id != 0 || no != "" {
|
||
t.Errorf("expected (0, \"\"), got (%d, %q)", id, no)
|
||
}
|
||
if len(fake.instances) != 0 {
|
||
t.Errorf("expected 0 instances on failure, got %d", len(fake.instances))
|
||
}
|
||
if len(fake.logs) != 0 {
|
||
t.Errorf("expected 0 logs on create failure, got %d", len(fake.logs))
|
||
}
|
||
}
|
||
|
||
// 分支 3:CreateInstance 成功 + 模板命中 → 返回 ID/InstanceNo + 写创建日志
|
||
func TestPersistGeneratedInstance_CreateSucceed(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
fake.templates["dream"] = &models.LaserCardTemplate{ID: 5, TemplateCode: "dream"}
|
||
fake.nextInstanceID = 100
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
id, no := ctrl.persistGeneratedInstance(123, 456, "dream", "job-1")
|
||
|
||
if id != 101 {
|
||
t.Errorf("expected id=101, got %d", id)
|
||
}
|
||
if no != "LC0000000101" {
|
||
t.Errorf("expected no=LC0000000101, got %q", no)
|
||
}
|
||
|
||
// 验证 instance 字段被正确填充
|
||
if len(fake.instances) != 1 {
|
||
t.Fatalf("expected 1 instance, got %d", len(fake.instances))
|
||
}
|
||
inst := fake.instances[0]
|
||
if inst.OwnerUserID != 123 {
|
||
t.Errorf("OwnerUserID = %d, want 123", inst.OwnerUserID)
|
||
}
|
||
if inst.StarID != 456 {
|
||
t.Errorf("StarID = %d, want 456", inst.StarID)
|
||
}
|
||
if inst.TemplateCode != "dream" {
|
||
t.Errorf("TemplateCode = %q, want %q", inst.TemplateCode, "dream")
|
||
}
|
||
if inst.TemplateID != 5 {
|
||
t.Errorf("TemplateID = %d, want 5 (from FindTemplateByCode)", inst.TemplateID)
|
||
}
|
||
if inst.Status != models.LaserCardInstanceStatusRendered {
|
||
t.Errorf("Status = %q, want %q", inst.Status, models.LaserCardInstanceStatusRendered)
|
||
}
|
||
|
||
// 验证只写了 1 条创建日志(statusBefore="", statusAfter="rendered")
|
||
if len(fake.logs) != 1 {
|
||
t.Fatalf("expected 1 log, got %d", len(fake.logs))
|
||
}
|
||
got := fake.logs[0]
|
||
if got.statusBefore != "" || got.statusAfter != models.LaserCardInstanceStatusRendered {
|
||
t.Errorf("create log status wrong: before=%q after=%q", got.statusBefore, got.statusAfter)
|
||
}
|
||
if got.action != models.LaserCardActionGenerateVariants {
|
||
t.Errorf("action = %q, want %q", got.action, models.LaserCardActionGenerateVariants)
|
||
}
|
||
if got.userID != 123 {
|
||
t.Errorf("log userID = %d, want 123", got.userID)
|
||
}
|
||
}
|
||
|
||
// 分支 4:模板未命中(FindTemplateByCode 失败) → TemplateID 仍为 0,但 instance 创建继续
|
||
func TestPersistGeneratedInstance_TemplateNotFound(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
// 注意:fake.templates 为空,FindTemplateByCode 一定失败
|
||
fake.nextInstanceID = 50
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
id, no := ctrl.persistGeneratedInstance(123, 456, "missing_template", "job-1")
|
||
|
||
// 创建应该仍然成功(模板查不到是 warn 但不阻断)
|
||
if id != 51 || no != "LC0000000051" {
|
||
t.Errorf("expected (51, LC0000000051), got (%d, %q)", id, no)
|
||
}
|
||
if len(fake.instances) != 1 {
|
||
t.Fatalf("expected 1 instance, got %d", len(fake.instances))
|
||
}
|
||
if fake.instances[0].TemplateID != 0 {
|
||
t.Errorf("TemplateID = %d, want 0 when template not found", fake.instances[0].TemplateID)
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// attachMaterialsSnapshot 测试
|
||
// ============================================================
|
||
|
||
// 分支 A:instanceID <= 0 → 跳过整个流程,不写 snapshot 不写 log
|
||
func TestAttachMaterialsSnapshot_NoInstanceID(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
ctrl.attachMaterialsSnapshot(0, "LC001", 123, "cutout-url", "cutout", nil, "job-1")
|
||
|
||
if len(fake.snapshotsByID) != 0 {
|
||
t.Errorf("expected no snapshot write, got %d", len(fake.snapshotsByID))
|
||
}
|
||
if len(fake.logs) != 0 {
|
||
t.Errorf("expected no log write, got %d", len(fake.logs))
|
||
}
|
||
}
|
||
|
||
// 分支 B:成功 → 写 snapshot + 写 log;variants 中 oss_key 为空的项被跳过
|
||
func TestAttachMaterialsSnapshot_Success(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
variants := []map[string]interface{}{
|
||
{"preset_id": "dream", "oss_key": "oss://dream"},
|
||
{"preset_id": "classic", "oss_key": "oss://classic"},
|
||
{"preset_id": "no_key"}, // 缺少 oss_key → 跳过
|
||
}
|
||
ctrl.attachMaterialsSnapshot(101, "LC0000000101", 123, "cutout-url", "cutout", variants, "job-1")
|
||
|
||
// 验证 snapshot:cutout + 2 个有效 variant = 3 项
|
||
if len(fake.snapshotsByID) != 1 {
|
||
t.Fatalf("expected 1 snapshot, got %d", len(fake.snapshotsByID))
|
||
}
|
||
snap, ok := fake.snapshotsByID[101]
|
||
if !ok {
|
||
t.Fatalf("snapshot for instanceID=101 not found")
|
||
}
|
||
if len(snap) != 3 {
|
||
t.Fatalf("snapshot len = %d, want 3 (cutout + 2 variants, 3rd skipped)", len(snap))
|
||
}
|
||
|
||
// cutout 优先
|
||
if snap[0].Role != "cutout" || snap[0].OssKey != "cutout-url" {
|
||
t.Errorf("snapshot[0] wrong: %+v (want cutout/cutout-url)", snap[0])
|
||
}
|
||
// variants 按顺序
|
||
if snap[1].Role != "composite" || snap[1].OssKey != "oss://dream" || snap[1].PresetID != "dream" {
|
||
t.Errorf("snapshot[1] wrong: %+v", snap[1])
|
||
}
|
||
if snap[2].OssKey != "oss://classic" || snap[2].PresetID != "classic" {
|
||
t.Errorf("snapshot[2] wrong: %+v", snap[2])
|
||
}
|
||
|
||
// 验证 log(statusBefore="rendered", statusAfter="")
|
||
if len(fake.logs) != 1 {
|
||
t.Fatalf("expected 1 log, got %d", len(fake.logs))
|
||
}
|
||
got := fake.logs[0]
|
||
if got.statusBefore != models.LaserCardInstanceStatusRendered {
|
||
t.Errorf("log statusBefore = %q, want %q", got.statusBefore, models.LaserCardInstanceStatusRendered)
|
||
}
|
||
if got.statusAfter != "" {
|
||
t.Errorf("log statusAfter = %q, want \"\"", got.statusAfter)
|
||
}
|
||
if got.userID != 123 || got.instanceNo != "LC0000000101" {
|
||
t.Errorf("log userID/no = (%d, %q), want (123, LC0000000101)", got.userID, got.instanceNo)
|
||
}
|
||
}
|
||
|
||
// 分支 C:snapshot 写失败 → 只 warn,不写 log
|
||
func TestAttachMaterialsSnapshot_Failed(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
fake.snapshotErr = errors.New("update conflict")
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
ctrl.attachMaterialsSnapshot(101, "LC0000000101", 123, "cutout-url", "cutout", nil, "job-1")
|
||
|
||
// snapshot 写失败时,log 也跳过(避免误导审计)
|
||
if len(fake.logs) != 0 {
|
||
t.Errorf("expected 0 logs on snapshot failure, got %d", len(fake.logs))
|
||
}
|
||
}
|
||
|
||
// 分支 D:empty cutoutURL + empty variants → snapshot 是空的(不应 panic)
|
||
func TestAttachMaterialsSnapshot_EmptyInputs(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
ctrl.attachMaterialsSnapshot(101, "LC0000000101", 123, "", "cutout", nil, "job-1")
|
||
|
||
snap := fake.snapshotsByID[101]
|
||
if len(snap) != 0 {
|
||
t.Errorf("expected empty snapshot, got %d items", len(snap))
|
||
}
|
||
// 仍然写 1 条 log(记录 snapshot 已更新,即使内容为空)
|
||
if len(fake.logs) != 1 {
|
||
t.Errorf("expected 1 log even for empty snapshot, got %d", len(fake.logs))
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 集成场景:Dify 路径完整流程(persist + attach)
|
||
// ============================================================
|
||
|
||
func TestPersistAndAttach_DifyFlow(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
fake.templates["dream"] = &models.LaserCardTemplate{ID: 5, TemplateCode: "dream"}
|
||
fake.nextInstanceID = 200
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
// Step 1: 持久化 instance(Dify 路径同步执行)
|
||
id, no := ctrl.persistGeneratedInstance(123, 456, "dream", "job-dify-1")
|
||
if id == 0 || no == "" {
|
||
t.Fatalf("persist failed: id=%d, no=%q", id, no)
|
||
}
|
||
|
||
// Step 2: 写入 materials_snapshot
|
||
variants := []map[string]interface{}{
|
||
{"preset_id": "dream", "oss_key": "oss://dream"},
|
||
{"preset_id": "classic", "oss_key": "oss://classic"},
|
||
}
|
||
ctrl.attachMaterialsSnapshot(id, no, 123, "cutout-url", "cutout", variants, "job-dify-1")
|
||
|
||
// 验证最终状态:
|
||
// - 1 个 instance
|
||
// - 1 个 snapshot(3 项:cutout + 2 variants)
|
||
// - 2 条 op log(create + snapshot update)
|
||
if len(fake.instances) != 1 {
|
||
t.Errorf("instances = %d, want 1", len(fake.instances))
|
||
}
|
||
if len(fake.snapshotsByID) != 1 {
|
||
t.Errorf("snapshots = %d, want 1", len(fake.snapshotsByID))
|
||
}
|
||
if len(fake.logs) != 2 {
|
||
t.Fatalf("logs = %d, want 2 (create + snapshot)", len(fake.logs))
|
||
}
|
||
|
||
// log 顺序:先 create(empty → rendered),后 snapshot(rendered → empty)
|
||
if fake.logs[0].statusBefore != "" || fake.logs[0].statusAfter != "rendered" {
|
||
t.Errorf("log[0] should be create: got before=%q after=%q",
|
||
fake.logs[0].statusBefore, fake.logs[0].statusAfter)
|
||
}
|
||
if fake.logs[1].statusBefore != "rendered" || fake.logs[1].statusAfter != "" {
|
||
t.Errorf("log[1] should be snapshot: got before=%q after=%q",
|
||
fake.logs[1].statusBefore, fake.logs[1].statusAfter)
|
||
}
|
||
}
|
||
|
||
// 集成场景:MiniMax 异步路径(创建失败时,提交响应仍能正常返回)
|
||
func TestPersistGeneratedInstance_MiniMaxSubmitResponseStable(t *testing.T) {
|
||
fake := newFakeRepo()
|
||
fake.createErr = errors.New("db transient")
|
||
ctrl := &LaserGenerateController{laserRepo: fake}
|
||
|
||
// 即使创建失败,调用方仍拿到 (0, "") 而不是 panic
|
||
id, no := ctrl.persistGeneratedInstance(1, 2, "dream", "job-mm-1")
|
||
if id != 0 || no != "" {
|
||
t.Errorf("expected (0, \"\") on failure, got (%d, %q)", id, no)
|
||
}
|
||
// 后续 attach 即使被调用也是 no-op
|
||
ctrl.attachMaterialsSnapshot(id, no, 1, "", "cutout", nil, "job-mm-1")
|
||
if len(fake.snapshotsByID) != 0 {
|
||
t.Errorf("expected no snapshot when instanceID=0, got %d", len(fake.snapshotsByID))
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// buildMaterialsSnapshot 单元测试(纯函数)
|
||
// ============================================================
|
||
|
||
func TestBuildMaterialsSnapshot(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
cutout string
|
||
variants []map[string]interface{}
|
||
wantLen int
|
||
}{
|
||
{
|
||
name: "empty inputs",
|
||
cutout: "",
|
||
variants: nil,
|
||
wantLen: 0,
|
||
},
|
||
{
|
||
name: "only cutout",
|
||
cutout: "oss://cutout",
|
||
variants: nil,
|
||
wantLen: 1,
|
||
},
|
||
{
|
||
name: "variants with empty oss_key are skipped",
|
||
cutout: "",
|
||
variants: []map[string]interface{}{{"preset_id": "x", "oss_key": ""}, {"preset_id": "y"}},
|
||
wantLen: 0,
|
||
},
|
||
{
|
||
name: "mixed valid",
|
||
cutout: "oss://cutout",
|
||
variants: []map[string]interface{}{
|
||
{"preset_id": "dream", "oss_key": "oss://dream"},
|
||
{"preset_id": "classic", "oss_key": ""}, // skipped
|
||
{"preset_id": "ice", "oss_key": "oss://ice"},
|
||
},
|
||
wantLen: 3, // cutout + dream + ice
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
snap := buildMaterialsSnapshot(tt.cutout, "cutout", tt.variants)
|
||
if len(snap) != tt.wantLen {
|
||
t.Errorf("len = %d, want %d (snap=%+v)", len(snap), tt.wantLen, snap)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// enrichRenderConfigsWithUserPrompt 单元测试
|
||
// 设计目的:userPrompt 加权策略不能破坏现有 render_configs 结构,
|
||
// 必须保证 bg_prompt 不出现 userPrompt 的 3 次重复(只能 0 / 1 / 2 次)
|
||
// ============================================================
|
||
|
||
// 分支 1:userPrompt 为空 → 原样返回,不动 bg_prompt
|
||
func TestEnrichRenderConfigsWithUserPrompt_Empty(t *testing.T) {
|
||
input := []map[string]interface{}{
|
||
{"preset_id": "dream", "bg_prompt": "原始 prompt"},
|
||
}
|
||
got := enrichRenderConfigsWithUserPrompt(input, "")
|
||
var arr []map[string]interface{}
|
||
if err := json.Unmarshal(got, &arr); err != nil {
|
||
t.Fatalf("unmarshal failed: %v", err)
|
||
}
|
||
if len(arr) != 1 || arr[0]["bg_prompt"] != "原始 prompt" {
|
||
t.Errorf("empty userPrompt should not modify bg_prompt, got %+v", arr)
|
||
}
|
||
}
|
||
|
||
// 分支 2:bg_prompt 为空 → 以「主题(必须遵循,不可偏离)」开头建立主题锚点
|
||
func TestEnrichRenderConfigsWithUserPrompt_EmptyBgPrompt(t *testing.T) {
|
||
input := []map[string]interface{}{
|
||
{"preset_id": "dream", "bg_prompt": ""},
|
||
}
|
||
got := enrichRenderConfigsWithUserPrompt(input, "海报风格")
|
||
bg := extractBgPrompt(t, got, 0)
|
||
if !strings.Contains(bg, "主题(必须遵循,不可偏离)") || !strings.Contains(bg, "海报风格") {
|
||
t.Errorf("expected 主题(必须遵循,不可偏离) prefix + 海报风格, got %q", bg)
|
||
}
|
||
}
|
||
|
||
// 分支 3:bg_prompt 已包含 userPrompt(前端已加权)→ 跳过,避免 3 次重复
|
||
func TestEnrichRenderConfigsWithUserPrompt_AlreadyWeighted(t *testing.T) {
|
||
weighted := "主题(必须遵循,不可偏离): 海报风格. 主题再强调: 海报风格"
|
||
input := []map[string]interface{}{
|
||
{"preset_id": "dream", "bg_prompt": weighted},
|
||
}
|
||
got := enrichRenderConfigsWithUserPrompt(input, "海报风格")
|
||
bg := extractBgPrompt(t, got, 0)
|
||
if bg != weighted {
|
||
t.Errorf("already-weighted bg_prompt should be unchanged, got %q", bg)
|
||
}
|
||
count := strings.Count(bg, "海报风格")
|
||
if count > 2 {
|
||
t.Errorf("expected at most 2 occurrences, got %d (bg=%q)", count, bg)
|
||
}
|
||
}
|
||
|
||
// 分支 4:bg_prompt 不包含 userPrompt(旧调用方未加权)→ 末尾追加一次 userPrompt
|
||
func TestEnrichRenderConfigsWithUserPrompt_AppendOnce(t *testing.T) {
|
||
input := []map[string]interface{}{
|
||
{"preset_id": "dream", "bg_prompt": "some existing prompt"},
|
||
}
|
||
got := enrichRenderConfigsWithUserPrompt(input, "赛博朋克")
|
||
bg := extractBgPrompt(t, got, 0)
|
||
if !strings.HasPrefix(bg, "some existing prompt") {
|
||
t.Errorf("expected original prefix preserved, got %q", bg)
|
||
}
|
||
if !strings.HasSuffix(bg, "赛博朋克") && !strings.Contains(bg, "主题强调: 赛博朋克") {
|
||
t.Errorf("expected userPrompt appended with 主题强调 prefix, got %q", bg)
|
||
}
|
||
if strings.Count(bg, "赛博朋克") != 1 {
|
||
t.Errorf("expected exactly 1 occurrence, got %d (bg=%q)", strings.Count(bg, "赛博朋克"), bg)
|
||
}
|
||
}
|
||
|
||
// 分支 5:多个 variant 同时加权,每个 bg_prompt 都要正确处理
|
||
func TestEnrichRenderConfigsWithUserPrompt_MultipleVariants(t *testing.T) {
|
||
input := []map[string]interface{}{
|
||
{"preset_id": "dream", "bg_prompt": ""},
|
||
{"preset_id": "classic", "bg_prompt": "主题(必须遵循,不可偏离): 海报风格"},
|
||
{"preset_id": "ice", "bg_prompt": "old prompt"},
|
||
}
|
||
got := enrichRenderConfigsWithUserPrompt(input, "海报风格")
|
||
var arr []map[string]interface{}
|
||
if err := json.Unmarshal(got, &arr); err != nil {
|
||
t.Fatalf("unmarshal failed: %v", err)
|
||
}
|
||
if len(arr) != 3 {
|
||
t.Fatalf("expected 3 variants, got %d", len(arr))
|
||
}
|
||
// dream: 空 bg → 主题锚点
|
||
if !strings.Contains(arr[0]["bg_prompt"].(string), "主题(必须遵循,不可偏离)") {
|
||
t.Errorf("dream should have 主题 prefix, got %q", arr[0]["bg_prompt"])
|
||
}
|
||
// classic: 已被前端加权 → 不变
|
||
if arr[1]["bg_prompt"].(string) != "主题(必须遵循,不可偏离): 海报风格" {
|
||
t.Errorf("classic should be unchanged, got %q", arr[1]["bg_prompt"])
|
||
}
|
||
// ice: 未加权 → 末尾追加
|
||
if !strings.Contains(arr[2]["bg_prompt"].(string), "主题强调: 海报风格") {
|
||
t.Errorf("ice should have 主题强调 suffix, got %q", arr[2]["bg_prompt"])
|
||
}
|
||
}
|
||
|
||
// extractBgPrompt helper:从序列化的 render_configs JSON 里提取第 i 个 variant 的 bg_prompt
|
||
func extractBgPrompt(t *testing.T, data []byte, idx int) string {
|
||
t.Helper()
|
||
var arr []map[string]interface{}
|
||
if err := json.Unmarshal(data, &arr); err != nil {
|
||
t.Fatalf("unmarshal failed: %v", err)
|
||
}
|
||
if idx >= len(arr) {
|
||
t.Fatalf("idx %d out of range (len=%d)", idx, len(arr))
|
||
}
|
||
bg, _ := arr[idx]["bg_prompt"].(string)
|
||
return bg
|
||
}
|