- Entry 增加 DeviceVisits:同一 URL 多次/多设备上报取各设备最大值再求和,
不再平方级放大(30 次访问曾被计成 465 次)
- handleGet 不再截断 100 条,返回 {items,total},后台显示当前条数
- 清理死代码(hostVisits/hostOf/visitCount/isCodeHostVal/dashySection)
- 守门测试:重复上报、多设备求和、陈旧数据、持久化、统计、门户文案、第 101 条可见性
78 lines
2.7 KiB
Go
78 lines
2.7 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"path/filepath"
|
||
"testing"
|
||
)
|
||
|
||
// 复现「访问次数被平方级放大」:扩展每 30s 上报一次该 URL 的
|
||
// chrome.history.search visitCount —— 那是**绝对累计值**(1,2,3,4,5…),
|
||
// 而 handleSync 用 `e.TotalVisits += item.VisitCount` 把它当增量累加。
|
||
// 结果 5 次真实访问被记成 15 次。
|
||
func TestSyncDoubleCountsAbsoluteVisitCount(t *testing.T) {
|
||
dir := t.TempDir()
|
||
oldDir, oldFile, oldTok := dataDir, historyFile, syncToken
|
||
dataDir = dir
|
||
historyFile = filepath.Join(dir, "history.json")
|
||
syncToken = "t"
|
||
defer func() { dataDir, historyFile, syncToken = oldDir, oldFile, oldTok }()
|
||
|
||
history = map[string]*Entry{}
|
||
|
||
// 模拟 5 轮同步:同一 URL 的绝对 visitCount 依次为 1..5
|
||
for round, vc := range []int{1, 2, 3, 4, 5} {
|
||
body, _ := json.Marshal(SyncRequest{Items: []SyncItem{{
|
||
URL: "https://ex.com/a", Title: "A", Device: "desktop",
|
||
Timestamp: float64(1700000000 + round*30), VisitCount: vc,
|
||
}}})
|
||
req := httptest.NewRequest(http.MethodPost, "/sync", bytes.NewReader(body))
|
||
req.Header.Set("Authorization", "Bearer t")
|
||
w := httptest.NewRecorder()
|
||
handleSync(w, req)
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("round %d: status = %d", round, w.Code)
|
||
}
|
||
}
|
||
|
||
e := history["https://ex.com/a"]
|
||
if e == nil {
|
||
t.Fatal("entry not created")
|
||
}
|
||
t.Logf("真实访问 5 次 → 落盘 TotalVisits = %d", e.TotalVisits)
|
||
if e.TotalVisits != 5 {
|
||
t.Errorf("TotalVisits = %d; 真实只访问 5 次,被平方级放大(期望 5)", e.TotalVisits)
|
||
}
|
||
}
|
||
|
||
// 上报 100 次同一 URL 会放大到什么程度 —— 量化后果。
|
||
func TestSyncInflationScalesQuadratically(t *testing.T) {
|
||
dir := t.TempDir()
|
||
oldDir, oldFile, oldTok := dataDir, historyFile, syncToken
|
||
dataDir = dir
|
||
historyFile = filepath.Join(dir, "history.json")
|
||
syncToken = "t"
|
||
defer func() { dataDir, historyFile, syncToken = oldDir, oldFile, oldTok }()
|
||
|
||
history = map[string]*Entry{}
|
||
// 真实访问 30 次,但扩展每 30s 同步一次 → 上报 30 次绝对值
|
||
for round := 1; round <= 30; round++ {
|
||
body, _ := json.Marshal(SyncRequest{Items: []SyncItem{{
|
||
URL: "https://ex.com/b", Device: "desktop",
|
||
Timestamp: float64(1700000000 + round*30), VisitCount: round,
|
||
}}})
|
||
req := httptest.NewRequest(http.MethodPost, "/sync", bytes.NewReader(body))
|
||
req.Header.Set("Authorization", "Bearer t")
|
||
handleSync(httptest.NewRecorder(), req)
|
||
}
|
||
got := history["https://ex.com/b"].TotalVisits
|
||
want := 30
|
||
t.Logf("真实 30 次访问 → 落盘 %d 次(虚高 %.1f×)", got, float64(got)/float64(want))
|
||
if got != want {
|
||
t.Errorf("TotalVisits = %d; want %d", got, want)
|
||
}
|
||
}
|