halkyon_base runs as the instance's register or as one of its members. The register keeps the ways in, handles and profiles; members ask it over HTTP with the instance's shared secret (instanceSecret), and open sessions of their own for its accounts, under the same account ID. - AccountsMode from the environment: INSTANCE_MEMBERS on the register, ACCOUNT_REGISTER_URL on a member. Learn always holds the register (Explore joins the profiles); Todo is a member where it is set. - AccountRegister: LocalRegister, RemoteRegister, an in-memory one in tests. Calls between services on the web server, refused without the secret (constant-time compare); a server needing it doesn't start without it. - Deleting an account deletes it everywhere: notices per member kept in the deletion's transaction (account_deletion_notice), told at once and hourly until confirmed; members prune daily whom the register no longer knows, never while it knows none of them or can't be reached. - Deploy: INSTANCE_SECRET and INSTANCE_MEMBERS in compose and .env. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
69 lines
2 KiB
Dart
69 lines
2 KiB
Dart
import 'package:halkyon_base_server/base.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('The accounts mode from the environment', () {
|
|
test('without settings, the server holds its own register, with no '
|
|
'members', () {
|
|
final mode = AccountsMode.fromEnvironment(const {});
|
|
|
|
expect(mode, isA<HoldsRegister>());
|
|
expect((mode as HoldsRegister).members, isEmpty);
|
|
});
|
|
|
|
test('the register tells the members it names, separated by commas or '
|
|
'spaces', () {
|
|
final mode = AccountsMode.fromEnvironment(const {
|
|
'INSTANCE_MEMBERS': ' https://todo.example.com, http://todo:8082 ',
|
|
});
|
|
|
|
expect((mode as HoldsRegister).members, [
|
|
Uri.parse('https://todo.example.com'),
|
|
Uri.parse('http://todo:8082'),
|
|
]);
|
|
});
|
|
|
|
test('a register named makes the server its member', () {
|
|
final mode = AccountsMode.fromEnvironment(const {
|
|
'ACCOUNT_REGISTER_URL': 'http://learn:8082',
|
|
'INSTANCE_MEMBERS': 'http://ignored',
|
|
});
|
|
|
|
expect(
|
|
(mode as MemberOfRegister).register,
|
|
Uri.parse('http://learn:8082'),
|
|
);
|
|
});
|
|
|
|
test('empty values count as unset, as Compose passes them on', () {
|
|
expect(
|
|
AccountsMode.fromEnvironment(const {
|
|
'ACCOUNT_REGISTER_URL': '',
|
|
'INSTANCE_MEMBERS': ' ',
|
|
}),
|
|
isA<HoldsRegister>(),
|
|
);
|
|
});
|
|
|
|
test('anything but an HTTP(S) address is refused, not guessed at', () {
|
|
expect(
|
|
() => AccountsMode.fromEnvironment(const {
|
|
'ACCOUNT_REGISTER_URL': 'learn:8082',
|
|
}),
|
|
throwsArgumentError,
|
|
);
|
|
expect(
|
|
() => AccountsMode.fromEnvironment(const {
|
|
'INSTANCE_MEMBERS': 'ftp://todo',
|
|
}),
|
|
throwsArgumentError,
|
|
);
|
|
});
|
|
});
|
|
|
|
test('the shared secret is the same only when it is', () {
|
|
expect(isInstanceSecret('s3cret', 's3cret'), isTrue);
|
|
expect(isInstanceSecret('s3cret!', 's3cret'), isFalse);
|
|
expect(isInstanceSecret('', 's3cret'), isFalse);
|
|
});
|
|
}
|