Files
root 44406b72db Initial commit: img-infinite-canvas AI design workbench MVP
Moteva-style AI design workbench replica. Home prompt creates a project;
the project page provides an infinite canvas with node drag, zoom/pan,
a design chat panel, history replay, image asset upload, and project
save/regenerate.

- Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory
  or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage;
  sky-valley/pi agent runtime adapter.
- Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n,
  shadcn/ui components, auth (OTP/Turnstile/Google/WeChat).
- Deploy: Docker Compose and k3s manifests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 23:15:37 +08:00

117 lines
3.9 KiB
Go

package application
import (
"testing"
"time"
"img_infinite_canvas/internal/domain/design"
)
func TestGeneratorTaskStatusWaitsForActiveCanvasWork(t *testing.T) {
threadID := "thread-image"
project := design.Project{
ID: "project-image",
LastThreadID: threadID,
Status: design.StatusExploring,
UpdatedAt: time.Now(),
Nodes: []design.Node{
{
ID: "image-1",
Type: design.NodeTypeImage,
Title: "Done image",
Content: "http://localhost:19000/canvas-assets/done.png",
Status: "success",
},
{
ID: "image-2",
Type: design.NodeTypeImage,
Title: "Pending image",
Content: "pending prompt",
Status: "generating",
},
},
Messages: []design.Message{
{ID: "user", Role: "user", Type: "user", Content: "make two images", ThreadID: threadID},
{ID: "planner", Role: "assistant", Type: "assistant", Status: "success", ThreadID: threadID, Name: "agent_planner"},
{ID: "tool", Role: "tool", Type: "tool", Title: "GPT Image 2", Status: "running", ThreadID: threadID},
{
ID: "artifact-1",
Role: "tool",
Type: "tool_use",
Title: "generate_media",
Name: "generate_media",
ToolHint: "generate_image_gpt_image_2",
Status: "success",
ThreadID: threadID,
CreatedAt: time.Now(),
Content: `[{"event_data":{"artifact":[{"artifact_id":"image-1","artifact_type":"image_info","format":"png","height":1024,"image_name":"Done image","image_url":"http://localhost:19000/canvas-assets/done.png","prompt_text":"make two images","width":1024}]}}]`,
},
},
}
task := generatorTaskFromProject(project, threadID)
if task.Status != "submitted" {
t.Fatalf("expected active canvas work to keep task submitted, got %s", task.Status)
}
if len(task.Artifacts) != 1 {
t.Fatalf("expected partial artifact to remain visible, got %#v", task.Artifacts)
}
}
func TestGeneratorTaskStatusDoesNotCompleteExploringPlannerOnlyThread(t *testing.T) {
threadID := "thread-planning"
project := design.Project{
ID: "project-planning",
LastThreadID: threadID,
Status: design.StatusExploring,
Messages: []design.Message{
{ID: "user", Role: "user", Type: "user", Content: "make images", ThreadID: threadID},
{ID: "planner", Role: "assistant", Type: "assistant", Status: "success", ThreadID: threadID, Name: "agent_planner"},
},
}
task := generatorTaskFromProject(project, threadID)
if task.Status != "submitted" {
t.Fatalf("expected exploring planner-only thread to stay submitted, got %s", task.Status)
}
}
func TestGeneratorTaskStatusCompletesWhenCanvasSettles(t *testing.T) {
threadID := "thread-image"
project := design.Project{
ID: "project-image",
LastThreadID: threadID,
Status: design.StatusReady,
UpdatedAt: time.Now(),
Nodes: []design.Node{
{
ID: "image-1",
Type: design.NodeTypeImage,
Title: "Done image",
Content: "http://localhost:19000/canvas-assets/done.png",
Status: "success",
},
},
Messages: []design.Message{
{ID: "user", Role: "user", Type: "user", Content: "make one image", ThreadID: threadID},
{ID: "tool", Role: "tool", Type: "tool", Title: "GPT Image 2", Status: "success", ThreadID: threadID},
{
ID: "artifact-1",
Role: "tool",
Type: "tool_use",
Title: "generate_media",
Name: "generate_media",
Status: "success",
ThreadID: threadID,
Content: `[{"event_data":{"artifact":[{"artifact_id":"image-1","artifact_type":"image_info","format":"png","height":1024,"image_name":"Done image","image_url":"http://localhost:19000/canvas-assets/done.png","prompt_text":"make one image","width":1024}]}}]`,
},
{ID: "assistant", Role: "assistant", Type: "assistant", Status: "success", ThreadID: threadID},
},
}
task := generatorTaskFromProject(project, threadID)
if task.Status != "completed" {
t.Fatalf("expected settled canvas task to complete, got %s", task.Status)
}
}