f382c27200
后台新增「数据概览」(商品/合格/按状态/品牌/分类/待审核) 与「操作日志」(全局审计分页);商品列表支持多选批量改状态/分类。公开首页标题改为「天工」并展示合格档案数;新增公开接口 /api/v1/stats 与后台 /api/stats、/api/audit、/api/products/bulk。合格口径=quality_score≥0.6 且在用。 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
72 lines
2.0 KiB
Go
72 lines
2.0 KiB
Go
package adminstore
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
func TestStatsAndBulk(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
base, err := s.Stats(ctx)
|
|
if err != nil {
|
|
t.Fatalf("stats: %v", err)
|
|
}
|
|
|
|
var p1, p2 string
|
|
if err := s.pool.QueryRow(ctx,
|
|
"INSERT INTO product (name, status) VALUES ($1,'active') RETURNING id",
|
|
"批量测试1 "+randomHex(4)).Scan(&p1); err != nil {
|
|
t.Fatalf("insert p1: %v", err)
|
|
}
|
|
if err := s.pool.QueryRow(ctx,
|
|
"INSERT INTO product (name, status) VALUES ($1,'active') RETURNING id",
|
|
"批量测试2 "+randomHex(4)).Scan(&p2); err != nil {
|
|
t.Fatalf("insert p2: %v", err)
|
|
}
|
|
t.Cleanup(func() { _, _ = s.pool.Exec(ctx, "DELETE FROM product WHERE id = ANY($1)", []string{p1, p2}) })
|
|
|
|
after, err := s.Stats(ctx)
|
|
if err != nil {
|
|
t.Fatalf("stats after: %v", err)
|
|
}
|
|
if after.Products != base.Products+2 {
|
|
t.Fatalf("product count: got %d want %d", after.Products, base.Products+2)
|
|
}
|
|
|
|
// Bulk set status to deprecated.
|
|
n, err := s.BulkSetStatus(ctx, "tester", []string{p1, p2}, "deprecated")
|
|
if err != nil || n != 2 {
|
|
t.Fatalf("bulk status: n=%d err=%v", n, err)
|
|
}
|
|
var deprecated int
|
|
if err := s.pool.QueryRow(ctx,
|
|
"SELECT count(*) FROM product WHERE id = ANY($1) AND status='deprecated'",
|
|
[]string{p1, p2}).Scan(&deprecated); err != nil {
|
|
t.Fatalf("verify: %v", err)
|
|
}
|
|
if deprecated != 2 {
|
|
t.Fatalf("expected 2 deprecated, got %d", deprecated)
|
|
}
|
|
|
|
// Invalid status rejected.
|
|
if _, err := s.BulkSetStatus(ctx, "tester", []string{p1}, "nope"); err != ErrInvalidStatus {
|
|
t.Fatalf("expected ErrInvalidStatus, got %v", err)
|
|
}
|
|
|
|
// Empty selection rejected.
|
|
if _, err := s.BulkSetStatus(ctx, "tester", nil, "active"); err != ErrNoTargets {
|
|
t.Fatalf("expected ErrNoTargets, got %v", err)
|
|
}
|
|
|
|
// Global audit log should contain the bulk_status entry.
|
|
rows, total, err := s.ListAllAudit(ctx, 10, 0)
|
|
if err != nil {
|
|
t.Fatalf("audit: %v", err)
|
|
}
|
|
if total == 0 || len(rows) == 0 {
|
|
t.Fatalf("expected audit rows, got total=%d", total)
|
|
}
|
|
}
|