79 lines
3.3 KiB
Go
79 lines
3.3 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestParseHistoryFilterUsesRecentSevenDaysByDefault(t *testing.T) {
|
|
server := &Server{now: func() time.Time { return time.Date(2026, 7, 16, 8, 0, 0, 0, time.UTC) }}
|
|
filter, err := server.parseHistoryFilter(httptest.NewRequest("GET", "/api/admin/history/tickets", nil), historyTicketStatuses)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if filter.From != "2026-07-10" || filter.To != "2026-07-16" || filter.Page != 1 || filter.PageSize != 20 {
|
|
t.Fatalf("default history filter = %#v", filter)
|
|
}
|
|
}
|
|
|
|
func TestParseHistoryFilterRejectsUnsafeRangeAndPageSize(t *testing.T) {
|
|
server := &Server{now: time.Now}
|
|
for _, path := range []string{
|
|
"/api/admin/history/tickets?from=2026-01-01&to=2026-02-01",
|
|
"/api/admin/history/tickets?from=2026-07-10&to=2026-07-16&page_size=101",
|
|
} {
|
|
if _, err := server.parseHistoryFilter(httptest.NewRequest("GET", path, nil), historyTicketStatuses); err == nil {
|
|
t.Fatalf("expected history filter to be rejected: %s", path)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHistoryTicketViewMasksPersonalDataAndHonorsAnonymization(t *testing.T) {
|
|
server, ticket := encryptedTicketFixture(t)
|
|
row := historyTicketRow{
|
|
ID: ticket.ID, ProjectID: ticket.ProjectID, ProjectName: "云岭漂流", ProjectCode: "RIDE",
|
|
QueueSessionID: "session-a", BusinessDate: time.Date(2026, 7, 16, 0, 0, 0, 0, time.UTC),
|
|
TicketNumber: 42, DisplayNumber: ticket.DisplayNumber, PartySize: ticket.PartySize, Status: ticket.Status,
|
|
JoinedAt: ticket.JoinedAt, PhoneCiphertext: ticket.PhoneCiphertext, PhoneNonce: ticket.PhoneNonce, PhoneHMAC: ticket.PhoneHMAC,
|
|
LastNameCiphertext: ticket.LastNameCiphertext, LastNameNonce: ticket.LastNameNonce, Honorific: ticket.Honorific,
|
|
}
|
|
masked, err := server.historyTicketView(row, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if masked["phone"] != nil || masked["last_name"] != nil || masked["phone_masked"] != "****5678" {
|
|
t.Fatalf("masked history view leaked personal data: %#v", masked)
|
|
}
|
|
revealed, err := server.historyTicketView(row, true)
|
|
if err != nil || revealed["phone"] != "+8613812345678" || revealed["last_name"] != "林" {
|
|
t.Fatalf("revealed history view = %#v, err = %v", revealed, err)
|
|
}
|
|
marked := time.Date(2026, 10, 14, 0, 0, 0, 0, time.UTC)
|
|
row.HistoryAnonymizedAt = &marked
|
|
anonymized, err := server.historyTicketView(row, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if anonymized["personal_data_status"] != "ANONYMIZED" || anonymized["phone"] != nil || anonymized["phone_masked"] != nil {
|
|
t.Fatalf("anonymized history view = %#v", anonymized)
|
|
}
|
|
if anonymized["honorific"] != nil {
|
|
t.Fatalf("anonymized history view leaked honorific: %#v", anonymized)
|
|
}
|
|
}
|
|
|
|
func TestHistorySummaryAlertsUseMinimumSampleAndOperationalThresholds(t *testing.T) {
|
|
longWait := 15.0 * 60
|
|
alerts := historySummaryAlerts([]historyProjectRow{
|
|
{ProjectID: "project-alert", ProjectName: "峡谷漂流", TotalTickets: 10, MissedTickets: 3, CompletedTickets: 6, AverageWait: &longWait},
|
|
{ProjectID: "project-small", ProjectName: "小样本项目", TotalTickets: 4, MissedTickets: 4, CompletedTickets: 0, AverageWait: &longWait},
|
|
})
|
|
if len(alerts) != 1 {
|
|
t.Fatalf("alerts = %#v, want one alert after minimum sample filtering", alerts)
|
|
}
|
|
if alerts[0]["type"] != "MISSED_RATE" || alerts[0]["project_id"] != "project-alert" {
|
|
t.Fatalf("alert = %#v", alerts[0])
|
|
}
|
|
}
|