Files
ARTEX/db/findings_test.go
T
Autumn-27 64d9c66f6a feat: 新增发现详情页(多 Tab + 状态区可改严重度/处置状态)
详情页 /function/findings/detail?id=<finding_id>:
- 概览 Tab:左栏摘要 + 证据/PoC,右栏状态区(严重等级、漏洞类型、涉及资产、处理状态、所属任务、发现时间)
- 严重等级与处理状态用下拉,选择后直接调后端接口即时修正
- 另有「证据/PoC」「元数据」两个 Tab
- 发现列表页漏洞名称改为链接进入详情

后端:
- GET  /api/exploration/findings/{id}  取单条(含资产解析)
- PATCH /api/exploration/findings/{id} 扩展为部分更新 {status?, severity?}
- db.GetFinding / db.SetFindingSeverity(同步探索节点 payload,保持任务内视图一致)
- db.ValidSeverity 白名单;api 增加 getFinding / setFindingSeverity
- mock 支持单条 GET / PATCH,并回填 finding_id 供 demo 链接

db 测试补充 GetFinding / SetFindingSeverity 往返断言;next build 通过。
2026-08-18 11:43:50 -04:00

128 lines
3.9 KiB
Go

package db
import (
"slices"
"testing"
)
// TestFindingsPageAndStats exercises ListFindingsPage (filter/sort/paging) and
// FindingStats against the live dev PG. It tags its rows with a unique vulnclass
// so assertions are isolated from any pre-existing data, and cleans up after.
func TestFindingsPageAndStats(t *testing.T) {
d, err := Open(testDSN(t))
if err != nil {
t.Skipf("postgres unavailable (%v) — skipping", err)
}
defer d.Close()
const vc = "__test_vc_pagination__"
// clean any leftovers from a prior aborted run, and clean up on exit
cleanup := func() { _, _ = d.Exec(`DELETE FROM findings WHERE vulnclass=$1`, vc) }
cleanup()
defer cleanup()
// Seed 6 findings under the marker vulnclass: 1 critical, 3 high, 2 low; 2 pending.
// The critical row carries a name to verify round-trip.
seed := []struct {
sev, status, name string
}{
{"critical", "pending", "严重漏洞标题"},
{"high", "resolved", ""},
{"high", "resolved", ""},
{"high", "pending", ""},
{"low", "resolved", ""},
{"low", "resolved", ""},
}
var ids []int64
for i, s := range seed {
id, err := d.AddFinding(0, 0, vc, s.name, s.sev, "summary", "poc", "tester", nil)
if err != nil {
t.Fatalf("AddFinding[%d]: %v", i, err)
}
if _, err := d.SetFindingStatus(id, s.status); err != nil {
t.Fatalf("SetFindingStatus[%d]: %v", i, err)
}
ids = append(ids, id)
}
// Filter by our vulnclass → exactly the 6 seeded rows, paged 2 per page.
p1, total, err := d.ListFindingsPage(FindingFilter{VulnClass: vc, Sort: "severity"}, 1, 2)
if err != nil {
t.Fatal(err)
}
if total != 6 {
t.Fatalf("total: want 6, got %d", total)
}
if len(p1) != 2 {
t.Fatalf("page1 size: want 2, got %d", len(p1))
}
// severity sort → critical first (with its name round-tripped), then high.
if p1[0].Severity != "critical" {
t.Fatalf("severity sort: want critical first, got %q", p1[0].Severity)
}
if p1[0].Name != "严重漏洞标题" {
t.Fatalf("name round-trip: want 严重漏洞标题, got %q", p1[0].Name)
}
if p1[1].Severity != "high" {
t.Fatalf("severity sort: want high second, got %q", p1[1].Severity)
}
// Combined filter: vulnclass + status=pending → 2 rows.
pend, total, err := d.ListFindingsPage(FindingFilter{VulnClass: vc, Status: FindingPending}, 1, 50)
if err != nil {
t.Fatal(err)
}
if total != 2 || len(pend) != 2 {
t.Fatalf("pending filter: want 2/2, got %d/%d", total, len(pend))
}
// Combined filter: vulnclass + severity=high → 3 rows.
_, total, err = d.ListFindingsPage(FindingFilter{VulnClass: vc, Severity: "high"}, 1, 50)
if err != nil {
t.Fatal(err)
}
if total != 3 {
t.Fatalf("high filter: want 3, got %d", total)
}
// Stats: whole-table, so assert our contribution is reflected (>=) and the
// marker vulnclass is present.
st, err := d.FindingStats()
if err != nil {
t.Fatal(err)
}
if st.Critical < 1 {
t.Fatalf("stats critical undercount: %+v", st)
}
if st.Total < 6 || st.High < 3 || st.Low < 2 || st.Pending < 2 {
t.Fatalf("stats undercount: %+v", st)
}
if !slices.Contains(st.VulnClasses, vc) {
t.Fatalf("stats vulnclasses missing %q", vc)
}
// GetFinding: single-row fetch round-trips id/name/severity.
one, err := d.GetFinding(ids[0])
if err != nil {
t.Fatal(err)
}
if one == nil || one.ID != ids[0] || one.Severity != "critical" || one.Name != "严重漏洞标题" {
t.Fatalf("GetFinding mismatch: %+v", one)
}
if miss, err := d.GetFinding(-1); err != nil || miss != nil {
t.Fatalf("GetFinding(-1): want nil,nil got %+v,%v", miss, err)
}
// SetFindingSeverity: standalone row updates; 0 rows for unknown id.
if n, err := d.SetFindingSeverity(ids[0], "high"); err != nil || n != 1 {
t.Fatalf("SetFindingSeverity: want 1,nil got %d,%v", n, err)
}
one, _ = d.GetFinding(ids[0])
if one.Severity != "high" {
t.Fatalf("severity not updated: %q", one.Severity)
}
if n, err := d.SetFindingSeverity(-1, "low"); err != nil || n != 0 {
t.Fatalf("SetFindingSeverity(-1): want 0,nil got %d,%v", n, err)
}
}