1+ import 'dart:async' ;
12import 'dart:collection' ;
23import 'package:test/test.dart' ;
34import 'package:launchdarkly_common_client/launchdarkly_common_client.dart' ;
5+ import 'package:launchdarkly_common_client/src/data_sources/data_source.dart' ;
46
57import '../ld_dart_client_test.dart' show TestConfig;
68
@@ -71,6 +73,44 @@ final class TestHook extends Hook {
7173 }
7274}
7375
76+ final class TestDataSourceWithEnvironmentId implements DataSource {
77+ final StreamController <DataSourceEvent > _eventController = StreamController ();
78+ final String environmentId;
79+
80+ TestDataSourceWithEnvironmentId (this .environmentId);
81+
82+ @override
83+ Stream <DataSourceEvent > get events => _eventController.stream;
84+
85+ @override
86+ void restart () {}
87+
88+ @override
89+ void start () {
90+ Timer (Duration (milliseconds: 10 ), () {
91+ _eventController.sink.add (DataEvent (
92+ 'put' ,
93+ '{"test-flag":{'
94+ '"version":1,'
95+ '"flagVersion":1,'
96+ '"value":"test-value",'
97+ '"variation":0,'
98+ '"reason":{"kind":"FALLTHROUGH"}'
99+ '}}' ,
100+ environmentId: environmentId));
101+ });
102+ }
103+
104+ @override
105+ void stop () {
106+ _eventController.close ();
107+ }
108+
109+ void close () {
110+ _eventController.close ();
111+ }
112+ }
113+
74114void main () {
75115 group ('LDCommonClient Hooks Integration' , () {
76116 late LDCommonClient client;
@@ -239,7 +279,7 @@ void main() {
239279 expect (evalContext.method, equals ('jsonVariation' ));
240280 expect (evalContext.context, isNotNull);
241281 expect (evalContext.environmentId,
242- isNull); // Environment ID not implemented yet
282+ isNull); // Environment ID null when in offline mode
243283 });
244284
245285 test ('should handle hooks when client is not started' , () async {
@@ -251,4 +291,100 @@ void main() {
251291 expect (initialHook.callLog, contains ('afterEvaluation' ));
252292 });
253293 });
294+
295+ group ('LDCommonClient Hooks with Environment ID' , () {
296+ late LDCommonClient client;
297+ late TestHook testHook;
298+ late LDContext testContext;
299+ late TestDataSourceWithEnvironmentId dataSource;
300+
301+ setUp (() {
302+ testHook = TestHook ('test-hook' );
303+ testContext = LDContextBuilder ().kind ('user' , 'test-user' ).build ();
304+ dataSource = TestDataSourceWithEnvironmentId ('test-env-123' );
305+
306+ final config = TestConfig ('test-sdk-key' , AutoEnvAttributes .disabled,
307+ offline: false );
308+
309+ client = LDCommonClient (
310+ config,
311+ CommonPlatform (),
312+ testContext,
313+ DiagnosticSdkData (name: 'test-sdk' , version: '1.0.0' ),
314+ hooks: [testHook],
315+ dataSourceFactories: (LDCommonConfig config, LDLogger logger,
316+ HttpProperties properties) {
317+ return {
318+ ConnectionMode .streaming: (LDContext context) {
319+ return dataSource;
320+ },
321+ ConnectionMode .polling: (LDContext context) {
322+ return dataSource;
323+ },
324+ };
325+ },
326+ );
327+ });
328+
329+ tearDown (() async {
330+ await client.close ();
331+ dataSource.close ();
332+ });
333+
334+ test ('should pass environment ID to hooks when available from data source' ,
335+ () async {
336+ await client.start ();
337+
338+ // Wait for data source to initialize
339+ await Future .delayed (Duration (milliseconds: 50 ));
340+
341+ // Test all variation methods with environment ID
342+ client.boolVariation ('test-flag' , false );
343+ client.boolVariationDetail ('test-flag' , false );
344+ client.intVariation ('test-flag' , 0 );
345+ client.intVariationDetail ('test-flag' , 0 );
346+ client.doubleVariation ('test-flag' , 0.0 );
347+ client.doubleVariationDetail ('test-flag' , 0.0 );
348+ client.stringVariation ('test-flag' , 'default' );
349+ client.stringVariationDetail ('test-flag' , 'default' );
350+ client.jsonVariation ('test-flag' , LDValue .ofNull ());
351+ client.jsonVariationDetail ('test-flag' , LDValue .ofNull ());
352+
353+ // Should have calls for all variation methods
354+ final beforeEvaluationCalls =
355+ testHook.callLog.where ((call) => call == 'beforeEvaluation' ).length;
356+ final afterEvaluationCalls =
357+ testHook.callLog.where ((call) => call == 'afterEvaluation' ).length;
358+
359+ expect (beforeEvaluationCalls, equals (10 ));
360+ expect (afterEvaluationCalls, equals (10 ));
361+
362+ // All evaluation contexts should have the environment ID
363+ for (final evalContext in testHook.evaluationContexts) {
364+ expect (evalContext.environmentId, equals ('test-env-123' ),
365+ reason:
366+ 'Environment ID should be passed to hooks for method: ${evalContext .method }' );
367+ expect (evalContext.flagKey, equals ('test-flag' ));
368+ expect (evalContext.context, isNotNull);
369+ }
370+
371+ // Check that all variation methods are represented
372+ final methods =
373+ testHook.evaluationContexts.map ((ctx) => ctx.method).toSet ();
374+ expect (
375+ methods,
376+ containsAll ([
377+ 'boolVariation' ,
378+ 'boolVariationDetail' ,
379+ 'intVariation' ,
380+ 'intVariationDetail' ,
381+ 'doubleVariation' ,
382+ 'doubleVariationDetail' ,
383+ 'stringVariation' ,
384+ 'stringVariationDetail' ,
385+ 'jsonVariation' ,
386+ 'jsonVariationDetail' ,
387+ ]));
388+ });
389+ });
254390}
0 commit comments