halkyon/test/deploy/halkyon_li_env_test.dart

142 lines
4.8 KiB
Dart
Raw Permalink Normal View History

import 'dart:io';
import 'package:test/test.dart';
/// halkyon.li's first `.env` (ADR 0039; the developer's choices of 8 Nov 2026):
/// every secret made fresh on the host, halkyon.li's settings, and never an existing
/// file overwritten, as the session peppers must never change once people sign in.
void main() {
const script = 'deploy/host/halkyon-li-env.sh';
late Directory folder;
setUp(() => folder = Directory.systemTemp.createTempSync('halkyon-li-env'));
tearDown(() => folder.deleteSync(recursive: true));
ProcessResult make(String target) => Process.runSync('sh', [script, target]);
Map<String, String> settings(String text) => {
for (final line in text.split('\n'))
if (line.isNotEmpty && !line.startsWith('#'))
line.substring(0, line.indexOf('=')): line.substring(
line.indexOf('=') + 1,
),
};
group('a new .env', () {
late Map<String, String> env;
late File file;
setUp(() {
file = File('${folder.path}/.env');
final result = make(file.path);
expect(result.exitCode, 0, reason: '${result.stderr}');
env = settings(file.readAsStringSync());
});
test('is readable by its owner alone', () {
expect(file.statSync().modeString(), 'rw-------');
});
test("makes every secret fresh, each of 32 bytes, none another's", () {
const secrets = [
'INSTANCE_SECRET',
'LEARN_POSTGRES_PASSWORD',
'LEARN_SESSION_KEY_HASH_PEPPER',
'SIGN_IN_DECOY_KEY',
'TRAIN_POSTGRES_PASSWORD',
'TRAIN_SESSION_KEY_HASH_PEPPER',
'MAP_KEY_SECRET',
'TODO_POSTGRES_PASSWORD',
'TODO_SESSION_KEY_HASH_PEPPER',
'MEDITATE_POSTGRES_PASSWORD',
'MEDITATE_SESSION_KEY_HASH_PEPPER',
];
for (final name in secrets) {
expect(env[name], matches(RegExp(r'^[0-9a-f]{64}$')), reason: name);
}
expect(
secrets.map((name) => env[name]).toSet(),
hasLength(secrets.length),
);
});
test('differs from the next one made', () {
final other = File('${folder.path}/other.env');
make(other.path);
expect(
settings(other.readAsStringSync())['INSTANCE_SECRET'],
isNot(env['INSTANCE_SECRET']),
);
});
test("serves halkyon.li's names, handles on halkyon.li itself (D7)", () {
expect(env['LEARN_DOMAIN'], 'learn.halkyon.li');
expect(env['LEARN_PUBLIC_HOST'], 'learn.halkyon.li');
expect(env['ACCOUNT_DOMAIN'], 'halkyon.li');
expect(env['TRAIN_DOMAIN'], 'train.halkyon.li');
expect(env['TODO_DOMAIN'], 'todo.halkyon.li');
expect(env['MEDITATE_DOMAIN'], 'meditate.halkyon.li');
expect(env['JELLYFIN_DOMAIN'], 'jellyfin.halkyon.li');
});
test('lets people in through SimpleLogin alone, once an operator '
'approves them, and mails nothing', () {
expect(env['SIGN_IN_WAYS'], 'simplelogin');
expect(env['SIGN_UP'], 'approval');
expect(env['SIMPLELOGIN_CLIENT_ID'], isEmpty);
expect(env['SIMPLELOGIN_CLIENT_SECRET'], isEmpty);
for (final name in [
'EMAIL_SIGN_IN_PEPPER',
'EMAIL_SMTP_HOST',
'TOTP_KEY',
'RECOVERY_CODE_KEY',
]) {
expect(env[name] ?? '', isEmpty, reason: name);
}
});
test('runs everything chosen, behind the forward, each file there', () {
final files = env['COMPOSE_FILE']!.split(':');
expect(files, [
'compose.learn.yaml',
'compose.jellyfin.yaml',
'compose.languagetool.yaml',
'compose.train.yaml',
'compose.maps.yaml',
'compose.search.yaml',
'compose.todo.yaml',
'compose.todo-search.yaml',
'compose.meditate.yaml',
'compose.forwarded.yaml',
]);
for (final name in files) {
expect(File('deploy/$name').existsSync(), isTrue, reason: name);
}
});
test("keeps Train's map and routes to southern Bavaria, its search to "
'Germany, in memory beside the rest', () {
expect(env['MAP_REGION'], '10,47.2,13.9,49');
expect(env['BROUTER_REGION'], '10,45,15,50');
expect(env['PHOTON_REGION'], 'europe/germany');
expect(env['PHOTON_HEAP'], '2g');
expect(env['PHOTON_MEMORY'], '3g');
});
test('says Germany for the data and the backups, and reads the library '
'from the HDD', () {
expect(env['DATA_LOCATION'], 'DE');
expect(env['BACKUP_LOCATION'], 'DE');
expect(env['JELLYFIN_MEDIA'], '/data/media');
expect(env.containsKey('MEDITATE_LIBRARY'), isFalse);
});
});
test('never overwrites an existing .env, whose peppers must stay', () {
final file = File('${folder.path}/.env')..writeAsStringSync('KEPT=1\n');
final result = make(file.path);
expect(result.exitCode, isNot(0));
expect(result.stderr, contains('exists'));
expect(file.readAsStringSync(), 'KEPT=1\n');
});
}