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:
parent
3fc40fc395
commit
3bc47deb27
|
|
@ -38,26 +38,29 @@ Future<({HttpServer server, Completer<HttpRequest> completer})> _mockHttp() asyn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock for Component 2's /auth/get-token endpoint.
|
// Mock for Component 2's /auth/get-token endpoint.
|
||||||
// Responds to every request, completing [tokenReq] on the first one.
|
// Reads and captures the full request body; completes [tokenReq] on the first call.
|
||||||
// When [ok] is false, returns 401 with an error payload.
|
// When [ok] is false, returns 401.
|
||||||
Future<({HttpServer server, Completer<HttpRequest> tokenReq})> _mockTokenServer({
|
Future<({HttpServer server, Completer<({HttpRequest req, String rawBody})> tokenReq})>
|
||||||
|
_mockTokenServer({
|
||||||
String token = 'test-bearer-token',
|
String token = 'test-bearer-token',
|
||||||
bool ok = true,
|
bool ok = true,
|
||||||
}) async {
|
}) async {
|
||||||
final server = await HttpServer.bind('127.0.0.1', 0);
|
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 {
|
server.listen((req) async {
|
||||||
await req.drain<void>();
|
final bb = BytesBuilder(copy: false);
|
||||||
if (!c.isCompleted) c.complete(req);
|
await for (final chunk in req) bb.add(chunk);
|
||||||
final body = ok
|
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":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
|
req.response
|
||||||
..statusCode = ok ? 200 : 401
|
..statusCode = ok ? 200 : 401
|
||||||
..headers.set('content-type', 'application/json')
|
..headers.set('content-type', 'application/json')
|
||||||
..headers.set('content-length', '${body.length}')
|
..headers.set('content-length', '${resp.length}')
|
||||||
..headers.set('connection', 'close')
|
..headers.set('connection', 'close')
|
||||||
..write(body);
|
..write(resp);
|
||||||
await req.response.close();
|
await req.response.close();
|
||||||
});
|
});
|
||||||
return (server: server, tokenReq: c);
|
return (server: server, tokenReq: c);
|
||||||
|
|
@ -196,7 +199,7 @@ void main() {
|
||||||
group('HTTP routing', () {
|
group('HTTP routing', () {
|
||||||
late FilterProxy proxy;
|
late FilterProxy proxy;
|
||||||
late HttpServer comp2;
|
late HttpServer comp2;
|
||||||
late Completer<HttpRequest> comp2TokenReq;
|
late Completer<({HttpRequest req, String rawBody})> comp2TokenReq;
|
||||||
late HttpServer endpoint;
|
late HttpServer endpoint;
|
||||||
late Completer<HttpRequest> endpointReq;
|
late Completer<HttpRequest> endpointReq;
|
||||||
late HttpServer directServer;
|
late HttpServer directServer;
|
||||||
|
|
@ -242,11 +245,43 @@ void main() {
|
||||||
'GET http://127.0.0.1:${endpoint.port}/api HTTP/1.1\r\n'
|
'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',
|
'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.method, 'POST');
|
||||||
expect(req.uri.path, '/auth/get-token');
|
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 {
|
test('gated host: request goes directly to endpoint with Bearer token', () async {
|
||||||
final response = await _round(
|
final response = await _round(
|
||||||
proxy.port,
|
proxy.port,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue