Testing is complete!! And a nice build runner script to boot

This commit is contained in:
2025-02-05 13:16:12 -07:00
parent 4864de624c
commit 76ea825509
25 changed files with 1453 additions and 103 deletions
+402
View File
@@ -0,0 +1,402 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c"
url: "https://pub.dev"
source: hosted
version: "79.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643
url: "https://pub.dev"
source: hosted
version: "7.2.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.6.0"
async:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43
url: "https://pub.dev"
source: hosted
version: "1.11.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
http:
dependency: "direct dev"
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev"
source: hosted
version: "1.3.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
source: hosted
version: "0.12.17"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
package_config:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "2.0.1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev"
source: hosted
version: "1.10.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test:
dependency: "direct dev"
description:
name: test
sha256: "8391fbe68d520daf2314121764d38e37f934c02fd7301ad18307bd93bd6b725d"
url: "https://pub.dev"
source: hosted
version: "1.25.14"
test_api:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev"
source: hosted
version: "0.7.4"
test_core:
dependency: transitive
description:
name: test_core
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
url: "https://pub.dev"
source: hosted
version: "0.6.8"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "15.0.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.0 <4.0.0"
+8
View File
@@ -0,0 +1,8 @@
name: "e2e_test_dart_runner"
environment:
sdk: ">=3.0.0 <4.0.0"
dev_dependencies:
test: ^1.24.0
http: ^1.1.0
+178
View File
@@ -0,0 +1,178 @@
// ignore_for_file: avoid_print
import 'dart:async';
import 'dart:io';
const packages = ['backend', 'frontend', 'shared_models'];
Map<int, Process> runnerWatchProcesses = {};
void main(List<String> args) async {
// Get args and create bool for if `--watch` passed in
final watch = args.contains('--watch');
if (watch) {
print('👀 Running build_runner in watch mode for all packages...\n');
} else {
print('🏃 Running build_runner in build mode for all packages...\n');
}
final projectRootPath = await Process.run(
'git',
['rev-parse', '--show-toplevel'],
).then((result) => result.stdout.toString().trim());
for (final package in packages) {
unawaited(runBuildRunner('$projectRootPath/$package', watch: watch));
}
await Future<void>.delayed(const Duration(seconds: 1));
var i = 0;
var j = 0;
while (runnerWatchProcesses.isNotEmpty) {
await Future<void>.delayed(const Duration(seconds: 1));
i++;
if (i == 10) {
if (!watch) print(' Waiting for build to finish... ${runnerWatchProcesses.keys}');
if (packages.length != runnerWatchProcesses.entries.length) {
j++;
} else {
j = 0;
}
if (j == 2) {
print(
'Missing build runners for too long in tracked processes. Something probably went wrong. Check logs and fix the script.',
);
exitRunners();
}
}
}
print('\n✅ All build_runner tasks completed successfully!');
}
Future<void> runBuildRunner(String packagePath, {required bool watch}) async {
final package = packagePath.split('/').last;
print('📦 Starting build_runner for $package...');
Future<(Process, bool)> buildRunnerFunc() => watch ? _runBuildRunnerWatch(packagePath) : _runBuildRunner(packagePath);
const maxAttempts = 5;
Future<void> attemptRecover(Object? e, int i) async {
try {
print('\n⚠️ Error in $package:\n$e\nAttempting recovery (${i + 1} / $maxAttempts)...\n');
print(' Waiting 10 seconds before retry...');
await Future<void>.delayed(const Duration(seconds: 1));
// Recovery attempt
print(' Running pub get...');
await Process.run(
package == 'frontend' ? 'flutter' : 'dart',
['pub', 'get'],
workingDirectory: packagePath,
);
print(' Retrying build_runner...');
return;
} catch (e) {
print('Error while trying to recover $package: $e');
}
}
Object? err;
for (var i = 0; i < maxAttempts; i++) {
try {
final (process, needsRestart) = await buildRunnerFunc();
final removed = runnerWatchProcesses.remove(process.pid);
assert(removed != null, true);
if (needsRestart) {
await attemptRecover(process.stderr, i);
} else {
return;
}
} catch (e) {
await attemptRecover(e, i);
}
}
print('\n❌ Failed to recover $packagePath: $err');
exitRunners();
}
Future<(Process, bool)> _runBuildRunner(String package) async {
final process = await Process.start(
'dart',
['run', 'build_runner', 'build', '--delete-conflicting-outputs'],
workingDirectory: package,
);
runnerWatchProcesses.addAll({process.pid: process});
final outputLines = <String>[];
final errorLines = <String>[];
process.stdout.transform(const SystemEncoding().decoder).listen(outputLines.add);
process.stderr.transform(const SystemEncoding().decoder).listen(errorLines.add);
final exitCode = await process.exitCode;
if (exitCode != 0) {
// Print filtered error output
final relevantErrors = errorLines
.where((line) => line.contains('ERROR') || line.contains('Exception') || line.contains('Failed'))
.join('\n');
print('Build runner failed for $package:\n$relevantErrors');
return (process, true);
}
// Print success with minimal output
final successMessage =
outputLines.where((line) => line.contains('Succeeded') || line.contains('Generated')).join('\n').trim();
print(' ${successMessage.isEmpty ? "Completed successfully" : successMessage}');
return (process, false);
}
Future<(Process, bool)> _runBuildRunnerWatch(String packagePath) async {
final package = packagePath.split('/').last;
final process = await Process.start(
'dart',
['run', 'build_runner', 'watch', '--delete-conflicting-outputs'],
workingDirectory: packagePath,
);
runnerWatchProcesses.addAll({process.pid: process});
var restart = false;
process.stdout.transform(const SystemEncoding().decoder).listen((line) {
if (line.contains('Succeeded') || line.contains('Generated')) {
print('🎯 [${package.toUpperCase()}] - ${line.trim()}');
}
if (line.contains('SEVERE')) {
print('🚩 [${package.toUpperCase()}] - ${line.trim()}');
restart = true;
}
});
process.stderr.transform(const SystemEncoding().decoder).listen((line) {
if (line.contains('ERROR') || line.contains('Exception') || line.contains('Failed')) {
print('🎯 [${package.toUpperCase()}] - ${line.trim()}');
restart = true;
}
});
final code = await process.exitCode;
if (!restart) {
restart = code != 0;
}
return (process, restart);
}
void exitRunners() {
print('❌ Killing processes');
for (final pEntry in runnerWatchProcesses.entries) {
pEntry.value.kill();
}
exit(1);
}
+76
View File
@@ -0,0 +1,76 @@
import 'dart:async';
import 'dart:io';
void main() async {
final projectRootPath = await Process.run(
'git',
['rev-parse', '--show-toplevel'],
).then((result) => result.stdout.toString().trim());
final backendPath = '$projectRootPath/backend';
// Start the server
print('Starting Dart Frog server...');
final build = await Process.start(
'dart_frog',
['build'],
workingDirectory: backendPath,
mode: ProcessStartMode.inheritStdio,
);
List<String> serverLogs = [];
final server = await Process.start('dart', ['build/bin/server.dart'], workingDirectory: backendPath);
server.stdout.transform(const SystemEncoding().decoder).listen((line) => serverLogs.add(line));
server.stderr.transform(const SystemEncoding().decoder).listen((line) => serverLogs.add(line));
// Wait for server to be ready
print('Waiting for server to be ready...');
await _waitForServer();
print('Running tests...');
try {
// Run the tests
final testResult = await Process.run('dart', ['test', '$backendPath/test_e2e/tests/']);
stdout.write(testResult.stdout);
stderr.write(testResult.stderr);
// Kill the server regardless of test result
server.kill();
if (testResult.exitCode != 0) {
final sub = serverLogs.length > 10 ? serverLogs.length - 10 : 0;
stdout.write("Server logs:\n${serverLogs.sublist(sub).join('\n')}");
}
// Exit with the same code as the test process
exit(testResult.exitCode);
} catch (e) {
print('Error running tests: $e');
build.kill();
server.kill();
exit(1);
}
}
Future<void> _waitForServer() async {
final client = HttpClient();
const maxAttempts = 30; // 30 seconds timeout
var attempts = 0;
while (attempts < maxAttempts) {
try {
final request =
await client.getUrl(Uri.parse('http://localhost:8080/health')).timeout(const Duration(seconds: 1));
final response = await request.close();
await response.drain<void>();
if (response.statusCode == 200) {
print('Server is ready!');
return;
}
} catch (e) {
attempts++;
await Future.delayed(const Duration(seconds: 1));
}
}
throw Exception('Server failed to start after $maxAttempts seconds');
}