halkyon/test/check_files_test.dart
Tizian 「ティツィアーン」 3def5126a3
build(check): the checks compare the generated code, as CI did alone
On 23 Oct a push went red in CI because an endpoint's changed comment
hadn't been generated into the module's client: only CI compared the
committed generated code with the generator's output. tool/check.dart now
runs every server's generation (Melos' `generate`, about 15 s) after
fetching the packages and fails, naming the files, where the working
tree's generated code changed by it; the files stay written anew, to be
committed. It compares the files that differ from the committed ones
before and after by their content, so uncommitted changes count as they
do everywhere else in the checks.

It refuses a Serverpod CLI at another version than the lock holds
Serverpod at (`dart install serverpod_cli@<version>`), and the root's
tests hold CI's SERVERPOD_VERSION to the lock. CI keeps installing the
CLI at that version and puts it on the job's PATH; its own comparison
step goes, as the checks run it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-23 17:48:31 +02:00

184 lines
6.5 KiB
Dart

import 'dart:io';
import 'package:test/test.dart';
import '../tool/src/check_files.dart';
void main() {
test("our own Dart files are formatted: the apps', the shared packages', the "
"tools'", () {
for (final path in [
'apps/learn/halkyon_learn_flutter/lib/main.dart',
'apps/learn/halkyon_learn_server/lib/server.dart',
'apps/learn/halkyon_learn_server/test/integration/auth/account_test.dart',
'apps/todo/halkyon_todo_flutter/lib/main.dart',
'packages/halkyon_app/lib/halkyon_app.dart',
'packages/halkyon_base_server/lib/src/auth/email.dart',
// SRP's client half, written by hand beside the module's generated
// client, and its tests.
'packages/halkyon_base_client/lib/srp.dart',
'packages/halkyon_base_client/lib/src/srp/srp_client.dart',
'packages/halkyon_base_client/test/srp_client_test.dart',
'packages/halkyon_keys/lib/src/account_keys.dart',
'tool/check.dart',
]) {
expect(isOwnDartFile(path), isTrue, reason: path);
}
});
test('what a generator writes is left as it wrote it', () {
for (final path in [
// The client packages, Serverpod's from top to bottom, but for the
// module's SRP.
'apps/learn/halkyon_learn_client/lib/src/protocol/client.dart',
'apps/todo/halkyon_todo_client/lib/halkyon_todo_client.dart',
'packages/halkyon_base_client/lib/src/protocol/protocol.dart',
'packages/halkyon_base_client/lib/halkyon_base_client.dart',
// A server's generated sources and test tools.
'apps/learn/halkyon_learn_server/lib/src/generated/protocol.dart',
'packages/halkyon_base_server/lib/src/generated/endpoints.dart',
'apps/learn/halkyon_learn_server/test/integration/test_tools/serverpod_test_tools.dart',
// gen-l10n's localizations, the app's and the shared packages'.
'apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations.dart',
'apps/learn/halkyon_learn_flutter/lib/l10n/app_localizations_de.dart',
'packages/halkyon_ui/lib/l10n/halkyon_ui_localizations_en.dart',
'packages/halkyon_app/lib/l10n/halkyon_app_localizations.dart',
]) {
expect(isOwnDartFile(path), isFalse, reason: path);
}
});
test('the files of other languages are no Dart files', () {
expect(isOwnDartFile('apps/learn/tool/app_icons.py'), isFalse);
expect(
isOwnDartFile('packages/halkyon_app/lib/l10n/halkyon_app_en.arb'),
isFalse,
);
});
group('what changed since a commit', () {
const packages = {
'halkyon_app': 'packages/halkyon_app',
'halkyon_ui': 'packages/halkyon_ui',
'halkyon_ui_example': 'packages/halkyon_ui/example',
'halkyon_learn_flutter': 'apps/learn/halkyon_learn_flutter',
};
test('touches the packages its files are in, the innermost one', () {
expect(
packagesTouched([
'packages/halkyon_app/lib/src/app/app_frame.dart',
'packages/halkyon_app/test/app/instance_gate_test.dart',
'packages/halkyon_ui/example/lib/main.dart',
], packages),
['halkyon_app', 'halkyon_ui_example'],
);
});
test("files of no package (docs, tools, deploy) touch none: the tools' "
'own tests run anyway', () {
expect(
packagesTouched([
'docs/adr/0015-one-repository.md',
'tool/check.dart',
'deploy/compose.yaml',
], packages),
isEmpty,
);
});
test("the root's pubspec, lock or lint rules touch every package", () {
for (final path in [
'pubspec.yaml',
'pubspec.lock',
'analysis_options.yaml',
]) {
expect(packagesTouched([path], packages), isNull, reason: path);
}
});
});
test("Melos' filters take every package, or those touched and what "
'depends on them', () {
expect(melosFilters(), isEmpty);
expect(melosFilters(touched: ['halkyon_app', 'halkyon_ui']), [
'--scope=halkyon_app',
'--scope=halkyon_ui',
'--include-dependents',
]);
});
group('the generated code', () {
test("the CLI's version is the one the lock holds Serverpod at", () {
const lock = '''
packages:
serverpod:
dependency: transitive
description:
name: serverpod
sha256: "0"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
sdks:
dart: ">=3.13.0 <4.0.0"
''';
expect(lockedVersion(lock, 'serverpod'), '4.0.4');
expect(lockedVersion(lock, 'serverpod_cli'), isNull);
expect(
serverpodCliVersion('Some notice\nServerpod version: 4.0.4\n'),
'4.0.4',
);
expect(serverpodCliVersion('serverpod: command not found'), isNull);
});
test('CI installs the CLI at the version the lock holds Serverpod at, so '
"check.dart's comparison holds there too", () {
final pinned = RegExp(
r'^\s*SERVERPOD_VERSION: (\S+)$',
multiLine: true,
).firstMatch(File('.forgejo/workflows/ci.yaml').readAsStringSync());
expect(
pinned?.group(1),
lockedVersion(File('pubspec.lock').readAsStringSync(), 'serverpod'),
);
});
test('differs where the generator changed, removed or added a file', () {
final before = {
'apps/learn/halkyon_learn_server/lib/src/studyset/studyset.spy.yaml':
'a',
'apps/learn/halkyon_learn_server/lib/src/generated/protocol.dart': 'b',
};
final after = {
...before,
// Written anew from the changed model.
'apps/learn/halkyon_learn_server/lib/src/generated/protocol.dart': 'c',
// A model's class no longer generated.
'apps/learn/halkyon_learn_client/lib/src/protocol/gone.dart':
absentFile,
// A new model's class.
'apps/learn/halkyon_learn_client/lib/src/protocol/new.dart': 'd',
};
expect(differingFiles(before, after), [
'apps/learn/halkyon_learn_client/lib/src/protocol/gone.dart',
'apps/learn/halkyon_learn_client/lib/src/protocol/new.dart',
'apps/learn/halkyon_learn_server/lib/src/generated/protocol.dart',
]);
});
test('a file written back as committed differs from its change before, '
'and changes the generator left alone count for nothing', () {
expect(
differingFiles(
{
'apps/todo/halkyon_todo_client/lib/src/protocol/client.dart': 'a',
'apps/todo/halkyon_todo_server/lib/src/greeting.dart': 'b',
},
{'apps/todo/halkyon_todo_server/lib/src/greeting.dart': 'b'},
),
['apps/todo/halkyon_todo_client/lib/src/protocol/client.dart'],
);
});
});
}