Files
HomeAgent/internal/tracker/test/tracker_contract_test.go
JianFeeeee aac11f6661 test: 按模块导出接口重写黑盒判据(批次 4:coldcontext / tracker / network)
3 个文件全部在 `<module>/test/`,源码目录仍零 `_test.go`。
共 15 条判据,只调导出接口,输入 → 可观察输出。

## internal/memory/coldcontext —— NewStore/Insert/Get/Query/Consume/Blocks

    TestInsertThenGet                 空 ID 自动补齐;Get 拿回内容;时间戳填好
    TestInsertExplicitIDKeepsIt       显式 ID 不被覆盖
    TestQueryReturnsRelevantDoc       按分词器词形召回相关文档
    TestConsumeIsOneShot              消费过的文档不再返回
    TestBlocksReflectsInsertedDoc     Blocks 输出可调用

**判据写错过一次并修正**:分词器按空白切词,文档里「发票重试」是一个
token,所以「发票 重试 账单」查不到而「发票重试」能查到 —— 实测探针
确认后按真实输入契约写。判据的输入必须能走到被测路径。

## internal/tracker —— NewTracker/Init/PreAction/PostAction/Rollback

    TestFreshTrackerHasNoChanges      新 tracker 无 changeset
    TestNewChangeSetCarriesAction     Action/ID/时间
    TestPostActionWithoutFileChangeIsNoop  无文件变更不凭空造 changeset
    TestRollbackLatestWithoutChangesReportsError
    TestRollbackClearsChangeSets      Rollback 后视图清空

不碰 t.changeSets、不假设 upper/merge 目录结构。

## internal/network —— NewMonitor/CheckOnce/Status/AggregateResult

    TestCheckOnceReachableServer      2xx → Reachable=true + URL/时间
    TestCheckOnceServerError          5xx → Reachable=false
    TestCheckOnceUnreachableEndpoint  连不上 → Error 非空,不 panic
    TestCheckOnceInvalidURL           非法 URL → 失败状态
    TestFreshMonitorStatusIsEmpty     初始状态为空

用 httptest 提供真实 HTTP 输入,不 mock client、不碰 m.status。

## 变异验证(改回旧实现必须红)

    coldcontext Insert 不补 ID       → ★ Insert 没有给空 ID 的文档补 ID
    network     5xx 判可达           → ★ 5xx 端点判可达
    tracker     无变更也造 changeset → ★ 无文件变更却 HasChanges=true

全部还原后复跑全绿。

## 验证

    go build ./...                            PASS
    go vet ./...                              PASS
    gofmt -l                                  干净
    python3 .github/scripts/check-ci-gates.py PASS
    go test ./... -count=1                    17 个测试包 / 100 包无测试 / 0 FAIL
    3 个新包 -race                            全绿
    源码目录 _test.go                         0
2026-11-09 12:56:13 +08:00

79 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package test
// internal/tracker 的黑盒判据:NewTracker/PreAction/PostAction → 可观察输出。
// 不碰 t.changeSets、不读内部目录结构(只用公开方法返回的结果)。
import (
"path/filepath"
"testing"
"github.com/JianFeeeee/HologramAgent/internal/tracker"
)
func newTracker(t *testing.T) *tracker.Tracker {
t.Helper()
dir := t.TempDir()
tr := tracker.NewTracker(filepath.Join(dir, "data"), filepath.Join(dir, "work"))
if err := tr.Init(); err != nil {
t.Fatalf("Init: %v", err)
}
return tr
}
// TestFreshTrackerHasNoChanges:新 tracker 无 changeset。
func TestFreshTrackerHasNoChanges(t *testing.T) {
tr := newTracker(t)
if tr.HasChanges() {
t.Fatal("★ 新 tracker 报告有变更")
}
if got := tr.ChangeSets(); len(got) != 0 {
t.Fatalf("★ ChangeSets() = %v,期望空", got)
}
}
// TestNewChangeSetCarriesAction:构造出的 changeset 带 action/ID/时间。
func TestNewChangeSetCarriesAction(t *testing.T) {
cs := tracker.NewChangeSet("write_file")
if cs.Action != "write_file" {
t.Fatalf("★ Action = %q", cs.Action)
}
if cs.ID == "" || cs.Timestamp.IsZero() {
t.Fatalf("★ ID/时间未填:%+v", cs)
}
}
// TestPostActionWithoutFileChangeIsNoop:没有文件变更时 PostAction
// 不得凭空造出 changeset。
func TestPostActionWithoutFileChangeIsNoop(t *testing.T) {
tr := newTracker(t)
_ = tr.PreAction("write_file")
got := tr.PostAction("write_file")
if len(got.Files) != 0 {
t.Fatalf("★ 无文件变更却记录了 %d 条", len(got.Files))
}
if tr.HasChanges() {
t.Fatal("★ 无文件变更却 HasChanges=true")
}
}
// TestRollbackLatestWithoutChangesReportsError:无 changeset 时定向回滚报错。
func TestRollbackLatestWithoutChangesReportsError(t *testing.T) {
tr := newTracker(t)
if err := tr.RollbackLatest(); err == nil {
t.Fatal("★ 无 changeset 时 RollbackLatest 应报错")
}
}
// TestRollbackClearsChangeSets:Rollback 后视图清空(无需挂载 overlay)。
func TestRollbackClearsChangeSets(t *testing.T) {
tr := newTracker(t)
_ = tr.PreAction("a")
_ = tr.PostAction("a")
if err := tr.Rollback(); err != nil {
t.Fatalf("Rollback: %v", err)
}
if tr.HasChanges() {
t.Fatal("★ Rollback 后仍有变更")
}
}