11package dev .openfeature .sdk .e2e .steps ;
22
33import static dev .openfeature .sdk .testutils .TestFlagsUtils .buildFlags ;
4+ import static org .assertj .core .api .Assertions .assertThat ;
5+ import static org .mockito .ArgumentMatchers .any ;
6+ import static org .mockito .ArgumentMatchers .anyString ;
7+ import static org .mockito .Mockito .doAnswer ;
8+ import static org .mockito .Mockito .doThrow ;
9+ import static org .mockito .Mockito .mock ;
10+ import static org .mockito .Mockito .spy ;
11+ import static org .mockito .Mockito .when ;
412
13+ import dev .openfeature .sdk .Client ;
14+ import dev .openfeature .sdk .ErrorCode ;
15+ import dev .openfeature .sdk .EventProvider ;
16+ import dev .openfeature .sdk .FeatureProvider ;
517import dev .openfeature .sdk .OpenFeatureAPI ;
18+ import dev .openfeature .sdk .ProviderEvaluation ;
19+ import dev .openfeature .sdk .ProviderEventDetails ;
20+ import dev .openfeature .sdk .ProviderState ;
21+ import dev .openfeature .sdk .Reason ;
22+ import dev .openfeature .sdk .Value ;
623import dev .openfeature .sdk .e2e .State ;
24+ import dev .openfeature .sdk .exceptions .FatalError ;
25+ import dev .openfeature .sdk .exceptions .ProviderNotReadyError ;
726import dev .openfeature .sdk .providers .memory .Flag ;
827import dev .openfeature .sdk .providers .memory .InMemoryProvider ;
928import io .cucumber .java .en .Given ;
29+ import io .cucumber .java .en .Then ;
30+ import org .awaitility .Awaitility ;
1031import java .util .Map ;
32+ import java .util .concurrent .atomic .AtomicBoolean ;
1133
1234public class ProviderSteps {
1335 private final State state ;
@@ -16,11 +38,131 @@ public ProviderSteps(State state) {
1638 this .state = state ;
1739 }
1840
19- @ Given ("a stable provider" )
20- public void aStableProvider () {
41+ @ Given ("a {} provider" )
42+ public void a_provider_with_status (String providerType ) throws Exception {
43+ // Normalize input to handle both single word and quoted strings
44+ String normalizedType = providerType .toLowerCase ().replaceAll ("[\" \\ s]+" , " " ).trim ();
45+
46+ switch (normalizedType ) {
47+ case "not ready" :
48+ setupMockProvider (ErrorCode .PROVIDER_NOT_READY , "Provider in not ready state" ,
49+ ProviderState .NOT_READY );
50+ break ;
51+ case "stable" :
52+ case "ready" :
53+ setupStableProvider ();
54+ break ;
55+ case "fatal" :
56+ setupMockProvider (ErrorCode .PROVIDER_FATAL , "Provider in fatal state" ,
57+ ProviderState .FATAL );
58+ break ;
59+ case "error" :
60+ setupMockProvider (ErrorCode .GENERAL , "Provider in error state" , ProviderState .ERROR );
61+ break ;
62+ case "stale" :
63+ setupMockProvider (null , null , ProviderState .STALE );
64+ break ;
65+ default :
66+ throw new IllegalArgumentException ("Unsupported provider type: " + providerType );
67+ }
68+ }
69+
70+ // ===============================
71+ // Provider Status Assertion Steps
72+ // ===============================
73+
74+ @ Then ("the provider status should be {string}" )
75+ public void the_provider_status_should_be (String expectedStatus ) {
76+ ProviderState actualStatus = state .client .getProviderState ();
77+ ProviderState expected = ProviderState .valueOf (expectedStatus );
78+ assertThat (actualStatus ).isEqualTo (expected );
79+ }
80+
81+ // ===============================
82+ // Helper Methods
83+ // ===============================
84+
85+ private void setupStableProvider () throws Exception {
2186 Map <String , Flag <?>> flags = buildFlags ();
2287 InMemoryProvider provider = new InMemoryProvider (flags );
2388 OpenFeatureAPI .getInstance ().setProviderAndWait (provider );
2489 state .client = OpenFeatureAPI .getInstance ().getClient ();
2590 }
91+
92+ private void setupMockProvider (ErrorCode errorCode , String errorMessage ,
93+ ProviderState providerState )
94+ throws Exception {
95+ EventProvider mockProvider = spy (EventProvider .class );
96+
97+ switch (providerState ) {
98+ case NOT_READY :
99+ doAnswer (invocationOnMock -> {
100+ while (true ) {}
101+ }).when (mockProvider ).initialize (any ());
102+ break ;
103+ case FATAL :
104+ doThrow (new FatalError (errorMessage )).when (mockProvider ).initialize (any ());
105+ break ;
106+ }
107+ // Configure all evaluation methods with a single helper
108+ configureMockEvaluations (mockProvider , errorCode , errorMessage );
109+
110+ OpenFeatureAPI .getInstance ().setProvider (providerState .name (), mockProvider );
111+ state .client = OpenFeatureAPI .getInstance ().getClient (providerState .name ());
112+
113+ ProviderEventDetails details = ProviderEventDetails .builder ().errorCode (errorCode ).build ();
114+ switch (providerState ) {
115+ case FATAL :
116+ case ERROR :
117+ mockProvider .emitProviderReady (details );
118+ mockProvider .emitProviderError (details );
119+ break ;
120+ case STALE :
121+ mockProvider .emitProviderReady (details );
122+ mockProvider .emitProviderStale (details );
123+ break ;
124+ default :
125+ }
126+ Awaitility .await ().until (() -> {
127+ ProviderState providerState1 = state .client .getProviderState ();
128+ return providerState1 == providerState ;
129+ });
130+ }
131+
132+ private void configureMockEvaluations (FeatureProvider mockProvider , ErrorCode errorCode , String errorMessage ) {
133+ // Configure Boolean evaluation
134+ when (mockProvider .getBooleanEvaluation (anyString (), any (Boolean .class ), any ()))
135+ .thenAnswer (invocation -> createProviderEvaluation (invocation .getArgument (1 ), errorCode , errorMessage
136+ ));
137+
138+ // Configure String evaluation
139+ when (mockProvider .getStringEvaluation (anyString (), any (String .class ), any ()))
140+ .thenAnswer (invocation -> createProviderEvaluation (invocation .getArgument (1 ), errorCode , errorMessage
141+ ));
142+
143+ // Configure Integer evaluation
144+ when (mockProvider .getIntegerEvaluation (anyString (), any (Integer .class ), any ()))
145+ .thenAnswer (invocation -> createProviderEvaluation (invocation .getArgument (1 ), errorCode , errorMessage
146+ ));
147+
148+ // Configure Double evaluation
149+ when (mockProvider .getDoubleEvaluation (anyString (), any (Double .class ), any ()))
150+ .thenAnswer (invocation -> createProviderEvaluation (invocation .getArgument (1 ), errorCode , errorMessage
151+ ));
152+
153+ // Configure Object evaluation
154+ when (mockProvider .getObjectEvaluation (anyString (), any (Value .class ), any ()))
155+ .thenAnswer (invocation -> createProviderEvaluation (invocation .getArgument (1 ), errorCode , errorMessage
156+ ));
157+ }
158+
159+ private <T > ProviderEvaluation <T > createProviderEvaluation (T defaultValue , ErrorCode errorCode ,
160+ String errorMessage ) {
161+ return ProviderEvaluation .<T >builder ()
162+ .value (defaultValue )
163+ .errorCode (errorCode )
164+ .errorMessage (errorMessage )
165+ .reason (Reason .ERROR .toString ())
166+ .build ();
167+ }
26168}
0 commit comments