fix(client): 补提交漏掉的 device_usage 模型 + 测试(Windows 构建缺文件)
device_usage.dart 一直是 untracked(stats-overhaul WIP 漏提交),本地有所以 analyze/test 过,但不在 git → bundle → Windows 构建报「找不到 device_usage.dart / DeviceUsage isn't a type」。account_providers/account_api/golden 测试都引用它,补提交。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
// device_usage.dart — 单设备窗口用量(GET /v1/usage/devices?days=N 的一条)。
|
||||||
|
// 统计页「下分设备」明细的数据载体,与账户聚合的 UsagePoint 对应。
|
||||||
|
class DeviceUsage {
|
||||||
|
const DeviceUsage({
|
||||||
|
required this.uuid,
|
||||||
|
required this.name,
|
||||||
|
required this.platform,
|
||||||
|
this.bytesUp = 0,
|
||||||
|
this.bytesDown = 0,
|
||||||
|
this.minutesUsed = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String uuid;
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// ios | android | windows | macos。
|
||||||
|
final String platform;
|
||||||
|
final int bytesUp;
|
||||||
|
final int bytesDown;
|
||||||
|
final int minutesUsed;
|
||||||
|
|
||||||
|
int get bytesTotal => bytesUp + bytesDown;
|
||||||
|
double get gbTotal => bytesTotal / (1024 * 1024 * 1024);
|
||||||
|
|
||||||
|
factory DeviceUsage.fromJson(Map<String, dynamic> m) => DeviceUsage(
|
||||||
|
uuid: m['uuid'] as String? ?? '',
|
||||||
|
name: m['name'] as String? ?? '',
|
||||||
|
platform: m['platform'] as String? ?? '',
|
||||||
|
bytesUp: (m['bytes_up'] as num?)?.toInt() ?? 0,
|
||||||
|
bytesDown: (m['bytes_down'] as num?)?.toInt() ?? 0,
|
||||||
|
minutesUsed: (m['minutes_used'] as num?)?.toInt() ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pangolin_vpn/models/device_usage.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('DeviceUsage.fromJson', () {
|
||||||
|
test('parses a full row and derives totals', () {
|
||||||
|
final d = DeviceUsage.fromJson(const {
|
||||||
|
'uuid': 'dev-a',
|
||||||
|
'name': 'My iPhone',
|
||||||
|
'platform': 'ios',
|
||||||
|
'bytes_up': 1500,
|
||||||
|
'bytes_down': 2500,
|
||||||
|
'minutes_used': 8,
|
||||||
|
});
|
||||||
|
expect(d.uuid, 'dev-a');
|
||||||
|
expect(d.name, 'My iPhone');
|
||||||
|
expect(d.platform, 'ios');
|
||||||
|
expect(d.bytesUp, 1500);
|
||||||
|
expect(d.bytesDown, 2500);
|
||||||
|
expect(d.minutesUsed, 8);
|
||||||
|
expect(d.bytesTotal, 4000);
|
||||||
|
expect(d.gbTotal, 4000 / (1024 * 1024 * 1024));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tolerates missing/absent fields with zero defaults', () {
|
||||||
|
final d = DeviceUsage.fromJson(const {'uuid': 'dev-b', 'name': '', 'platform': ''});
|
||||||
|
expect(d.bytesUp, 0);
|
||||||
|
expect(d.bytesDown, 0);
|
||||||
|
expect(d.minutesUsed, 0);
|
||||||
|
expect(d.bytesTotal, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user