mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 20:27:01 +01:00
Make the sync-account route testable, and cover every branch
Some checks are pending
release-apk / release (APK) (push) Waiting to run
Some checks are pending
release-apk / release (APK) (push) Waiting to run
The route read its own configuration from Platform.environment and $HOME, so the only branch a test could reach was "disabled" -- the serving path, the missing-config path and the malformed-email path were all unreachable without mutating the process environment. Both are now constructor parameters defaulting to the environment, so production behaviour is unchanged and tests drive all four outcomes: 404 when disabled, 200 with the account when enabled, 404 when ~/.config/crdt-sync/ is incomplete, and 404 when firebase.json has no usable email. The password's trailing newline is stripped, which the test pins. 256 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:
parent
3c6c071513
commit
be376fd240
@ -28,7 +28,20 @@ class WrapperServer {
|
||||
required this.webRoot,
|
||||
required this.backlogPath,
|
||||
required this.logPath,
|
||||
});
|
||||
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;
|
||||
@ -98,14 +111,12 @@ class WrapperServer {
|
||||
/// daemons already use, so there is one source of truth per machine rather
|
||||
/// than a per-app copy.
|
||||
Future<void> _syncAccount(HttpRequest request) async {
|
||||
if ((Platform.environment[kSyncAccountEnvVar] ?? '').isEmpty) {
|
||||
if (!serveSyncAccount) {
|
||||
request.response.statusCode = HttpStatus.notFound;
|
||||
return;
|
||||
}
|
||||
final home = Platform.environment['HOME'] ?? '';
|
||||
final configDir = p.join(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 — '
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crdt_sync/crdt_sync.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as p;
|
||||
@ -149,4 +150,66 @@ void main() {
|
||||
expect(kind('a.bin'), 'application/octet-stream');
|
||||
expect(kind('a.unknown'), 'application/octet-stream');
|
||||
});
|
||||
|
||||
group('sync-account provisioning', () {
|
||||
/// Starts a second server with provisioning enabled against [files].
|
||||
Future<String> enabledOrigin(Map<String, String> files) async {
|
||||
final configDir = Directory(p.join(root.path, 'crdt-sync'))
|
||||
..createSync(recursive: true);
|
||||
files.forEach((name, contents) {
|
||||
File(p.join(configDir.path, name)).writeAsStringSync(contents);
|
||||
});
|
||||
final enabled = WrapperServer(
|
||||
webRoot: p.join(root.path, 'web'),
|
||||
backlogPath: p.join(root.path, 'todo', 'BACKLOG.md'),
|
||||
logPath: p.join(root.path, 'state', 'todo_notes.json'),
|
||||
serveSyncAccount: true,
|
||||
syncConfigDir: configDir.path,
|
||||
);
|
||||
await enabled.start(0);
|
||||
addTearDown(enabled.stop);
|
||||
return 'http://localhost:${enabled.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 app is running.
|
||||
final response = await http.get(Uri.parse('$base$kSyncAccountPath'));
|
||||
|
||||
expect(response.statusCode, HttpStatus.notFound);
|
||||
});
|
||||
|
||||
test('serves the account when enabled', () async {
|
||||
final origin = await enabledOrigin({
|
||||
'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 enabledOrigin({});
|
||||
|
||||
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 enabledOrigin({
|
||||
'firebase.json': '{"apiKey":"x"}',
|
||||
'password': 'pw',
|
||||
});
|
||||
|
||||
final response = await http.get(Uri.parse('$origin$kSyncAccountPath'));
|
||||
|
||||
expect(response.statusCode, HttpStatus.notFound);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user