Skip to content

Commit 0ee1422

Browse files
authored
fix(dart_frog): Pipeline does not maintain RequestContext (#605)
1 parent fca92f8 commit 0ee1422

File tree

6 files changed

+96
-84
lines changed

6 files changed

+96
-84
lines changed

examples/counter/test/routes/_middleware_test.dart

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,22 @@ class _MockRequestContext extends Mock implements RequestContext {}
99
void main() {
1010
group('middleware', () {
1111
test('provides incremented count', () async {
12-
int? count;
13-
final handler = middleware(
14-
(context) {
15-
count = context.read<int>();
16-
return Response(body: '');
17-
},
18-
);
12+
final handler = middleware((context) => Response());
1913
final request = Request.get(Uri.parse('http://localhost/'));
2014
final context = _MockRequestContext();
15+
2116
when(() => context.request).thenReturn(request);
17+
when(() => context.provide<int>(any())).thenReturn(context);
2218

2319
await handler(context);
24-
expect(count, equals(1));
2520

26-
await handler(context);
27-
expect(count, equals(2));
21+
final create = verify(() => context.provide<int>(captureAny()))
22+
.captured
23+
.single as int Function();
2824

29-
await handler(context);
30-
expect(count, equals(3));
25+
expect(create(), equals(1));
26+
expect(create(), equals(2));
27+
expect(create(), equals(3));
3128
});
3229
});
3330
}

examples/kitchen_sink/test/routes/_middleware_test.dart

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,20 @@ class _MockRequestContext extends Mock implements RequestContext {}
99
void main() {
1010
group('middleware', () {
1111
test('provides greeting', () async {
12-
String? greeting;
13-
final handler = middleware(
14-
(context) {
15-
greeting = context.read<String>();
16-
return Response(body: '');
17-
},
18-
);
12+
final handler = middleware((_) => Response());
1913
final request = Request.get(Uri.parse('http://localhost/'));
2014
final context = _MockRequestContext();
15+
2116
when(() => context.request).thenReturn(request);
17+
when(() => context.provide<String>(any())).thenReturn(context);
2218

2319
await handler(context);
24-
expect(greeting, equals('Hello'));
20+
21+
final create = verify(() => context.provide<String>(captureAny()))
22+
.captured
23+
.single as String Function();
24+
25+
expect(create(), equals('Hello'));
2526
});
2627
});
2728
}

examples/web_socket_counter/test/routes/_middleware_test.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ class _MockRequestContext extends Mock implements RequestContext {}
1010
void main() {
1111
group('middleware', () {
1212
test('provides a CounterCubit instance.', () async {
13-
CounterCubit? cubit;
14-
final handler = middleware(
15-
(context) {
16-
cubit = context.read<CounterCubit>();
17-
return Response();
18-
},
19-
);
13+
final handler = middleware((_) => Response());
2014
final request = Request.get(Uri.parse('http://localhost/'));
2115
final context = _MockRequestContext();
16+
2217
when(() => context.request).thenReturn(request);
18+
when(() => context.provide<CounterCubit>(any())).thenReturn(context);
2319

2420
await handler(context);
25-
expect(cubit, isNotNull);
21+
22+
final create = verify(() => context.provide<CounterCubit>(captureAny()))
23+
.captured
24+
.single as CounterCubit Function();
25+
expect(create(), isA<CounterCubit>());
2626
});
2727
});
2828
}

packages/dart_frog/lib/src/pipeline.dart

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,28 @@ part of '_internal.dart';
66
/// {@endtemplate}
77
class Pipeline {
88
/// {@macro pipeline}
9-
const Pipeline() : this._(const shelf.Pipeline());
10-
11-
const Pipeline._(this._pipeline);
12-
13-
final shelf.Pipeline _pipeline;
9+
const Pipeline();
1410

1511
/// Returns a new [Pipeline] with [middleware] added to the existing set of
1612
/// [Middleware].
1713
///
1814
/// [middleware] will be the last [Middleware] to process a request and
1915
/// the first to process a response.
20-
Pipeline addMiddleware(Middleware middleware) {
21-
return Pipeline._(
22-
_pipeline.addMiddleware((innerHandler) {
23-
return (request) async {
24-
final response = await middleware(
25-
(context) async {
26-
final response = await innerHandler(context.request._request);
27-
return Response._(response);
28-
},
29-
)(RequestContext._(request));
30-
return response._response;
31-
};
32-
}),
33-
);
34-
}
16+
Pipeline addMiddleware(Middleware middleware) =>
17+
_Pipeline(middleware, addHandler);
3518

3619
/// Returns a new [Handler] with [handler] as the final processor of a
3720
/// [Request] if all of the middleware in the pipeline have passed the request
3821
/// through.
39-
Handler addHandler(Handler handler) {
40-
return (context) async {
41-
final response = await _pipeline.addHandler((request) async {
42-
final context = RequestContext._(request);
43-
final response = await handler(context);
44-
return response._response;
45-
})(context.request._request);
46-
return Response._(response);
47-
};
48-
}
22+
Handler addHandler(Handler handler) => handler;
23+
}
24+
25+
class _Pipeline extends Pipeline {
26+
_Pipeline(this._middleware, this._parent);
27+
28+
final Middleware _middleware;
29+
final Middleware _parent;
30+
31+
@override
32+
Handler addHandler(Handler handler) => _parent(_middleware(handler));
4933
}

packages/dart_frog/test/src/middleware_test.dart

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:io';
22

33
import 'package:dart_frog/dart_frog.dart';
4+
import 'package:http/http.dart' as http;
45
import 'package:mocktail/mocktail.dart';
56
import 'package:test/test.dart';
67

@@ -31,16 +32,14 @@ void main() {
3132
final handler =
3233
const Pipeline().addMiddleware(middleware).addHandler(onRequest);
3334

34-
final request = Request.get(Uri.parse('http://localhost/'));
35-
final context = _MockRequestContext();
36-
when(() => context.request).thenReturn(request);
37-
final response = await handler(context);
35+
final server = await serve(handler, 'localhost', 3020);
36+
final client = http.Client();
37+
final response = await client.get(Uri.parse('http://localhost:3020/'));
3838

3939
await expectLater(response.statusCode, equals(HttpStatus.ok));
40-
await expectLater(
41-
await response.body(),
42-
equals('$stringValue $intValue'),
43-
);
40+
await expectLater(response.body, equals('$stringValue $intValue'));
41+
42+
await server.close();
4443
});
4544

