fix(test): 契约测试解耦 stats-overhaul WIP(CI 抓到的真问题)
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
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
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>
This commit is contained in:
@@ -1,17 +1,16 @@
|
||||
// api_contract_test.dart — HTTP API 契约快照(支柱 2:契约单源)。
|
||||
//
|
||||
// 冻结 /v1/me、/v1/usage、/v1/usage/devices 三个响应的「字段面」(snake_case JSON)。
|
||||
// 冻结 /v1/me、/v1/usage 响应的「字段面」(snake_case JSON)。
|
||||
// 字段增删改名会让本测试失败,逼开发者同步后端响应 + 文档,避免控制面与客户端悄悄分叉。
|
||||
//
|
||||
// ⚠️ 改下面的冻结集 = 改 HTTP 契约,必须同步:
|
||||
// - 后端:server/internal/httpapi/account.go(/v1/me)、server/internal/usage/handler.go
|
||||
// (/v1/usage、/v1/usage/devices)
|
||||
// - 客户端:client/lib/models/{me,usage_point,device_usage}.dart
|
||||
// - 后端:server/internal/httpapi/account.go(/v1/me)、server/internal/usage/handler.go(/v1/usage)
|
||||
// - 客户端:client/lib/models/{me,usage_point}.dart
|
||||
// - 文档:各 model 头注 + docs/dev-conventions.html 支柱 2
|
||||
// 注:/v1/usage/devices(DeviceUsage)契约待 stats-overhaul 合并后补(device_usage.dart 当前 WIP、未提交)。
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pangolin_vpn/models/me.dart';
|
||||
import 'package:pangolin_vpn/models/usage_point.dart';
|
||||
import 'package:pangolin_vpn/models/device_usage.dart';
|
||||
|
||||
// ── 契约冻结集(后端 snake_case JSON key)──────────────────────────
|
||||
const _frozenMeKeys = <String>{
|
||||
@@ -31,15 +30,6 @@ const _frozenUsagePointKeys = <String>{
|
||||
'bytes_down',
|
||||
'minutes_used',
|
||||
};
|
||||
const _frozenDeviceUsageKeys = <String>{
|
||||
'uuid',
|
||||
'name',
|
||||
'platform',
|
||||
'bytes_up',
|
||||
'bytes_down',
|
||||
'minutes_used',
|
||||
};
|
||||
|
||||
Map<String, dynamic> _meSample() => {
|
||||
'email': 'me@example.com',
|
||||
'plan': 'pro',
|
||||
@@ -59,15 +49,6 @@ Map<String, dynamic> _usagePointSample() => {
|
||||
'minutes_used': 12,
|
||||
};
|
||||
|
||||
Map<String, dynamic> _deviceUsageSample() => {
|
||||
'uuid': 'dev-uuid-1',
|
||||
'name': 'MacBook',
|
||||
'platform': 'macos',
|
||||
'bytes_up': 3000,
|
||||
'bytes_down': 4000,
|
||||
'minutes_used': 30,
|
||||
};
|
||||
|
||||
void main() {
|
||||
group('/v1/me 契约快照', () {
|
||||
test('字段面冻结(增删字段须同步后端 account.go + 文档)', () {
|
||||
@@ -103,21 +84,4 @@ void main() {
|
||||
expect(p.bytesTotal, 3000);
|
||||
});
|
||||
});
|
||||
|
||||
group('/v1/usage/devices 契约快照(DeviceUsage)', () {
|
||||
test('字段面冻结', () {
|
||||
expect(_deviceUsageSample().keys.toSet(), _frozenDeviceUsageKeys);
|
||||
});
|
||||
|
||||
test('fromJson 正确解析 + 派生', () {
|
||||
final d = DeviceUsage.fromJson(_deviceUsageSample());
|
||||
expect(d.uuid, 'dev-uuid-1');
|
||||
expect(d.name, 'MacBook');
|
||||
expect(d.platform, 'macos');
|
||||
expect(d.bytesUp, 3000);
|
||||
expect(d.bytesDown, 4000);
|
||||
expect(d.minutesUsed, 30);
|
||||
expect(d.bytesTotal, 7000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,14 +57,5 @@ func TestContractUsagePoint(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestContractDeviceUsagePoint(t *testing.T) {
|
||||
assertFrozenKeys(t, "/v1/usage/devices DeviceUsagePoint", jsonTagSet(DeviceUsagePoint{}),
|
||||
"uuid", "name", "platform", "bytes_up", "bytes_down", "minutes_used",
|
||||
)
|
||||
}
|
||||
|
||||
func TestContractDeviceUsageResponse(t *testing.T) {
|
||||
assertFrozenKeys(t, "/v1/usage/devices envelope", jsonTagSet(deviceUsageResponse{}),
|
||||
"devices",
|
||||
)
|
||||
}
|
||||
// 注:/v1/usage/devices(DeviceUsagePoint + deviceUsageResponse)契约待 stats-overhaul
|
||||
// 合并后补——这两个 struct 当前是未提交 WIP,引用它们会让 CI 编译失败。
|
||||
|
||||
Reference in New Issue
Block a user