mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 19:27:15 +01:00
Stop the history tests reading the developer's own Firebase account
14 tests failed with a RemoteSyncError from listDirectory, and the cause was not in the screen they were testing. The sync guard short-circuits on "no backend configured", but it asks that question two ways: the sync token AND whether a Firebase account exists. The tests faked the token into secure storage and stopped there, so on a machine holding a cached refresh token the real factory answered yes, the guard opened, and the fetch escaped to the network -- where flutter_test blocks HttpClient. That went unnoticed until the Linux desktop target landed, because the desktop build is what authenticates from the shared refresh token: the same tests pass on the commit before it, on this machine, this minute. So the fixture now pins firebaseFactory to "no account" by default, which is what the faked token was already trying to express, and HistoryScreen takes it as a tests-only seam next to the httpClient one it already had. The two direct constructions in workout_sync_read_test.dart are pinned the same way. 402 Flutter tests pass and flutter analyze is clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DfJC1Wu1SEgJ6X49k5Jqtg
This commit is contained in:
parent
c05dc9c224
commit
2f95c6cb69
@ -8,6 +8,7 @@ library;
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:crdt_sync/crdt_sync.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:workout_app/models/exercise.dart';
|
||||
@ -29,11 +30,20 @@ class HistoryScreen extends StatefulWidget {
|
||||
///
|
||||
/// [httpClient] is injected only by tests, so the synced-workout fetch can
|
||||
/// be driven without real network — same pattern as `SettingsScreen`.
|
||||
const HistoryScreen({super.key, this.httpClient});
|
||||
///
|
||||
/// [firebaseFactory] is likewise tests-only, and is NOT redundant with
|
||||
/// [httpClient]: the sync guard short-circuits on "no backend configured",
|
||||
/// and it asks Firebase that question directly. On a desktop that has a
|
||||
/// cached refresh token the real factory answers yes, so the guard opens and
|
||||
/// the fetch goes to the network even with the sync token faked away.
|
||||
const HistoryScreen({super.key, this.httpClient, this.firebaseFactory});
|
||||
|
||||
/// Overrides the HTTP client used to read synced workouts (tests only).
|
||||
final http.Client? httpClient;
|
||||
|
||||
/// Overrides how a Firebase account is discovered (tests only).
|
||||
final Future<FirebaseRestClient?> Function()? firebaseFactory;
|
||||
|
||||
@override
|
||||
State<HistoryScreen> createState() => _HistoryScreenState();
|
||||
}
|
||||
@ -106,6 +116,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
||||
Future<List<Map<String, dynamic>>> _readSyncedPayloads() =>
|
||||
WorkoutSyncService(
|
||||
httpClient: widget.httpClient,
|
||||
firebaseFactory: widget.firebaseFactory,
|
||||
).readMergedWorkoutPayloads();
|
||||
|
||||
Future<void> _pickExercise(String name) async {
|
||||
|
||||
@ -23,9 +23,23 @@ Future<void> pumpHistory(WidgetTester tester, Widget w) async {
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
Widget wrapHistory({http.Client? httpClient}) => MaterialApp(
|
||||
/// Wraps the history screen with sync pinned to a known state.
|
||||
///
|
||||
/// [firebaseFactory] defaults to "no account", which is what makes these
|
||||
/// tests independent of the machine they run on. Faking the sync token is not
|
||||
/// enough on its own: the guard also asks Firebase whether an account exists,
|
||||
/// and on a desktop holding a cached refresh token the real factory says yes,
|
||||
/// so the fetch escapes to the network and the test fails with a
|
||||
/// RemoteSyncError that has nothing to do with the screen.
|
||||
Widget wrapHistory({
|
||||
http.Client? httpClient,
|
||||
Future<FirebaseRestClient?> Function()? firebaseFactory,
|
||||
}) => MaterialApp(
|
||||
theme: buildAppTheme(),
|
||||
home: HistoryScreen(httpClient: httpClient),
|
||||
home: HistoryScreen(
|
||||
httpClient: httpClient,
|
||||
firebaseFactory: firebaseFactory ?? () async => null,
|
||||
),
|
||||
);
|
||||
|
||||
/// A GitHub mock serving one PC device log holding a run and a manual.
|
||||
|
||||
@ -75,6 +75,7 @@ void main() {
|
||||
);
|
||||
final payloads = await WorkoutSyncService(
|
||||
httpClient: httpClient,
|
||||
firebaseFactory: () async => null,
|
||||
).readMergedManualPayloads();
|
||||
expect(payloads, hasLength(1));
|
||||
expect(payloads.single['cost'], 'NEW');
|
||||
@ -148,6 +149,7 @@ void main() {
|
||||
);
|
||||
final manuals = await WorkoutSyncService(
|
||||
httpClient: httpClient,
|
||||
firebaseFactory: () async => null,
|
||||
).readMergedManualPayloads();
|
||||
expect(manuals, hasLength(1));
|
||||
expect(manuals.single['kind'], 'manual_workout');
|
||||
@ -207,6 +209,7 @@ void main() {
|
||||
|
||||
final payloads = await WorkoutSyncService(
|
||||
httpClient: httpClient,
|
||||
firebaseFactory: () async => null,
|
||||
).readMergedManualPayloads();
|
||||
|
||||
expect(payloads, hasLength(1));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user