4645
test('middleware can be used to read the request body', () async {
@@ -119,4 +118,27 @@ void main() {
119118
expect(response.statusCode, equals(HttpStatus.ok));
120119
expect(response.body(), completion(equals(body)));
121120
});
121+
122+
test('chaining middleware retains request context', () async {
123+
const value = 'test-value';
124+
Middleware noop() => (handler) => (context) => handler(context);
125+
126+
Future<Response> onRequest(RequestContext context) async {
127+
final value = context.read<String>();
128+
return Response(body: value);
129+
}
130+
131+
final handler =
132+
const Pipeline().addMiddleware(noop()).addHandler(onRequest);
133+
final request = Request.get(Uri.parse('http://localhost/'));
134+
final context = _MockRequestContext();
135+
136+
when(() => context.read<String>()).thenReturn(value);
137+
when(() => context.request).thenReturn(request);
138+
139+
final response = await handler(context);
140+
141+
expect(response.statusCode, equals(HttpStatus.ok));
142+
expect(response.body(), completion(equals(value)));
143+
});
122144
}

packages/dart_frog/test/src/provider_test.dart

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import 'dart:io';
22

33
import 'package:dart_frog/dart_frog.dart';
4-
import 'package:mocktail/mocktail.dart';
4+
import 'package:http/http.dart' as http;
55
import 'package:test/test.dart';
66

7-
class _MockRequestContext extends Mock implements RequestContext {}
8-
97
void main() {
108
test('values can be provided and read via middleware', () async {
119
const value = '__test_value__';
@@ -21,13 +19,14 @@ void main() {
2119
final handler =
2220
const Pipeline().addMiddleware(middleware).addHandler(onRequest);
2321

24-
final request = Request.get(Uri.parse('http://localhost/'));
25-
final context = _MockRequestContext();
26-
when(() => context.request).thenReturn(request);
27-
final response = await handler(context);
22+
final server = await serve(handler, 'localhost', 3010);
23+
final client = http.Client();
24+
final response = await client.get(Uri.parse('http://localhost:3010/'));
2825

2926
await expectLater(response.statusCode, equals(HttpStatus.ok));
30-
await expectLater(await response.body(), equals(value));
27+
await expectLater(response.body, equals(value));
28+
29+
await server.close();
3130
});
3231

3332
test('descendant providers can access provided values', () async {
@@ -46,29 +45,38 @@ void main() {
4645
final handler =
4746
const Pipeline().addMiddleware(middleware).addHandler(onRequest);
4847

49-
final request = Request.get(Uri.parse('http://localhost/'));
50-
final context = _MockRequestContext();
51-
when(() => context.request).thenReturn(request);
52-
final response = await handler(context);
48+
final server = await serve(handler, 'localhost', 3011);
49+
final client = http.Client();
50+
final response = await client.get(Uri.parse('http://localhost:3011/'));
5351

5452
await expectLater(response.statusCode, equals(HttpStatus.ok));
55-
await expectLater(await response.body(), equals(url));
53+
await expectLater(response.body, equals(url));
54+
55+
await server.close();
5656
});
5757

5858
test('A StateError is thrown when reading an un-provided value', () async {
59+
Object? exception;
5960
Response onRequest(RequestContext context) {
60-
context.read<Uri>();
61+
try {
62+
context.read<Uri>();
63+
} catch (e) {
64+
exception = e;
65+
}
6166
return Response();
6267
}
6368

6469
final handler = const Pipeline()
6570
.addMiddleware((handler) => handler)
6671
.addHandler(onRequest);
6772

68-
final request = Request.get(Uri.parse('http://localhost/'));
69-
final context = _MockRequestContext();
70-
when(() => context.request).thenReturn(request);
73+
final server = await serve(handler, 'localhost', 3012);
74+
final client = http.Client();
75+
final response = await client.get(Uri.parse('http://localhost:3012/'));
76+
77+
await expectLater(response.statusCode, equals(HttpStatus.ok));
78+
expect(exception, isA<StateError>());
7179

72-
await expectLater(() => handler(context), throwsStateError);
80+
await server.close();
7381
});
7482
}

0 commit comments

Comments
 (0)