Skip to content

Commit 031e462

Browse files
fix(#233): change registration order to ensure composed providers are available in the request
1 parent 33b889d commit 031e462

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

packages/serinus/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 2.1.7
4+
5+
**Released on:** 14-03-2026
6+
7+
### Fixes
8+
9+
- Fix a bug that prevented composed providers to be correctly registered in the route context. This bug caused the composed providers to not be available in the route context, which could lead to issues with dependency injection and route handling. This fix ensures that composed providers are correctly registered and available in the route context, allowing for proper dependency injection and route handling when using composed providers in Serinus applications. (#233)[https://github.com/francescovallone/serinus/issues/233]
10+
311
## 2.1.6
412

513
**Released on:** 12-03-2026

packages/serinus/lib/src/containers/serinus_container.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ class SerinusContainer {
7575
await modulesContainer.registerModules(entrypoint);
7676
modulesContainer.addEntrypointToInternalCoreModule(internalCoreModule);
7777
}
78-
routesResolver?.resolve();
7978
await modulesContainer.finalize(entrypoint);
79+
routesResolver?.resolve();
8080
inspector.inspectModules(modulesContainer.scopes);
8181
await emitHook<OnApplicationBootstrap>();
8282
}

packages/serinus/test/containers/composed_module_test.dart

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:io';
2+
13
import 'package:mocktail/mocktail.dart';
24
import 'package:serinus/serinus.dart';
35
import 'package:test/test.dart';
@@ -12,6 +14,33 @@ class _MockAdapter extends Mock implements HttpAdapter {
1214
String get name => 'http';
1315
}
1416

17+
class _MockIncomingMessage extends Mock implements IncomingMessage {}
18+
19+
class _MockHttpHeaders extends Mock implements HttpHeaders {}
20+
21+
Request _buildRequest() {
22+
final incoming = _MockIncomingMessage();
23+
when(() => incoming.queryParameters).thenReturn({});
24+
when(() => incoming.headers).thenReturn(SerinusHeaders(_MockHttpHeaders()));
25+
when(() => incoming.contentType).thenReturn(ContentType.json);
26+
when(() => incoming.method).thenReturn('GET');
27+
when(() => incoming.path).thenReturn('/composed');
28+
when(() => incoming.uri).thenReturn(Uri.parse('http://localhost/composed'));
29+
when(() => incoming.id).thenReturn('req-composed');
30+
when(() => incoming.host).thenReturn('localhost');
31+
when(() => incoming.hostname).thenReturn('localhost');
32+
when(() => incoming.port).thenReturn(3000);
33+
when(() => incoming.cookies).thenReturn(const []);
34+
when(() => incoming.segments).thenReturn(const []);
35+
when(() => incoming.clientInfo).thenReturn(null);
36+
when(() => incoming.contentLength).thenReturn(0);
37+
when(() => incoming.isWebSocket).thenReturn(false);
38+
when(() => incoming.webSocketKey).thenReturn('');
39+
when(() => incoming.body()).thenReturn('');
40+
when(() => incoming.json()).thenReturn(null);
41+
return Request(incoming);
42+
}
43+
1544
// Define a provider type that will be produced by a composed provider
1645
class ProducedProvider extends Provider {
1746
ProducedProvider();
@@ -364,4 +393,37 @@ void main() {
364393
},
365394
);
366395
});
396+
397+
group('ComposedProvider in RequestContext', () {
398+
test('composed providers are available in RequestContext', () async {
399+
final composedA = Module.composed<Module>(
400+
(CompositionContext ctx) async => ProducedModuleA(),
401+
inject: const [],
402+
);
403+
final parent = ParentModule([composedA]);
404+
405+
final container = ModulesContainer(
406+
ApplicationConfig(serverAdapter: _MockAdapter()),
407+
);
408+
await container.registerModules(parent);
409+
await container.finalize(parent);
410+
411+
final scope = container.getScopeByProvider(ProducedProvider);
412+
final providers = <Type, Provider>{
413+
for (final provider in scope.unifiedProviders)
414+
provider.runtimeType: provider,
415+
};
416+
417+
final context = RequestContext<dynamic>.withBody(
418+
_buildRequest(),
419+
null,
420+
providers,
421+
scope.unifiedValues,
422+
const <Type, Object>{},
423+
);
424+
425+
expect(context.canUse<ProducedProvider>(), isTrue);
426+
expect(context.use<ProducedProvider>().hello(), equals('produced'));
427+
});
428+
});
367429
}

0 commit comments

Comments
 (0)