Files
HomeAgent/internal/network/test/monitor_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

81 lines
2.4 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/network 的黑盒判据:NewMonitor → CheckOnce → EndpointStatus。
// 用 httptest 提供真实输入,不断言内部结构。
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/JianFeeeee/HologramAgent/internal/network"
)
func newMonitor() *network.Monitor { return network.NewMonitor(time.Second) }
// TestCheckOnceReachableServer:2xx 端点判可达,且带回 URL/延迟/时间。
func TestCheckOnceReachableServer(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
st := newMonitor().CheckOnce(context.Background(), srv.URL)
if !st.Reachable {
t.Fatalf("★ 2xx 端点判不可达:%+v", st)
}
if st.URL != srv.URL {
t.Fatalf("★ URL = %q,期望 %q", st.URL, srv.URL)
}
if st.LastCheck.IsZero() {
t.Fatal("★ LastCheck 未填")
}
}
// TestCheckOnceServerError:5xx 判不可达。
func TestCheckOnceServerError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
st := newMonitor().CheckOnce(context.Background(), srv.URL)
if st.Reachable {
t.Fatalf("★ 5xx 端点判可达:%+v", st)
}
}
// TestCheckOnceUnreachableEndpoint:连不上的端点必须带 Error,不 panic。
func TestCheckOnceUnreachableEndpoint(t *testing.T) {
// 127.0.0.1:1 上没有服务,连接会立刻失败
st := newMonitor().CheckOnce(context.Background(), "http://127.0.0.1:1/health")
if st.Reachable {
t.Fatal("★ 连不上的端点判可达")
}
if st.Error == "" {
t.Fatal("★ 失败时没给出 Error")
}
if st.URL != "http://127.0.0.1:1/health" {
t.Fatalf("★ URL = %q", st.URL)
}
}
// TestCheckOnceInvalidURL:非法 URL 必须报错而不是 panic。
func TestCheckOnceInvalidURL(t *testing.T) {
st := newMonitor().CheckOnce(context.Background(), "://not-a-url")
if st.Reachable || st.Error == "" {
t.Fatalf("★ 非法 URL 没有得到失败状态:%+v", st)
}
}
// TestFreshMonitorStatusIsEmpty:未跑过 Start 时状态为空、聚合结果可用。
func TestFreshMonitorStatusIsEmpty(t *testing.T) {
m := newMonitor()
if got := m.Status(); len(got) != 0 {
t.Fatalf("★ 新 Monitor 的 Status = %v,期望空", got)
}
_ = m.AggregateResult() // 输出必须可调用
}