Make the sync-account route testable, and cover every branch

Same change todo just took: the route read its own configuration from
Platform.environment and $HOME, so only the "disabled" branch was reachable
from a test. Both are now constructor parameters defaulting to the
environment -- production behaviour unchanged -- and the four outcomes are
covered: 404 disabled, 200 with the account, 404 when ~/.config/crdt-sync/ is
incomplete, 404 when firebase.json has no usable email.

522 tests, analyzer clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177xwzcpfpWG2qca8Mr39q9
This commit is contained in:
Krzysztof Rudnicki 2026-08-09 21:49:28 +02:00
parent 55fade1e15
commit 19991f3600
2 changed files with 79 additions and 10 deletions

View File

@ -35,7 +35,20 @@ class WrapperServer {
required this.webRoot,
required this.dataDir,
required this.gitHubProxy,
});
bool? serveSyncAccount,
String? syncConfigDir,
}) : serveSyncAccount =
serveSyncAccount ??
(Platform.environment[kSyncAccountEnvVar] ?? '').isNotEmpty,
syncConfigDir =
syncConfigDir ??
p.join(Platform.environment['HOME'] ?? '', '.config', 'crdt-sync');
/// Whether the sync-account route answers; off unless explicitly enabled.
final bool serveSyncAccount;
/// Directory holding `firebase.json` and `password`.
final String syncConfigDir;
/// Directory holding the built Flutter web assets.
final String webRoot;
@ -114,17 +127,12 @@ class WrapperServer {
/// the same reasoning that keeps the GitHub token in [gitHubProxy] instead
/// of the browser.
Future<void> _syncAccount(HttpRequest request) async {
if ((Platform.environment[kSyncAccountEnvVar] ?? '').isEmpty) {
if (!serveSyncAccount) {
request.response.statusCode = HttpStatus.notFound;
return;
}
final configDir = p.join(
Platform.environment['HOME'] ?? '',
'.config',
'crdt-sync',
);
final configFile = File(p.join(configDir, 'firebase.json'));
final passwordFile = File(p.join(configDir, 'password'));
final configFile = File(p.join(syncConfigDir, 'firebase.json'));
final passwordFile = File(p.join(syncConfigDir, 'password'));
if (!configFile.existsSync() || !passwordFile.existsSync()) {
stderr.writeln(
'Sync account requested but ~/.config/crdt-sync/ is incomplete — '

View File

@ -7,6 +7,7 @@ import 'dart:io';
import 'package:diet_guard_app/desktop/github_proxy.dart';
import 'package:diet_guard_app/desktop/wrapper_server.dart';
import 'package:crdt_sync/crdt_sync.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
@ -20,7 +21,10 @@ void main() {
late List<http.Request> outbound;
late Map<String, http.Response> canned;
Future<void> startServer() async {
Future<void> startServer({
bool serveSyncAccount = false,
String? syncConfigDir,
}) async {
outbound = [];
canned = {};
final proxy = GitHubProxy(
@ -36,6 +40,8 @@ void main() {
webRoot: webRoot,
dataDir: dataDir,
gitHubProxy: proxy,
serveSyncAccount: serveSyncAccount,
syncConfigDir: syncConfigDir,
);
await server.start(0);
}
@ -285,4 +291,59 @@ void main() {
expect(typeOf('data.bin').mimeType, 'application/octet-stream');
});
});
group('sync-account provisioning', () {
/// Writes [files] into a temp config dir and restarts with the route on.
Future<String> enableWith(Map<String, String> files) async {
final configDir = Directory(p.join(tempDir.path, 'crdt-sync'))
..createSync(recursive: true);
files.forEach((name, contents) {
File(p.join(configDir.path, name)).writeAsStringSync(contents);
});
await server.stop();
await startServer(serveSyncAccount: true, syncConfigDir: configDir.path);
return 'http://localhost:${server.port}';
}
test('is 404 when not enabled', () async {
// The default, and the whole security posture: a credential route must
// not be reachable just because the wrapper is running.
final response = await http.get(url(kSyncAccountPath));
expect(response.statusCode, HttpStatus.notFound);
});
test('serves the account when enabled', () async {
final origin = await enableWith({
'firebase.json': '{"email":"a@b.c"}',
'password': 'pw\n',
});
final response = await http.get(Uri.parse('$origin$kSyncAccountPath'));
final account = FirebaseAccount.tryParse(response.body);
expect(response.statusCode, HttpStatus.ok);
expect(account?.email, 'a@b.c');
expect(account?.password, 'pw');
});
test('is 404 when the config files are absent', () async {
final origin = await enableWith({});
final response = await http.get(Uri.parse('$origin$kSyncAccountPath'));
expect(response.statusCode, HttpStatus.notFound);
});
test('is 404 when firebase.json has no usable email', () async {
final origin = await enableWith({
'firebase.json': '{"apiKey":"x"}',
'password': 'pw',
});
final response = await http.get(Uri.parse('$origin$kSyncAccountPath'));
expect(response.statusCode, HttpStatus.notFound);
});
});
}