halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps

What every Halkyon app's shell needs, taken from Halkyon Learn's and set
on halkyon_base's client: the instances a device keeps an account on,
the first start's tiles and where an instance keeps people's data, the
account switcher; signing in anonymously, with GitHub or an email code,
switching person and the start screen; the gates that build an app's
dependencies per instance and per person; and the sync service with its
status, Settings card and banner. Texts of its own in English and
German take the app's name; what only the app can say, it passes in.
Fakes for apps' tests are in testing.dart.

An instance's basics now name the app it serves (`app`, set by the
host's configuration), and an app accepts only an instance of its own.
SerialQueue and CoalescingRunner move into halkyon_core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-19 15:18:49 +02:00
commit e23cc77f7c
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,48 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_app/halkyon_app.dart';
void main() {
late Directory root;
late PersonFolders folders;
setUp(() async {
root = await Directory.systemTemp.createTemp('people_');
folders = PersonFolders(root);
});
tearDown(() => root.delete(recursive: true));
Future<void> write(String person) async {
final folder = await Directory('${root.path}/$person').create();
await File('${folder.path}/sync.sqlite').writeAsString(person);
}
Future<List<String>> people() async => [
for (final entry in root.listSync())
entry.uri.pathSegments.lastWhere((segment) => segment.isNotEmpty),
]..sort();
test("opening a person's folder wipes everyone else's", () async {
await write('anonymous-1');
await write('github-1');
final folder = await folders.openOnly('github-1');
expect(await people(), ['github-1']);
expect(
await File('${folder.path}/sync.sqlite').readAsString(),
'github-1',
reason: "the person's own data stays",
);
});
test('creates the folder of a person new to this device', () async {
await write('anonymous-1');
final folder = await folders.openOnly('anonymous-2');
expect(folder.existsSync(), isTrue);
expect(await people(), ['anonymous-2']);
});
}