package comicanimator import ( "bytes" "html/template" "net/http/httptest" "net/url" "os" "path/filepath" "strings" "testing" "time" ) func TestSignedURLTampering(t *testing.T) { dir := t.TempDir() tool := &Tool{cfg: Config{PublicBaseURL: "https://example.test", SigningSecret: string(make([]byte, 32)), SignedURLTTL: time.Minute}, store: newStore()} u := Upload{ID: "upl_test", Path: filepath.Join(dir, "x.png")} os.WriteFile(u.Path, []byte("x"), 0600) tool.store.putUpload(u) raw := tool.signedURL(u.ID) parsed, err := url.Parse(raw) if err != nil { t.Fatal(err) } if parsed.Query().Get("signature") == "" || parsed.Query().Get("expires") == "" { t.Fatal("missing signature fields") } if !stringsContains(parsed.Path, u.ID) { t.Fatal("missing upload id") } } func stringsContains(s, part string) bool { return len(s) >= len(part) && s[len(s)-len(part):] == part } func TestValidateWebP(t *testing.T) { good := append([]byte("RIFF\x10\x00\x00\x00WEBPVP8X"), make([]byte, 4)...) if err := validateWebP(good); err != nil { t.Fatal(err) } if err := validateWebP([]byte("not webp")); err == nil { t.Fatal("corrupt webp accepted") } } func TestTemplatesExecute(t *testing.T) { if err := template.Must(template.New("tool").Parse(toolPage)).Execute(&bytes.Buffer{}, map[string]any{"CSRF": "x", "Duration": 6}); err != nil { t.Fatal(err) } if err := template.Must(template.New("instructor").Parse(instructorPage)).Execute(&bytes.Buffer{}, map[string]any{"Outputs": []outputFile{{Name: "safe.mp4", Size: 10, Modified: time.Now()}}}); err != nil { t.Fatal(err) } } func TestSystemPromptReloadsFromFile(t *testing.T) { path := filepath.Join(t.TempDir(), "system-prompt.txt") if err := os.WriteFile(path, []byte("first prompt\n"), 0600); err != nil { t.Fatal(err) } tool := &Tool{cfg: Config{PromptFile: path}} prompt, err := tool.systemPrompt() if err != nil || prompt != "first prompt" { t.Fatalf("first load = %q, %v", prompt, err) } if err := os.WriteFile(path, []byte("second prompt\n"), 0600); err != nil { t.Fatal(err) } prompt, err = tool.systemPrompt() if err != nil || prompt != "second prompt" { t.Fatalf("reloaded prompt = %q, %v", prompt, err) } } func TestSystemPromptRejectsEmptyFile(t *testing.T) { path := filepath.Join(t.TempDir(), "system-prompt.txt") if err := os.WriteFile(path, []byte(" \n"), 0600); err != nil { t.Fatal(err) } if _, err := (&Tool{cfg: Config{PromptFile: path}}).systemPrompt(); err == nil { t.Fatal("empty system prompt accepted") } } func TestStudentCompletedCardUsesGenerationMediaRoutes(t *testing.T) { tool := &Tool{} response := httptest.NewRecorder() tool.renderCard(response, Generation{ID: "gen_test", Status: Completed}, false) body := response.Body.String() for _, route := range []string{`src="generations/gen_test/video"`, `href="generations/gen_test/download"`} { if !strings.Contains(body, route) { t.Errorf("student generation card missing %q", route) } } } func TestVideoSignatures(t *testing.T) { mp4 := append([]byte{0, 0, 0, 16}, []byte("ftypisom")...) if !validVideoHeader("video/mp4", mp4) { t.Fatal("valid MP4 rejected") } if validVideoHeader("video/mp4", []byte("not a video")) { t.Fatal("invalid MP4 accepted") } if !validVideoHeader("video/webm", []byte{0x1a, 0x45, 0xdf, 0xa3}) { t.Fatal("valid WebM rejected") } }