feat: M1.1 完成 + M1.2 启动 — 全量更新
M1.1 收尾: - 24项 P0/P1/P2 bug 修复 (Rust 107 tests + Flutter 15 tests) - Flutter App v0.3: cupertino_icons 修复, 单元测试, 调试面板, APK 52.6MB - 示例插件完善: manifest.json + 请求/响应示范 + 7个测试 - API 文档重写 (以 routes.rs 为唯一权威) - MILESTONES.md 更新至 100% M1.2 启动: - P0: 插件管理 API 闭环 (handle_manager_message Custom 分支 + broadcast_plugin_states) - ServiceManager 集成测试 8/8 (tests/m1_2_service_manager.rs) - M1.2 测试计划 (docs/M1.2_TEST_PLAN.md, 18个E2E场景) - 动态插件系统: auto_rollback + version_manager GC + 路径穿越防护 总计: Rust 115/115 测试, Flutter 15/15 测试, 零 warning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
183
clients/flutter/test/models/models_test.dart
Normal file
183
clients/flutter/test/models/models_test.dart
Normal file
@@ -0,0 +1,183 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:showen_v2_flutter/models/api_response.dart';
|
||||
import 'package:showen_v2_flutter/models/app_event.dart';
|
||||
import 'package:showen_v2_flutter/models/ble_models.dart';
|
||||
import 'package:showen_v2_flutter/models/ble_status.dart';
|
||||
import 'package:showen_v2_flutter/models/device_status.dart';
|
||||
import 'package:showen_v2_flutter/models/player_status.dart';
|
||||
import 'package:showen_v2_flutter/models/video_item.dart';
|
||||
import 'package:showen_v2_flutter/models/wifi_network.dart';
|
||||
import 'package:showen_v2_flutter/models/wifi_status.dart';
|
||||
|
||||
void main() {
|
||||
group('ApiResponse', () {
|
||||
test('fromJson and toJson round trip', () {
|
||||
final response = ApiResponse.fromJson(const <String, dynamic>{
|
||||
'status': 'ok',
|
||||
'message': 'done',
|
||||
});
|
||||
|
||||
expect(response.isOk, isTrue);
|
||||
expect(response.toJson(), <String, dynamic>{
|
||||
'status': 'ok',
|
||||
'message': 'done',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('AppEvent', () {
|
||||
test('fromJson prefers data payload map', () {
|
||||
final event = AppEvent.fromJson(const <String, dynamic>{
|
||||
'type': 'status',
|
||||
'data': <String, dynamic>{'connected': true},
|
||||
});
|
||||
|
||||
expect(event.type, 'status');
|
||||
expect(event.payload, <String, dynamic>{'connected': true});
|
||||
});
|
||||
|
||||
test('fromJson normalizes scalar payload', () {
|
||||
final event = AppEvent.fromJson(const <String, dynamic>{
|
||||
'type': 'progress',
|
||||
'payload': 42,
|
||||
});
|
||||
|
||||
expect(event.payload, <String, dynamic>{'value': 42});
|
||||
});
|
||||
});
|
||||
|
||||
group('Ble models', () {
|
||||
test('BleDevice stores constructor fields', () {
|
||||
const device = BleDevice(name: 'Showen', id: 'dev-1', rssi: -48);
|
||||
|
||||
expect(device.name, 'Showen');
|
||||
expect(device.id, 'dev-1');
|
||||
expect(device.rssi, -48);
|
||||
});
|
||||
|
||||
test('BleStatus parses json and raw json', () {
|
||||
final status = BleStatus.fromJson(const <String, dynamic>{
|
||||
'ok': true,
|
||||
'action': 'provision',
|
||||
'state': 'queued',
|
||||
});
|
||||
final raw = BleStatus.fromRawJson(
|
||||
'{"ok":false,"action":"scan","error":"failed"}',
|
||||
);
|
||||
|
||||
expect(status.isQueued, isTrue);
|
||||
expect(status.message, 'queued');
|
||||
expect(raw.isSuccess, isFalse);
|
||||
expect(raw.message, 'failed');
|
||||
});
|
||||
});
|
||||
|
||||
group('BleServiceStatus', () {
|
||||
test('initial and fromJson', () {
|
||||
final initial = BleServiceStatus.initial();
|
||||
final status = BleServiceStatus.fromJson(const <String, dynamic>{
|
||||
'running': true,
|
||||
'embedded': true,
|
||||
'device_name': 'Showen BLE',
|
||||
});
|
||||
|
||||
expect(initial.running, isFalse);
|
||||
expect(initial.embedded, isFalse);
|
||||
expect(status.running, isTrue);
|
||||
expect(status.embedded, isTrue);
|
||||
expect(status.deviceName, 'Showen BLE');
|
||||
});
|
||||
});
|
||||
|
||||
group('DeviceStatus', () {
|
||||
test('initial and copyWith preserve nested models', () {
|
||||
final updated = DeviceStatus.initial().copyWith(
|
||||
connected: true,
|
||||
connectionType: 'wifi',
|
||||
deviceName: 'Showen Box',
|
||||
ipAddress: '192.168.1.20',
|
||||
playerStatus: PlayerStatus.initial().copyWith(running: true),
|
||||
wifiStatus: WifiStatus.fromJson(
|
||||
const <String, dynamic>{'connected': true, 'ssid': 'Office'},
|
||||
),
|
||||
bleStatus: BleServiceStatus.fromJson(
|
||||
const <String, dynamic>{'running': true, 'embedded': false},
|
||||
),
|
||||
);
|
||||
|
||||
expect(updated.connected, isTrue);
|
||||
expect(updated.connectionType, 'wifi');
|
||||
expect(updated.deviceName, 'Showen Box');
|
||||
expect(updated.ipAddress, '192.168.1.20');
|
||||
expect(updated.playerStatus?.running, isTrue);
|
||||
expect(updated.wifiStatus?.ssid, 'Office');
|
||||
expect(updated.bleStatus?.running, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('PlayerStatus', () {
|
||||
test('fromJson and toJson round trip', () {
|
||||
final status = PlayerStatus.fromJson(const <String, dynamic>{
|
||||
'running': true,
|
||||
'paused': false,
|
||||
'in_transition': true,
|
||||
'current_index': 3,
|
||||
'playlist_length': 9,
|
||||
'current_video': 'intro.mp4',
|
||||
});
|
||||
|
||||
expect(status.toJson(), <String, dynamic>{
|
||||
'running': true,
|
||||
'paused': false,
|
||||
'in_transition': true,
|
||||
'current_index': 3,
|
||||
'playlist_length': 9,
|
||||
'current_video': 'intro.mp4',
|
||||
});
|
||||
expect(status.copyWith(paused: true).paused, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('VideoItem', () {
|
||||
test('fromJson parses file metadata', () {
|
||||
final video = VideoItem.fromJson(const <String, dynamic>{
|
||||
'name': 'demo.mp4',
|
||||
'size': 3145728,
|
||||
});
|
||||
|
||||
expect(video.name, 'demo.mp4');
|
||||
expect(video.size, 3145728);
|
||||
expect(video.sizeLabel, '3.0 MB');
|
||||
});
|
||||
});
|
||||
|
||||
group('WifiNetwork', () {
|
||||
test('fromJson parses network metadata', () {
|
||||
final network = WifiNetwork.fromJson(const <String, dynamic>{
|
||||
'ssid': 'ShowenLab',
|
||||
'signal': -51,
|
||||
'security': 'WPA2',
|
||||
});
|
||||
|
||||
expect(network.ssid, 'ShowenLab');
|
||||
expect(network.signalLabel, '-51 dBm');
|
||||
expect(network.security, 'WPA2');
|
||||
});
|
||||
});
|
||||
|
||||
group('WifiStatus', () {
|
||||
test('disconnected and fromJson', () {
|
||||
final disconnected = WifiStatus.disconnected();
|
||||
final status = WifiStatus.fromJson(const <String, dynamic>{
|
||||
'connected': true,
|
||||
'ssid': 'ShowenLab',
|
||||
'ip': '192.168.1.10',
|
||||
});
|
||||
|
||||
expect(disconnected.connected, isFalse);
|
||||
expect(status.connected, isTrue);
|
||||
expect(status.ssid, 'ShowenLab');
|
||||
expect(status.ip, '192.168.1.10');
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user