From 777c7cb93a8647d5edbe26392d846ebf19a06913 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 28 Aug 2026 12:36:41 +0200 Subject: [PATCH] Fix two long-red CI gates: palette_check path after md rename, file-length in sync_settings_ui Co-Authored-By: Claude Opus 5 (1M context) --- .../lib/src/firebase_connect_result.dart | 47 +++++++ .../lib/src/firebase_sync_controller.dart | 49 +------ .../lib/src/sync_settings_screen.dart | 1 + sync_settings_ui/lib/sync_settings_ui.dart | 1 + .../firebase_sync_controller_google_test.dart | 129 ++++++++++++++++++ .../test/firebase_sync_controller_test.dart | 116 ---------------- .../scripts/palette_check.py | 4 +- 7 files changed, 182 insertions(+), 165 deletions(-) create mode 100644 sync_settings_ui/lib/src/firebase_connect_result.dart create mode 100644 sync_settings_ui/test/firebase_sync_controller_google_test.dart diff --git a/sync_settings_ui/lib/src/firebase_connect_result.dart b/sync_settings_ui/lib/src/firebase_connect_result.dart new file mode 100644 index 0000000..45a86f1 --- /dev/null +++ b/sync_settings_ui/lib/src/firebase_connect_result.dart @@ -0,0 +1,47 @@ +/// Outcome of a Firebase connect attempt, distinguishing why it did or did +/// not end connected -- the UI needs different copy for each case, and +/// collapsing them lost that distinction in the app-local copies this +/// generalizes. +enum FirebaseConnectOutcome { + /// Signed in and the session is confirmed stored. + connected, + + /// The client rejected the given account (bad password / wrong account). + rejected, + + /// The user dismissed the Google picker. Not an error. + cancelled, + + /// Google signed in, but this device did not end up with a stored + /// session -- the client came back non-null yet [FirebaseSyncController] + /// re-reads state rather than trusting that, and found nothing durable. + signedInButNotPersisted, + + /// Google succeeded but resolved to a uid the security rules do not pin + /// (wrong account entirely). + wrongAccount, + + /// Any other failure (network, unreachable keystore, etc). + failed, +} + +/// Result of a [FirebaseSyncController.connectWithPassword] or +/// [FirebaseSyncController.connectWithGoogle] call. +class FirebaseConnectResult { + /// Creates a [FirebaseConnectResult]. + const FirebaseConnectResult({ + required this.outcome, + this.email, + this.message, + }); + + /// What happened. + final FirebaseConnectOutcome outcome; + + /// The connected account's email, when known. + final String? email; + + /// Human-readable detail for [FirebaseConnectOutcome.failed] and + /// [FirebaseConnectOutcome.wrongAccount]. + final String? message; +} diff --git a/sync_settings_ui/lib/src/firebase_sync_controller.dart b/sync_settings_ui/lib/src/firebase_sync_controller.dart index 48a42ad..ca81c28 100644 --- a/sync_settings_ui/lib/src/firebase_sync_controller.dart +++ b/sync_settings_ui/lib/src/firebase_sync_controller.dart @@ -1,54 +1,7 @@ import 'dart:developer'; import 'package:crdt_sync/crdt_sync.dart'; - -/// Outcome of a Firebase connect attempt, distinguishing why it did or did -/// not end connected -- the UI needs different copy for each case, and -/// collapsing them lost that distinction in the app-local copies this -/// generalizes. -enum FirebaseConnectOutcome { - /// Signed in and the session is confirmed stored. - connected, - - /// The client rejected the given account (bad password / wrong account). - rejected, - - /// The user dismissed the Google picker. Not an error. - cancelled, - - /// Google signed in, but this device did not end up with a stored - /// session -- the client came back non-null yet [FirebaseSyncController] - /// re-reads state rather than trusting that, and found nothing durable. - signedInButNotPersisted, - - /// Google succeeded but resolved to a uid the security rules do not pin - /// (wrong account entirely). - wrongAccount, - - /// Any other failure (network, unreachable keystore, etc). - failed, -} - -/// Result of a [FirebaseSyncController.connectWithPassword] or -/// [FirebaseSyncController.connectWithGoogle] call. -class FirebaseConnectResult { - /// Creates a [FirebaseConnectResult]. - const FirebaseConnectResult({ - required this.outcome, - this.email, - this.message, - }); - - /// What happened. - final FirebaseConnectOutcome outcome; - - /// The connected account's email, when known. - final String? email; - - /// Human-readable detail for [FirebaseConnectOutcome.failed] and - /// [FirebaseConnectOutcome.wrongAccount]. - final String? message; -} +import 'package:sync_settings_ui/src/firebase_connect_result.dart'; /// Sequences Firebase connect/disconnect/status-probe over injected /// closures, generalized from the near-identical `_connectFirebase` / diff --git a/sync_settings_ui/lib/src/sync_settings_screen.dart b/sync_settings_ui/lib/src/sync_settings_screen.dart index 9b27a00..3329012 100644 --- a/sync_settings_ui/lib/src/sync_settings_screen.dart +++ b/sync_settings_ui/lib/src/sync_settings_screen.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:crdt_sync/crdt_sync.dart'; import 'package:flutter/material.dart'; import 'package:sync_settings_ui/src/backup_slot.dart'; +import 'package:sync_settings_ui/src/firebase_connect_result.dart'; import 'package:sync_settings_ui/src/firebase_sync_controller.dart'; part 'sync_settings_sections.dart'; diff --git a/sync_settings_ui/lib/sync_settings_ui.dart b/sync_settings_ui/lib/sync_settings_ui.dart index 101237f..4acad37 100644 --- a/sync_settings_ui/lib/sync_settings_ui.dart +++ b/sync_settings_ui/lib/sync_settings_ui.dart @@ -12,5 +12,6 @@ library; export 'src/backup_slot.dart'; +export 'src/firebase_connect_result.dart'; export 'src/firebase_sync_controller.dart'; export 'src/sync_settings_screen.dart'; diff --git a/sync_settings_ui/test/firebase_sync_controller_google_test.dart b/sync_settings_ui/test/firebase_sync_controller_google_test.dart new file mode 100644 index 0000000..2a90744 --- /dev/null +++ b/sync_settings_ui/test/firebase_sync_controller_google_test.dart @@ -0,0 +1,129 @@ +import 'package:crdt_sync/crdt_sync.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sync_settings_ui/sync_settings_ui.dart'; + +FirebaseRestClient _fakeClient() => FirebaseRestClient( + databaseUrl: 'https://example.firebasedatabase.app', + auth: FirebaseTokenProvider( + apiKey: 'test-key', + store: InMemoryCredentialStore(), + ), +); + +void main() { + group('connectWithGoogle', () { + test('throws StateError when no googleFirebaseFactory is wired', () async { + final controller = FirebaseSyncController( + accountLoader: () async => null, + accountSaver: (_) async {}, + accountClearer: () async {}, + sessionProbe: () async => false, + firebaseFactory: () async => null, + ); + expect(controller.supportsGoogle, isFalse); + expect(controller.connectWithGoogle, throwsStateError); + }); + + test('reports cancelled when the picker returns null', () async { + final controller = FirebaseSyncController( + accountLoader: () async => null, + accountSaver: (_) async {}, + accountClearer: () async {}, + sessionProbe: () async => false, + firebaseFactory: () async => null, + googleFirebaseFactory: () async => null, + ); + expect(controller.supportsGoogle, isTrue); + final result = await controller.connectWithGoogle(); + expect(result.outcome, FirebaseConnectOutcome.cancelled); + }); + + test('reports connected and the reported email on success', () async { + final controller = FirebaseSyncController( + accountLoader: () async => + const FirebaseAccount(email: 'g@b.com', password: ''), + accountSaver: (_) async {}, + accountClearer: () async {}, + sessionProbe: () async => true, + firebaseFactory: () async => null, + googleFirebaseFactory: () async => _fakeClient(), + ); + final result = await controller.connectWithGoogle(); + expect(result.outcome, FirebaseConnectOutcome.connected); + expect(result.email, 'g@b.com'); + }); + + test( + 'reports signedInButNotPersisted with the stale account email when ' + 'the client came back non-null but no session actually stuck -- the ' + 'invariant this package exists to centralize', + () async { + final controller = FirebaseSyncController( + accountLoader: () async => + const FirebaseAccount(email: 'stale@b.com', password: ''), + accountSaver: (_) async {}, + accountClearer: () async {}, + sessionProbe: () async => false, + firebaseFactory: () async => null, + googleFirebaseFactory: () async => _fakeClient(), + ); + final result = await controller.connectWithGoogle(); + expect( + result.outcome, + FirebaseConnectOutcome.signedInButNotPersisted, + ); + expect(result.email, 'stale@b.com'); + }, + ); + + test( + 'reports wrongAccount with the error message on FirebaseAuthError', + () async { + final controller = FirebaseSyncController( + accountLoader: () async => null, + accountSaver: (_) async {}, + accountClearer: () async {}, + sessionProbe: () async => false, + firebaseFactory: () async => null, + googleFirebaseFactory: () async => + throw FirebaseAuthError('wrong uid'), + ); + final result = await controller.connectWithGoogle(); + expect(result.outcome, FirebaseConnectOutcome.wrongAccount); + expect(result.message, 'wrong uid'); + }, + ); + + test( + 'reports failed with the stringified error on any other failure', + () async { + final controller = FirebaseSyncController( + accountLoader: () async => null, + accountSaver: (_) async {}, + accountClearer: () async {}, + sessionProbe: () async => false, + firebaseFactory: () async => null, + googleFirebaseFactory: () async => throw StateError('boom'), + ); + final result = await controller.connectWithGoogle(); + expect(result.outcome, FirebaseConnectOutcome.failed); + expect(result.message, contains('boom')); + }, + ); + }); + + group('disconnect', () { + test('calls the account clearer', () async { + var cleared = false; + final controller = FirebaseSyncController( + accountLoader: () async => null, + accountSaver: (_) async {}, + accountClearer: () async => cleared = true, + sessionProbe: () async => false, + firebaseFactory: () async => null, + ); + await controller.disconnect(); + expect(cleared, isTrue); + }); + }); +} diff --git a/sync_settings_ui/test/firebase_sync_controller_test.dart b/sync_settings_ui/test/firebase_sync_controller_test.dart index f95bc13..8ea116b 100644 --- a/sync_settings_ui/test/firebase_sync_controller_test.dart +++ b/sync_settings_ui/test/firebase_sync_controller_test.dart @@ -163,120 +163,4 @@ void main() { expect(stages, ['Saving account…', 'Signing in to Firebase…']); }); }); - - group('connectWithGoogle', () { - test('throws StateError when no googleFirebaseFactory is wired', () async { - final controller = FirebaseSyncController( - accountLoader: () async => null, - accountSaver: (_) async {}, - accountClearer: () async {}, - sessionProbe: () async => false, - firebaseFactory: () async => null, - ); - expect(controller.supportsGoogle, isFalse); - expect(controller.connectWithGoogle, throwsStateError); - }); - - test('reports cancelled when the picker returns null', () async { - final controller = FirebaseSyncController( - accountLoader: () async => null, - accountSaver: (_) async {}, - accountClearer: () async {}, - sessionProbe: () async => false, - firebaseFactory: () async => null, - googleFirebaseFactory: () async => null, - ); - expect(controller.supportsGoogle, isTrue); - final result = await controller.connectWithGoogle(); - expect(result.outcome, FirebaseConnectOutcome.cancelled); - }); - - test('reports connected and the reported email on success', () async { - final controller = FirebaseSyncController( - accountLoader: () async => - const FirebaseAccount(email: 'g@b.com', password: ''), - accountSaver: (_) async {}, - accountClearer: () async {}, - sessionProbe: () async => true, - firebaseFactory: () async => null, - googleFirebaseFactory: () async => _fakeClient(), - ); - final result = await controller.connectWithGoogle(); - expect(result.outcome, FirebaseConnectOutcome.connected); - expect(result.email, 'g@b.com'); - }); - - test( - 'reports signedInButNotPersisted with the stale account email when ' - 'the client came back non-null but no session actually stuck -- the ' - 'invariant this package exists to centralize', - () async { - final controller = FirebaseSyncController( - accountLoader: () async => - const FirebaseAccount(email: 'stale@b.com', password: ''), - accountSaver: (_) async {}, - accountClearer: () async {}, - sessionProbe: () async => false, - firebaseFactory: () async => null, - googleFirebaseFactory: () async => _fakeClient(), - ); - final result = await controller.connectWithGoogle(); - expect( - result.outcome, - FirebaseConnectOutcome.signedInButNotPersisted, - ); - expect(result.email, 'stale@b.com'); - }, - ); - - test( - 'reports wrongAccount with the error message on FirebaseAuthError', - () async { - final controller = FirebaseSyncController( - accountLoader: () async => null, - accountSaver: (_) async {}, - accountClearer: () async {}, - sessionProbe: () async => false, - firebaseFactory: () async => null, - googleFirebaseFactory: () async => - throw FirebaseAuthError('wrong uid'), - ); - final result = await controller.connectWithGoogle(); - expect(result.outcome, FirebaseConnectOutcome.wrongAccount); - expect(result.message, 'wrong uid'); - }, - ); - - test( - 'reports failed with the stringified error on any other failure', - () async { - final controller = FirebaseSyncController( - accountLoader: () async => null, - accountSaver: (_) async {}, - accountClearer: () async {}, - sessionProbe: () async => false, - firebaseFactory: () async => null, - googleFirebaseFactory: () async => throw StateError('boom'), - ); - final result = await controller.connectWithGoogle(); - expect(result.outcome, FirebaseConnectOutcome.failed); - expect(result.message, contains('boom')); - }, - ); - }); - - group('disconnect', () { - test('calls the account clearer', () async { - var cleared = false; - final controller = FirebaseSyncController( - accountLoader: () async => null, - accountSaver: (_) async {}, - accountClearer: () async => cleared = true, - sessionProbe: () async => false, - firebaseFactory: () async => null, - ); - await controller.disconnect(); - expect(cleared, isTrue); - }); - }); } diff --git a/unified-design-system/scripts/palette_check.py b/unified-design-system/scripts/palette_check.py index 3647cde..bd0275b 100644 --- a/unified-design-system/scripts/palette_check.py +++ b/unified-design-system/scripts/palette_check.py @@ -34,7 +34,9 @@ from palette_map import NON_COLOUR_CSS, PALETTE, STACKS # Repo root: this file is /unified-design-system/scripts/palette_check.py ROOT = Path(__file__).resolve().parents[2] -TOKENS_MD = ROOT / "unified-design-system" / "tokens.md" +# Renamed by the md-naming migration; the gate kept pointing at the old +# name and had been failing on every push since. +TOKENS_MD = ROOT / "unified-design-system" / "DOCS-tokens.md" TOKENS_CSS = ROOT / "web_ui" / "src" / "tokens.css" TOKENS_DART = ROOT / "design_system" / "lib" / "src" / "tokens.dart" # LockConfig moved out of _window.py when that file was split for the