@@ -21,9 +21,13 @@ import spock.util.concurrent.PollingConditions
2121class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
2222
2323 @Shared
24- private final rcPayload = new JsonSlurper (). parse(fetchResource(" config/flags-v1.json" )). with { json ->
25- return JsonOutput . toJson(json. data. attributes)
26- }
24+ private final rcConfig = new JsonSlurper (). parse(fetchResource(" ffe-system-test-data/ufc-config.json" )) as Map<String , Object >
25+
26+ @Shared
27+ private final rcPayload = JsonOutput . toJson(rcConfig)
28+
29+ @Shared
30+ private final loggedAllocations = buildLoggedAllocations(rcConfig)
2731
2832 @Override
2933 ProcessBuilder createProcessBuilder () {
@@ -70,34 +74,40 @@ class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
7074 setup :
7175 setRemoteConfig(" datadog/2/FFE_FLAGS/1/config" , rcPayload)
7276 final url = " http://localhost:${ httpPort} /openfeature/evaluate"
73- final testCases = parseTestCases(). findAll {
74- it. result. flagMetadata?. doLog
75- }
77+ final testCases = parseTestCases()
78+ assert ! testCases. isEmpty()
7679
7780 when :
78- final responses = testCases. collect {
81+ final results = testCases. collect {
7982 testCase ->
8083 final request = new Request.Builder ()
8184 .url(url)
8285 .post(RequestBody . create(MediaType . parse(' application/json' ), JsonOutput . toJson(testCase)))
8386 .build()
84- client. newCall(request). execute()
87+ final response = client. newCall(request). execute()
88+ final responseBody = new JsonSlurper (). parse(response. body(). byteStream())
89+ return [testCase : testCase, response : response, body : responseBody]
8590 }
91+ final expectedExposures = uniqueExpectedExposures(results)
8692
8793 then :
88- responses . every {
89- it. code() == 200
94+ results . every {
95+ it. response . code() == 200
9096 }
97+ ! expectedExposures. isEmpty()
9198 new PollingConditions (timeout : 10 ). eventually {
9299 final requests = evpProxyMessages* . getV2() as List<Map<String , Object > >
93100 final events = requests* . exposures. flatten()
94- assert events. size() == testCases . size()
95- testCases . each {
96- testCase ->
101+ assert events. size() == expectedExposures . size()
102+ expectedExposures . each {
103+ expected ->
97104 assert events. find {
98105 event ->
99- event. flag. key == testCase. flag && event. subject. id == testCase. targetingKey
100- } != null : " Unable to find exposure with flag=${ testCase.flag} and targetingKey=${ testCase.targetingKey} "
106+ event. flag. key == expected. flag &&
107+ event. allocation. key == expected. allocation &&
108+ event. variant. key == expected. variant &&
109+ event. subject. id == expected. targetingKey
110+ } != null : " Unable to find exposure ${ expected} "
101111 }
102112 }
103113 }
@@ -119,8 +129,16 @@ class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
119129 response. code() == 200
120130 final responseBody = new JsonSlurper (). parse(response. body(). byteStream())
121131 responseBody. value == testCase. result. value
122- responseBody. variant == testCase. result. variant
123- responseBody. flagMetadata?. allocationKey == testCase. result. flagMetadata?. allocationKey
132+ responseBody. reason == testCase. result. reason
133+ if (testCase. result. containsKey(' errorCode' )) {
134+ assert responseBody. errorCode == testCase. result. errorCode
135+ }
136+ if (testCase. result. containsKey(' variant' )) {
137+ assert responseBody. variant == testCase. result. variant
138+ }
139+ if (testCase. result. flagMetadata?. allocationKey) {
140+ assert responseBody. flagMetadata?. allocationKey == testCase. result. flagMetadata?. allocationKey
141+ }
124142
125143 where :
126144 testCase << parseTestCases()
@@ -131,11 +149,12 @@ class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
131149 }
132150
133151 private static List<Map<String , Object > > parseTestCases () {
134- final folder = fetchResource(' data' )
152+ final folder = fetchResource(' ffe-system-test- data/evaluation-cases ' )
135153 final uri = folder. toURI()
136154 final testsPath = Paths . get(uri)
137155 final files = Files . list(testsPath)
138156 .filter(path -> path. toString(). endsWith(' .json' ))
157+ .sorted(Comparator . comparing(path -> path. fileName. toString()))
139158 final result = []
140159 final slurper = new JsonSlurper ()
141160 files. each {
@@ -148,9 +167,51 @@ class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
148167 }
149168 result. addAll(testCases)
150169 }
170+ assert ! result. isEmpty()
151171 return result
152172 }
153173
174+ private List<Map<String , String > > uniqueExpectedExposures (final List<Map<String , Object > > results ) {
175+ final expected = []
176+ final seen = [] as Set<String >
177+ results. each { result ->
178+ final testCase = result. testCase as Map<String , Object >
179+ final body = result. body as Map<String , Object >
180+ final flag = testCase. flag as String
181+ final allocation = body. flagMetadata?. allocationKey as String
182+ final variant = body. variant as String
183+ if (! variant || ! allocation || ! allocationLogs(flag, allocation)) {
184+ return
185+ }
186+
187+ final exposure = [
188+ flag : flag,
189+ allocation : allocation,
190+ variant : variant,
191+ targetingKey : testCase. targetingKey
192+ ]
193+ final key = " ${ exposure.flag} \u 0000${ exposure.targetingKey} \u 0000${ exposure.allocation} \u 0000${ exposure.variant} "
194+ if (seen. add(key)) {
195+ expected. add(exposure)
196+ }
197+ }
198+ return expected
199+ }
200+
201+ private boolean allocationLogs (final String flag , final String allocation ) {
202+ return loggedAllocations[" ${ flag} \u 0000${ allocation} " ] == true
203+ }
204+
205+ private static Map<String , Boolean > buildLoggedAllocations (final Map<String , Object > config ) {
206+ final logged = [:]
207+ (config. flags as Map<String , Object > ). each { flag , definition ->
208+ (definition. allocations ?: []). each { allocation ->
209+ logged[" ${ flag} \u 0000${ allocation.key} " ] = allocation. doLog == true
210+ }
211+ }
212+ return logged
213+ }
214+
154215 private static Set<Product > decodeProducts (final Map<String , Object > request ) {
155216 return request. client. products. collect { Product . valueOf(it) }
156217 }
0 commit comments