package webui
import (
"bytes"
"context"
"errors"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/url"
"regexp"
"strings"
"testing"
"time"
"cmroubao/backend-api/internal/usecase"
"github.com/gin-gonic/gin"
)
const testTaskID = "00000000-0000-4000-8000-000000000001"
func TestListTasksRendersRealRowsEscapedWithSecurityHeaders(t *testing.T) {
now := time.Date(2026, 7, 26, 3, 4, 5, 0, time.UTC)
service := &fakeService{
listResult: TaskList{Items: []TaskSummary{{
ID: testTaskID,
Title: ``,
SKU: "SKU-1",
Status: "PENDING",
UpdatedAt: now,
}}},
}
router := newTestRouter(t, service)
response := performRequest(
t,
router,
http.MethodGet,
"/tasks?q=%3Cquery%3E&status=PENDING",
nil,
"",
)
if response.Code != http.StatusOK {
t.Fatalf("status = %d, body = %s", response.Code, response.Body)
}
body := response.Body.String()
if strings.Contains(body, ``) ||
!strings.Contains(body, "<script>") {
t.Fatalf("task title was not safely escaped: %s", body)
}
for _, text := range []string{
"SKU-1",
"待领取",
"/tasks/" + testTaskID,
"value=\"<query>\"",
} {
if !strings.Contains(body, text) {
t.Fatalf("body does not contain %q", text)
}
}
assertSecurityHeaders(t, response)
if service.listInput.Query != "" ||
service.listInput.Status != "PENDING" ||
service.listInput.Limit != defaultListLimit {
t.Fatalf("list input = %+v", service.listInput)
}
}
func TestListTasksRendersHonestEmptyState(t *testing.T) {
router := newTestRouter(t, &fakeService{})
response := performRequest(
t,
router,
http.MethodGet,
"/tasks",
nil,
"",
)
if response.Code != http.StatusOK {
t.Fatalf("status = %d", response.Code)
}
body := response.Body.String()
if !strings.Contains(body, "没有符合条件的任务") ||
!strings.Contains(body, "创建第一条采购任务") {
t.Fatalf("empty state missing: %s", body)
}
for _, fake := range []string{"RB-DEMO", "演示设备", "演示任务"} {
if strings.Contains(body, fake) {
t.Fatalf("empty page contains fake data %q", fake)
}
}
}
func TestNewTaskIssuesReusableStrictCSRFCookie(t *testing.T) {
router := newTestRouter(t, &fakeService{})
response := performRequest(
t,
router,
http.MethodGet,
"/tasks/new",
nil,
"",
)
if response.Code != http.StatusOK {
t.Fatalf("status = %d, body = %s", response.Code, response.Body)
}
cookie := csrfCookie(t, response)
if !cookie.HttpOnly ||
cookie.SameSite != http.SameSiteStrictMode ||
cookie.Path != "/" {
t.Fatalf("CSRF cookie = %+v", cookie)
}
body := response.Body.String()
if !strings.Contains(
body,
`name="csrf_token" value="`+cookie.Value+`"`,
) {
t.Fatal("form CSRF token does not match the cookie")
}
for _, required := range []string{
`name="title"`,
`name="sku"`,
`name="quantity"`,
`name="max_budget"`,
`name="image"`,
`action="/logout"`,
"最高总预算",
} {
if !strings.Contains(body, required) {
t.Fatalf("new task form missing %q", required)
}
}
if strings.Contains(body, "