Extend filter_proxy tests: verify /auth/get-token binding fields

- _mockTokenServer now reads and captures the request body instead of
  draining it — Completer type updated to ({HttpRequest, String rawBody})
- Two new tests: assert that url, method, nonce are present in the
  /auth/get-token request body; verify POST requests carry method=POST

48/48 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Morten V. Christiansen 2026-05-08 12:05:46 +02:00
parent 3fc40fc395
commit 3bc47deb27
1 changed files with 47 additions and 12 deletions

View File

@ -38,26 +38,29 @@ Future<({HttpServer server, Completer<HttpRequest> completer})> _mockHttp() asyn
}
// Mock for Component 2's /auth/get-token endpoint.
// Responds to every request, completing [tokenReq] on the first one.
// When [ok] is false, returns 401 with an error payload.
Future<({HttpServer server, Completer<HttpRequest> tokenReq})> _mockTokenServer({
// Reads and captures the full request body; completes [tokenReq] on the first call.
// When [ok] is false, returns 401.
Future<({HttpServer server, Completer<({HttpRequest req, String rawBody})> tokenReq})>
_mockTokenServer({
String token = 'test-bearer-token',
bool ok = true,
}) async {
final server = await HttpServer.bind('127.0.0.1', 0);
final c = Completer<HttpRequest>();
final c = Completer<({HttpRequest req, String rawBody})>();
server.listen((req) async {
await req.drain<void>();
if (!c.isCompleted) c.complete(req);
final body = ok
final bb = BytesBuilder(copy: false);
await for (final chunk in req) bb.add(chunk);
final rawBody = utf8.decode(bb.takeBytes());
if (!c.isCompleted) c.complete((req: req, rawBody: rawBody));
final resp = ok
? '{"ok":true,"token":"$token","expires_in":300}'
: '{"ok":false,"error":"no active session","login_required":true}';
: '{"ok":false,"error":"card not available","login_required":true}';
req.response
..statusCode = ok ? 200 : 401
..headers.set('content-type', 'application/json')
..headers.set('content-length', '${body.length}')
..headers.set('content-length', '${resp.length}')
..headers.set('connection', 'close')
..write(body);
..write(resp);
await req.response.close();
});
return (server: server, tokenReq: c);
@ -196,7 +199,7 @@ void main() {
group('HTTP routing', () {
late FilterProxy proxy;
late HttpServer comp2;
late Completer<HttpRequest> comp2TokenReq;
late Completer<({HttpRequest req, String rawBody})> comp2TokenReq;
late HttpServer endpoint;
late Completer<HttpRequest> endpointReq;
late HttpServer directServer;
@ -242,11 +245,43 @@ void main() {
'GET http://127.0.0.1:${endpoint.port}/api HTTP/1.1\r\n'
'Host: 127.0.0.1:${endpoint.port}\r\n\r\n',
);
final req = await comp2TokenReq.future.timeout(_kTimeout);
final (:req, rawBody: _) = await comp2TokenReq.future.timeout(_kTimeout);
expect(req.method, 'POST');
expect(req.uri.path, '/auth/get-token');
});
test('gated host: /auth/get-token body carries url, method, nonce', () async {
await _round(
proxy.port,
'GET http://127.0.0.1:${endpoint.port}/api?x=1 HTTP/1.1\r\n'
'Host: 127.0.0.1:${endpoint.port}\r\n\r\n',
);
final (:req, :rawBody) = await comp2TokenReq.future.timeout(_kTimeout);
expect(req.uri.path, '/auth/get-token');
final body = jsonDecode(rawBody) as Map<String, dynamic>;
expect(body['url'], contains('/api?x=1'));
expect(body['method'], 'GET');
expect(body['nonce'], isA<String>());
expect((body['nonce'] as String).length, greaterThan(8));
});
test('gated POST: /auth/get-token body carries method POST', () async {
const postBody = '{"key":"val"}';
await _round(
proxy.port,
'POST http://127.0.0.1:${endpoint.port}/submit HTTP/1.1\r\n'
'Host: 127.0.0.1:${endpoint.port}\r\n'
'Content-Type: application/json\r\n'
'Content-Length: ${postBody.length}\r\n\r\n'
'$postBody',
);
final (:req, :rawBody) = await comp2TokenReq.future.timeout(_kTimeout);
expect(req.uri.path, '/auth/get-token');
final body = jsonDecode(rawBody) as Map<String, dynamic>;
expect(body['method'], 'POST');
expect(body['url'], contains('/submit'));
});
test('gated host: request goes directly to endpoint with Bearer token', () async {
final response = await _round(
proxy.port,