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>
184 lines
5.5 KiB
Dart
184 lines
5.5 KiB
Dart
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');
|
|
});
|
|
});
|
|
}
|