6a782a07fd
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
75 lines
2.7 KiB
Dart
75 lines
2.7 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:http/testing.dart';
|
|
import 'package:pangolin_vpn/l10n/app_text.dart';
|
|
import 'package:pangolin_vpn/services/api_client.dart';
|
|
import 'package:pangolin_vpn/services/notices_api.dart';
|
|
|
|
ApiClient _c(MockClient m) => ApiClient(baseUrl: 'http://x', getToken: () => 't', refresh: () async => false, client: m);
|
|
|
|
const _utf8Json = {'content-type': 'application/json; charset=utf-8'};
|
|
|
|
void main() {
|
|
test('fetch 解析公告列表 + unread_count(双语字段)', () async {
|
|
final api = NoticesApi(_c(MockClient((req) async {
|
|
expect(req.url.path, '/v1/notices');
|
|
return http.Response(
|
|
'{"notices":['
|
|
'{"id":1,"type":"maintenance","title_zh":"维护通知","title_en":"Maintenance",'
|
|
'"body_zh":"今晚维护","body_en":"Tonight maintenance","link":"https://x/1",'
|
|
'"published_at":"2026-07-10T08:00:00Z","unread":true},'
|
|
'{"id":2,"type":"promo","title_zh":"活动","title_en":"Promo",'
|
|
'"published_at":"2026-07-09T08:00:00Z","unread":false}'
|
|
'],"unread_count":1}',
|
|
200,
|
|
headers: _utf8Json);
|
|
})));
|
|
final data = await api.fetch();
|
|
expect(data.items.length, 2);
|
|
expect(data.unreadCount, 1);
|
|
|
|
final first = data.items[0];
|
|
expect(first.id, 1);
|
|
expect(first.type, 'maintenance');
|
|
expect(first.titleZh, '维护通知');
|
|
expect(first.titleEn, 'Maintenance');
|
|
expect(first.bodyZh, '今晚维护');
|
|
expect(first.bodyEn, 'Tonight maintenance');
|
|
expect(first.link, 'https://x/1');
|
|
expect(first.publishedAt, DateTime.parse('2026-07-10T08:00:00Z'));
|
|
expect(first.unread, true);
|
|
expect(first.title(AppLang.zh), '维护通知');
|
|
expect(first.title(AppLang.en), 'Maintenance');
|
|
|
|
final second = data.items[1];
|
|
expect(second.id, 2);
|
|
expect(second.bodyZh, '');
|
|
expect(second.bodyEn, '');
|
|
expect(second.link, '');
|
|
expect(second.unread, false);
|
|
// 非 zh 语言取 en 兜底
|
|
expect(second.title(AppLang.ja), second.titleEn);
|
|
});
|
|
|
|
test('fetch 缺字段安全默认(不 crash)', () async {
|
|
final api = NoticesApi(_c(MockClient((req) async {
|
|
return http.Response('{}', 200);
|
|
})));
|
|
final data = await api.fetch();
|
|
expect(data.items, isEmpty);
|
|
expect(data.unreadCount, 0);
|
|
});
|
|
|
|
test('markRead 打 POST /v1/notices/read', () async {
|
|
var called = false;
|
|
final api = NoticesApi(_c(MockClient((req) async {
|
|
called = true;
|
|
expect(req.method, 'POST');
|
|
expect(req.url.path, '/v1/notices/read');
|
|
return http.Response('{"ok":true}', 200);
|
|
})));
|
|
await api.markRead();
|
|
expect(called, true);
|
|
});
|
|
}
|