144 lines
5.8 KiB
Go
144 lines
5.8 KiB
Go
package app
|
||
|
||
import (
|
||
"encoding/json"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestCloudAgentMediaReferencePrompt(t *testing.T) {
|
||
ref := func(name string) map[string]any {
|
||
return map[string]any{"name": name, "storageKey": "resource:test"}
|
||
}
|
||
refs := map[string]any{
|
||
"referenceImages": []any{ref("人物"), ref("假发")},
|
||
"referenceAudios": []any{ref("声音")},
|
||
"referenceVideos": []any{ref("运镜")},
|
||
}
|
||
prompt := "人物严格参考 @图片1,保持一镜到底。"
|
||
got, err := cloudAgentMediaReferencePrompt(prompt, refs)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
want := prompt + "\n\n【资产参考】\n假发:@图片2\n运镜:@视频1\n声音:@音频1"
|
||
if got != want {
|
||
t.Fatalf("prompt = %q, want %q", got, want)
|
||
}
|
||
if again, err := cloudAgentMediaReferencePrompt(got, refs); err != nil || again != got {
|
||
t.Fatalf("approval revalidation duplicated mentions: %q, %v", again, err)
|
||
}
|
||
for _, prompt := range []string{"参考 @图片3", "参考 @图片10", "参考 @音频0"} {
|
||
if _, err := cloudAgentMediaReferencePrompt(prompt, refs); err == nil {
|
||
t.Fatalf("unbound mention accepted: %q", prompt)
|
||
}
|
||
}
|
||
if got, err := cloudAgentMediaReferencePrompt("纯文字生成", nil); err != nil || got != "纯文字生成" {
|
||
t.Fatalf("text-only prompt changed: %q, %v", got, err)
|
||
}
|
||
if _, err := cloudAgentMediaReferencePrompt(strings.Repeat("长", 16000), refs); err == nil {
|
||
t.Fatal("expanded prompt must respect the length limit")
|
||
}
|
||
transient := map[string]any{"referenceImages": []any{ref("画布图片"), map[string]any{"name": "标注", "dataUrl": "data:image/png;base64,AA=="}}}
|
||
if got, err := cloudAgentMediaReferencePrompt("生成", transient); err != nil || got != "生成\n\n【资产参考】\n画布图片:@图片1" {
|
||
t.Fatalf("transient image must not create an unresolvable canvas chip: %q, %v", got, err)
|
||
}
|
||
if got, err := cloudAgentMediaReferencePrompt("生成", map[string]any{"referenceImages": []any{ref("标题\n@图片99")}}); err != nil || !strings.Contains(got, "标题 @图片99:@图片1") {
|
||
t.Fatalf("title introduced a false mention: %q, %v", got, err)
|
||
}
|
||
}
|
||
|
||
func TestCloudAgentMediaComposerPromptSeparatesCharacterAndImageSlots(t *testing.T) {
|
||
refs := map[string]any{
|
||
"referenceImages": []any{
|
||
map[string]any{"storageKey": "resource:character", "canvasReferenceKind": "character"},
|
||
map[string]any{"storageKey": "resource:scene"},
|
||
map[string]any{"storageKey": "resource:character-two", "canvasReferenceKind": "character"},
|
||
},
|
||
"referenceAudios": []any{map[string]any{"storageKey": "resource:voice"}},
|
||
}
|
||
prompt := "@图片1、@图片2、@图片3 和 @音频1"
|
||
want := "@角色1、@图片1、@角色2 和 @音频1"
|
||
if got := cloudAgentMediaComposerPrompt(prompt, refs); got != want {
|
||
t.Fatalf("composer prompt = %q, want %q", got, want)
|
||
}
|
||
if prompt != "@图片1、@图片2、@图片3 和 @音频1" {
|
||
t.Fatal("provider prompt was mutated")
|
||
}
|
||
if got := cloudAgentMediaComposerPrompt("参考 @图片10", refs); got != "参考 @图片10" {
|
||
t.Fatalf("unbound reference changed: %q", got)
|
||
}
|
||
}
|
||
|
||
func TestCloudAgentMediaMentionsPersistAcrossApproval(t *testing.T) {
|
||
s, _, args := agentMediaFixture(t)
|
||
args.Prompt = "人物参考 @图片1,使用另一张图的假发。"
|
||
run, _ := agentMediaRun(t, s, args, "request_approval")
|
||
if err := s.advanceCloudAgentByID("user", run.ID); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
waiting, err := s.CloudAgentRun("user", run.ID)
|
||
if err != nil || waiting.Approval == nil {
|
||
t.Fatalf("missing approval: %v", err)
|
||
}
|
||
want := args.Prompt + "\n\n【资产参考】\n叮当猫在飞:@图片2"
|
||
checkNode := func() {
|
||
t.Helper()
|
||
canvas, err := s.repo.CanvasProjectForUser("user", "agent-canvas")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
doc, _ := creationDocument(canvas.PayloadJSON)
|
||
nodes, _ := creationObjects(doc["nodes"])
|
||
meta := nodes[args.NodeID]["metadata"].(map[string]any)
|
||
if meta["composerContent"] != want || meta["prompt"] != want {
|
||
t.Fatalf("canvas lost reference mentions: %#v", meta)
|
||
}
|
||
refs := []string{}
|
||
for _, edge := range creationMaps(doc["connections"]) {
|
||
if edge["toNodeId"] == args.NodeID {
|
||
refs = append(refs, stringValue(edge["fromNodeId"]))
|
||
}
|
||
}
|
||
if strings.Join(refs, ",") != "hero,cat,shot-1" {
|
||
t.Fatalf("canvas chip order does not match media arrays: %v", refs)
|
||
}
|
||
}
|
||
var approved cloudAgentMediaArgs
|
||
stored, _ := s.repo.CloudAgent("user", run.ID)
|
||
state, _ := cloudAgentDecode(stored)
|
||
if err := json.Unmarshal([]byte(state.Approval.Call.Function.Arguments), &approved); err != nil || approved.Prompt != want {
|
||
t.Fatalf("approval prompt differs from draft: %q, %v", approved.Prompt, err)
|
||
}
|
||
checkNode()
|
||
approveAgentMediaDraft(t, s, run.ID)
|
||
checkNode()
|
||
stored, _ = s.repo.CloudAgent("user", run.ID)
|
||
state, _ = cloudAgentDecode(stored)
|
||
task, err := s.repo.TaskForUser("user", state.MediaTaskID)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var input canvasGenerationInput
|
||
if err := json.Unmarshal([]byte(task.InputJSON), &input); err != nil || input.Prompt != want || task.Prompt != want {
|
||
t.Fatalf("submitted prompt differs from approved mentions: %#v, %v", input, err)
|
||
}
|
||
}
|
||
|
||
func TestCloudAgentMediaReferenceEdgesFollowApprovedOrder(t *testing.T) {
|
||
edges := []map[string]any{
|
||
{"id": "cat-edge", "fromNodeId": "cat", "toNodeId": "target"},
|
||
{"id": "text-edge", "fromNodeId": "text", "toNodeId": "target"},
|
||
{"id": "other-edge", "fromNodeId": "cat", "toNodeId": "other"},
|
||
{"id": "hero-edge", "fromNodeId": "hero", "toNodeId": "target"},
|
||
{"id": "removed-edge", "fromNodeId": "removed", "toNodeId": "target"},
|
||
}
|
||
got := cloudAgentMediaConnections(edges, cloudAgentMediaArgs{NodeID: "target", ReferenceNodeIDs: []string{"hero", "cat"}, SourceNodeID: "text"})
|
||
ids := []string{}
|
||
for _, edge := range got {
|
||
ids = append(ids, stringValue(edge["id"]))
|
||
}
|
||
if strings.Join(ids, ",") != "other-edge,hero-edge,cat-edge,text-edge" {
|
||
t.Fatalf("wrong reference order or unrelated edge changed: %v", ids)
|
||
}
|
||
}
|