import 'dart:io'; import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; import '../../tool/src/images.dart'; /// Meditate's server beside the instance (Halkyon Meditate's ADR 0038, step /// 7; ADR 0039, D14), as Todo's: a member of the register, its own database /// and role, its site, and the operator's library read-only (D16). void main() { YamlMap services(String path) => (loadYaml(File(path).readAsStringSync()) as YamlMap)['services'] as YamlMap; String read(String path) => File(path).readAsStringSync(); group('its server', () { final server = services('deploy/compose.meditate.yaml')['meditate-server'] as YamlMap; final environment = server['environment'] as YamlMap; test('signs in through the register, with the instance secret', () { expect(environment['ACCOUNT_REGISTER_URL'], 'http://server:8082/'); expect( environment['SERVERPOD_PASSWORD_instanceSecret'], r'${INSTANCE_SECRET:?set INSTANCE_SECRET in deploy/.env}', ); }); test('has a database password and pepper of its own', () { expect( environment['SERVERPOD_DATABASE_PASSWORD'], startsWith(r'${MEDITATE_POSTGRES_PASSWORD:?'), ); expect( environment['SERVERPOD_PASSWORD_serverSideSessionKeyHashPepper'], startsWith(r'${MEDITATE_SESSION_KEY_HASH_PEPPER:?'), ); }); test('names its own domain as its public address', () { for (final variable in [ 'SERVERPOD_API_SERVER_PUBLIC_HOST', 'SERVERPOD_WEB_SERVER_PUBLIC_HOST', ]) { expect(environment[variable], startsWith(r'${MEDITATE_DOMAIN:?')); } }); test('has no library unless its own file mounts one', () { expect(environment.keys, isNot(contains('MEDITATE_LIBRARY_DIR'))); expect(server['volumes'], isNull); }); test("is told to Learn's register as a member", () { final learn = services('deploy/compose.meditate.yaml')['server'] as YamlMap; expect( (learn['environment'] as YamlMap)['INSTANCE_MEMBER_MEDITATE'], 'http://meditate-server:8082/', ); }); test('connects to its own database as its own role', () { final config = loadYaml( read('apps/meditate/halkyon_meditate_server/config/production.yaml'), ) as YamlMap; final database = config['database'] as YamlMap; expect(database['name'], 'halkyon_meditate'); expect(database['user'], 'halkyon_meditate'); expect(database['host'], 'postgres'); expect((config['sessionLogs'] as YamlMap)['persistentEnabled'], isFalse); expect(config.keys, isNot(contains('insightsServer'))); }); }); group("the operator's library (D16)", () { final server = services('deploy/compose.meditate-library.yaml')['meditate-server'] as YamlMap; test('is mounted read-only where the server reads it', () { expect(server['volumes'], [ r'${MEDITATE_LIBRARY:?set MEDITATE_LIBRARY in deploy/.env}:/library:ro', ]); expect( (server['environment'] as YamlMap)['MEDITATE_LIBRARY_DIR'], '/library', ); }); test('is checked by a program in the image', () { final dockerfile = read( 'apps/meditate/halkyon_meditate_server/Dockerfile', ); expect(dockerfile, contains('--target bin/check_library.dart')); expect(dockerfile, contains('bin/check_library')); }); }); group('its site', () { // The site's directives, without its comments. final site = read('deploy/sidecars/meditate.caddy') .split('\n') .where((line) => !line.trim().startsWith('#')) .join('\n'); test("sends the library's files to the web server, everything else to " 'the API server', () { expect(site, contains('handle /library/* {')); expect(site, contains('reverse_proxy meditate-server:8082')); expect(site, contains('reverse_proxy meditate-server:8080')); }); test("never passes on the calls between the instance's services", () { expect(site, isNot(contains('/halkyon-base'))); expect( RegExp('reverse_proxy meditate-server:8082').allMatches(site), hasLength(1), ); }); test('is mounted into Caddy by the compose file', () { final caddy = services('deploy/compose.meditate.yaml')['caddy'] as YamlMap; expect( caddy['volumes'], contains( './sidecars/meditate.caddy:/etc/caddy/sidecars/meditate.caddy:ro', ), ); }); }); group('the deploy and the backups', () { test('build, dump, migrate and wait for it as for Todo', () { expect( read('deploy/redeploy.sh'), contains('for app in train todo meditate; do'), ); }); test('back its database up beside the others', () { expect( read('deploy/backup.sh'), contains('for app in train todo meditate; do'), ); }); test("have tool/check.dart replay its image's resolution", () { expect( dartImages, contains('apps/meditate/halkyon_meditate_server/Dockerfile'), ); }); }); group("every app's server names the instance's domain, not halkyon.eu's", () { for (final (file, service, variable) in [ ('compose.learn.yaml', 'server', 'LEARN_PUBLIC_HOST'), ('compose.train.yaml', 'train-server', 'TRAIN_DOMAIN'), ('compose.todo.yaml', 'todo-server', 'TODO_DOMAIN'), ('compose.meditate.yaml', 'meditate-server', 'MEDITATE_DOMAIN'), ]) { test(service, () { final environment = (services('deploy/$file')[service] as YamlMap)['environment'] as YamlMap; for (final key in [ 'SERVERPOD_API_SERVER_PUBLIC_HOST', 'SERVERPOD_WEB_SERVER_PUBLIC_HOST', ]) { expect(environment[key], startsWith('\${$variable')); } }); } }); }