Files
wangjia eb1bd11439
ci-pangolin / Lint — shellcheck (pull_request) Failing after 9s
ci-pangolin / OpenAPI Sync Check (pull_request) Successful in 18s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 6s
ci-pangolin / Flutter — analyze + test (pull_request) Successful in 39s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 4s
ci-pangolin / Go — build + test (pull_request) Successful in 2m2s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 2m14s
ci-pangolin / Golden — 视觉回归 (components + auth) (pull_request) Successful in 31s
fix(test): 契约测试解耦 stats-overhaul WIP(CI 抓到的真问题)
CI 暴露:我的契约测试引用了未提交的 stats-overhaul 文件,本地能过(文件在工作区)、
CI checkout 已提交代码就崩:
- api_contract_test.dart import device_usage.dart(?? 未提交)→ flutter analyze 报
  'Target of URI doesn't exist'
- usage/contract_test.go 引用 DeviceUsagePoint/deviceUsageResponse(HEAD service.go/
  handler.go 均无)→ go-server 编译失败
摘掉 /v1/usage/devices(DeviceUsage)契约,保留 /v1/me + /v1/usage(已提交)。
待 stats-overhaul 合并后再补 DeviceUsage 契约。
验证:flutter analyze test/contract 'No issues';go vet usage 无错。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 18:13:27 +08:00

62 lines
1.7 KiB
Go

package usage
// contract_test.go — HTTP 契约快照(支柱 2:契约单源)。
//
// 冻结 /v1/usage(UsagePoint)与 /v1/usage/devices(deviceUsageResponse + DeviceUsagePoint)
// 的字段面。改字段会让本测试失败,逼同步客户端 model + 文档。
//
// ⚠️ 与客户端冻结集对账:client/test/contract/api_contract_test.dart 的
// _frozenUsagePointKeys / _frozenDeviceUsageKeys。两端必须一致。
import (
"reflect"
"strings"
"testing"
)
func jsonTagSet(v any) map[string]struct{} {
t := reflect.TypeOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
set := map[string]struct{}{}
for i := 0; i < t.NumField(); i++ {
tag := t.Field(i).Tag.Get("json")
if tag == "" || tag == "-" {
continue
}
name := strings.SplitN(tag, ",", 2)[0]
if name != "" {
set[name] = struct{}{}
}
}
return set
}
func assertFrozenKeys(t *testing.T, name string, got map[string]struct{}, want ...string) {
t.Helper()
wantSet := make(map[string]struct{}, len(want))
for _, k := range want {
wantSet[k] = struct{}{}
}
for k := range got {
if _, ok := wantSet[k]; !ok {
t.Errorf("%s: 出现未冻结字段 %q —— 改契约需同步另一端(客户端 model)+ 文档", name, k)
}
}
for _, k := range want {
if _, ok := got[k]; !ok {
t.Errorf("%s: 缺失冻结字段 %q", name, k)
}
}
}
func TestContractUsagePoint(t *testing.T) {
assertFrozenKeys(t, "/v1/usage UsagePoint", jsonTagSet(UsagePoint{}),
"date", "bytes_up", "bytes_down", "minutes_used",
)
}
// 注:/v1/usage/devices(DeviceUsagePoint + deviceUsageResponse)契约待 stats-overhaul
// 合并后补——这两个 struct 当前是未提交 WIP,引用它们会让 CI 编译失败。