Bind HTTP server tests to an ephemeral port
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-11-11 11:53:35 +01:00
parent f0d4f1036c
commit eb4ce6fd68
2 changed files with 18 additions and 10 deletions

View File

@ -26,6 +26,14 @@ class HttpServerService {
HttpServer? _server;
/// Port [start] binds. Tests set 0 (ephemeral) so a host process that
/// already owns 8765 cannot make them fail or answer in our place.
@visibleForTesting
int port = kWorkoutServerPort;
/// The port actually bound, or null while stopped.
int? get boundPort => _server?.port;
/// The most recent workout JSON string (updated after each finished workout).
String? _latestJson;
@ -53,16 +61,13 @@ class HttpServerService {
if (_server != null) return; // already running
await _loadFromDisk();
try {
_server = await HttpServer.bind(
InternetAddress.anyIPv4,
kWorkoutServerPort,
);
_server = await HttpServer.bind(InternetAddress.anyIPv4, port);
unawaited(_serve());
} on SocketException catch (error) {
// Port already in use or binding failed — not fatal.
debugPrint(
'WorkoutApp: could not bind the workout HTTP server on port '
'$kWorkoutServerPort ($error) — the PC cannot pull workouts over '
'$port ($error) — the PC cannot pull workouts over '
'HTTP; ADB and sync still work.',
);
_server = null;

View File

@ -5,8 +5,11 @@ import 'package:workout_app/services/http_server_service.dart';
void main() {
group('HttpServerService', () {
// Ephemeral port: the host may already run something on 8765.
setUp(() => HttpServerService.instance.port = 0);
tearDown(() async {
await HttpServerService.instance.stop();
HttpServerService.instance.port = kWorkoutServerPort;
});
test('is a singleton', () {
@ -44,22 +47,22 @@ void main() {
});
test('start swallows a bind failure when the port is taken', () async {
final blocker = await ServerSocket.bind(
InternetAddress.anyIPv4,
kWorkoutServerPort,
);
final blocker = await ServerSocket.bind(InternetAddress.anyIPv4, 0);
HttpServerService.instance.port = blocker.port;
// Binding fails (SocketException) but start() must not throw.
await HttpServerService.instance.start();
expect(HttpServerService.instance.boundPort, isNull);
await blocker.close();
});
test('serves the latest workout over HTTP', () async {
HttpServerService.instance.resetForTesting();
await HttpServerService.instance.start();
final port = HttpServerService.instance.boundPort!;
final client = HttpClient();
Future<int> get(String path) async {
final req = await client.get('localhost', kWorkoutServerPort, path);
final req = await client.get('localhost', port, path);
final resp = await req.close();
await resp.drain<void>();
return resp.statusCode;