Fix settings save path coverage

This commit is contained in:
Haitao Pan 2026-04-21 10:34:44 +08:00
parent f082ceabce
commit d852bb3a1b
2 changed files with 250 additions and 166 deletions

View File

@ -64,108 +64,126 @@ void main() {
expect(loginCount, 1);
});
testWidgets('shows account sync status, resync, and exit in signed-in mode', (
tester,
) async {
final controllers = _TestControllers();
addTearDown(controllers.dispose);
testWidgets(
'shows account sync status, resync, and exit in signed-in mode',
(tester) async {
final controllers = _TestControllers();
addTearDown(controllers.dispose);
var syncCount = 0;
var logoutCount = 0;
var syncCount = 0;
var logoutCount = 0;
final settings = SettingsSnapshot.defaults().copyWith(
accountBaseUrl: 'https://accounts.svc.plus',
accountUsername: 'review@svc.plus',
acpBridgeServerModeConfig: AcpBridgeServerModeConfig.defaults()
.copyWith(
cloudSynced: AcpBridgeServerModeConfig.defaults().cloudSynced
.copyWith(
lastSyncAt: DateTime(
2026,
4,
12,
10,
0,
).millisecondsSinceEpoch,
remoteServerSummary: AcpBridgeServerModeConfig.defaults()
.cloudSynced
.remoteServerSummary
.copyWith(endpoint: 'https://bridge.svc.plus'),
),
),
);
await tester.pumpWidget(
_buildTestApp(
child: SettingsAccountPanel(
settings: settings,
accountSession: const AccountSessionSummary(
userId: 'u-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'operator',
mfaEnabled: true,
totpEnabled: true,
),
accountState: AccountSyncState.defaults().copyWith(
syncState: 'ready',
syncMessage: 'Bridge access synced',
profileScope: 'bridge',
tokenConfigured: const AccountTokenConfigured(
bridge: true,
vault: false,
apisix: false,
final settings = SettingsSnapshot.defaults().copyWith(
accountBaseUrl: 'https://accounts.svc.plus',
accountUsername: 'review@svc.plus',
acpBridgeServerModeConfig: AcpBridgeServerModeConfig.defaults()
.copyWith(
cloudSynced: AcpBridgeServerModeConfig.defaults().cloudSynced
.copyWith(
lastSyncAt: DateTime(
2026,
4,
12,
10,
0,
).millisecondsSinceEpoch,
remoteServerSummary: AcpBridgeServerModeConfig.defaults()
.cloudSynced
.remoteServerSummary
.copyWith(endpoint: 'https://bridge.svc.plus'),
),
),
);
await tester.pumpWidget(
_buildTestApp(
child: SettingsAccountPanel(
settings: settings,
accountSession: const AccountSessionSummary(
userId: 'u-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'operator',
mfaEnabled: true,
totpEnabled: true,
),
accountState: AccountSyncState.defaults().copyWith(
syncState: 'ready',
syncMessage: 'Bridge access synced',
profileScope: 'bridge',
tokenConfigured: const AccountTokenConfigured(
bridge: true,
vault: false,
apisix: false,
),
),
accountBusy: false,
accountSignedIn: true,
accountMfaRequired: false,
accountBaseUrlController: controllers.baseUrl,
accountIdentifierController: controllers.identifier,
accountPasswordController: controllers.password,
accountMfaCodeController: controllers.mfaCode,
bridgeUrlController: controllers.bridgeUrl,
bridgeTokenController: controllers.bridgeToken,
onSaveAccountProfile: ({required bool isManualBridge}) async {},
onLogin: () async {},
onVerifyMfa: () async {},
onCancelMfa: () async {},
onSync: () async {
syncCount += 1;
},
onLogout: () async {
logoutCount += 1;
},
),
accountBusy: false,
accountSignedIn: true,
accountMfaRequired: false,
accountBaseUrlController: controllers.baseUrl,
accountIdentifierController: controllers.identifier,
accountPasswordController: controllers.password,
accountMfaCodeController: controllers.mfaCode,
bridgeUrlController: controllers.bridgeUrl,
bridgeTokenController: controllers.bridgeToken,
onSaveAccountProfile: ({required bool isManualBridge}) async {},
onLogin: () async {},
onVerifyMfa: () async {},
onCancelMfa: () async {},
onSync: () async {
syncCount += 1;
},
onLogout: () async {
logoutCount += 1;
},
),
),
);
);
expect(find.text('账号登录与同步'), findsOneWidget);
expect(find.text('账号同步'), findsOneWidget);
expect(find.textContaining('账号同步状态'), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-sync-button')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-manual-reset-button')), findsNothing);
expect(
find.byKey(const ValueKey('settings-account-logout-button')),
findsOneWidget,
);
expect(find.text('账号登录与同步'), findsOneWidget);
expect(find.text('账号同步'), findsOneWidget);
expect(find.textContaining('账号同步状态'), findsOneWidget);
expect(
find.byKey(const ValueKey('settings-account-sync-button')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-manual-reset-button')),
findsNothing,
);
expect(
find.byKey(const ValueKey('settings-account-logout-button')),
findsOneWidget,
);
expect(find.byKey(const ValueKey('settings-account-summary-service-url')), findsNothing);
expect(find.byKey(const ValueKey('settings-account-summary-account-identifier')), findsNothing);
expect(find.byKey(const ValueKey('settings-account-summary-remote-summary')), findsNothing);
expect(
find.byKey(const ValueKey('settings-account-summary-service-url')),
findsNothing,
);
expect(
find.byKey(
const ValueKey('settings-account-summary-account-identifier'),
),
findsNothing,
);
expect(
find.byKey(const ValueKey('settings-account-summary-remote-summary')),
findsNothing,
);
await tester.tap(
find.byKey(const ValueKey('settings-account-sync-button')),
);
await tester.pump();
await tester.tap(
find.byKey(const ValueKey('settings-account-logout-button')),
);
await tester.pump();
await tester.tap(
find.byKey(const ValueKey('settings-account-sync-button')),
);
await tester.pump();
await tester.tap(
find.byKey(const ValueKey('settings-account-logout-button')),
);
await tester.pump();
expect(syncCount, 1);
expect(logoutCount, 1);
});
expect(syncCount, 1);
expect(logoutCount, 1);
},
);
testWidgets('keeps details collapsed by default and expands diagnostics', (
tester,
@ -215,8 +233,16 @@ void main() {
),
);
expect(find.byKey(const ValueKey('settings-account-summary-service-url')), findsNothing);
expect(find.byKey(const ValueKey('settings-account-summary-account-identifier')), findsNothing);
expect(
find.byKey(const ValueKey('settings-account-summary-service-url')),
findsNothing,
);
expect(
find.byKey(
const ValueKey('settings-account-summary-account-identifier'),
),
findsNothing,
);
expect(
find.byKey(const ValueKey('settings-account-summary-expansion')),
findsOneWidget,
@ -225,80 +251,120 @@ void main() {
await tester.tap(find.byType(ExpansionTile));
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('settings-account-summary-service-url')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-summary-account-identifier')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-summary-remote-summary')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-summary-last-sync')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-summary-mfa-status')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-summary-sync-scope')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-summary-token-configured')), findsOneWidget);
});
testWidgets('shows manual bridge save status and reset action when not account sync', (
tester,
) async {
final controllers = _TestControllers();
addTearDown(controllers.dispose);
var saveCount = 0;
var logoutCount = 0;
await tester.pumpWidget(
_buildTestApp(
child: SettingsAccountPanel(
settings: SettingsSnapshot.defaults().copyWith(
acpBridgeServerModeConfig: AcpBridgeServerModeConfig.defaults().copyWith(
selfHosted: AcpBridgeServerModeConfig.defaults().selfHosted.copyWith(
serverUrl: 'https://xworkmate-bridge.svc.plus',
passwordRef: 'bridge-token-ref',
),
),
),
accountSession: const AccountSessionSummary(
userId: 'u-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'operator',
mfaEnabled: false,
),
accountState: null,
accountBusy: false,
accountSignedIn: true,
accountMfaRequired: false,
accountBaseUrlController: controllers.baseUrl,
accountIdentifierController: controllers.identifier,
accountPasswordController: controllers.password,
accountMfaCodeController: controllers.mfaCode,
bridgeUrlController: controllers.bridgeUrl,
bridgeTokenController: controllers.bridgeToken,
onSaveAccountProfile: ({required bool isManualBridge}) async {
saveCount += 1;
},
onLogin: () async {},
onVerifyMfa: () async {},
onCancelMfa: () async {},
onSync: () async {},
onLogout: () async {
logoutCount += 1;
},
),
),
expect(
find.byKey(const ValueKey('settings-account-summary-service-url')),
findsOneWidget,
);
expect(
find.byKey(
const ValueKey('settings-account-summary-account-identifier'),
),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-summary-remote-summary')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-summary-last-sync')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-summary-mfa-status')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-summary-sync-scope')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-summary-token-configured')),
findsOneWidget,
);
expect(find.text('手动 Bridge'), findsOneWidget);
expect(find.textContaining('保存状态'), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-manual-reset-button')), findsOneWidget);
expect(find.byKey(const ValueKey('settings-account-sync-button')), findsNothing);
await tester.tap(find.byKey(const ValueKey('settings-account-manual-reset-button')));
await tester.pump();
await tester.tap(find.byKey(const ValueKey('settings-account-logout-button')));
await tester.pump();
expect(saveCount, 1);
expect(logoutCount, 1);
});
testWidgets(
'shows manual bridge save status and reset action when not account sync',
(tester) async {
final controllers = _TestControllers();
addTearDown(controllers.dispose);
var saveCount = 0;
var logoutCount = 0;
var receivedManualBridge = false;
await tester.pumpWidget(
_buildTestApp(
child: SettingsAccountPanel(
settings: SettingsSnapshot.defaults().copyWith(
acpBridgeServerModeConfig: AcpBridgeServerModeConfig.defaults()
.copyWith(
selfHosted: AcpBridgeServerModeConfig.defaults()
.selfHosted
.copyWith(
serverUrl: 'https://xworkmate-bridge.svc.plus',
passwordRef: 'bridge-token-ref',
),
),
),
accountSession: const AccountSessionSummary(
userId: 'u-1',
email: 'review@svc.plus',
name: 'Review User',
role: 'operator',
mfaEnabled: false,
),
accountState: null,
accountBusy: false,
accountSignedIn: true,
accountMfaRequired: false,
accountBaseUrlController: controllers.baseUrl,
accountIdentifierController: controllers.identifier,
accountPasswordController: controllers.password,
accountMfaCodeController: controllers.mfaCode,
bridgeUrlController: controllers.bridgeUrl,
bridgeTokenController: controllers.bridgeToken,
onSaveAccountProfile: ({required bool isManualBridge}) async {
saveCount += 1;
receivedManualBridge = isManualBridge;
},
onLogin: () async {},
onVerifyMfa: () async {},
onCancelMfa: () async {},
onSync: () async {},
onLogout: () async {
logoutCount += 1;
},
),
),
);
expect(find.text('手动 Bridge'), findsOneWidget);
expect(find.textContaining('保存状态'), findsOneWidget);
expect(
find.byKey(const ValueKey('settings-account-manual-reset-button')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-account-sync-button')),
findsNothing,
);
await tester.tap(
find.byKey(const ValueKey('settings-account-manual-reset-button')),
);
await tester.pump();
await tester.tap(
find.byKey(const ValueKey('settings-account-logout-button')),
);
await tester.pump();
expect(saveCount, 1);
expect(receivedManualBridge, isTrue);
expect(logoutCount, 1);
},
);
testWidgets('shows live syncing feedback while resync is running', (
tester,
) async {

View File

@ -134,7 +134,25 @@ void main() {
test(
'ignores legacy INTERNAL_SERVICE_TOKEN for managed bridge auth resolution',
() async {
final storeRoot = await Directory.systemTemp.createTemp(
'xworkmate-bridge-auth-resolver-legacy-',
);
addTearDown(() async {
if (await storeRoot.exists()) {
await storeRoot.delete(recursive: true);
}
});
final store = SecureConfigStore(
secretRootPathResolver: () async => '${storeRoot.path}/secrets',
appDataRootPathResolver: () async => '${storeRoot.path}/app-data',
supportRootPathResolver: () async => '${storeRoot.path}/support',
enableSecureStorage: false,
);
await store.initialize();
final controller = AppController(
store: store,
environmentOverride: const <String, String>{
'INTERNAL_SERVICE_TOKEN': 'legacy-bridge-token',
},