11
11
import org .json .JSONException ;
12
12
import org .json .JSONObject ;
13
13
import org .junit .Assert ;
14
+ import org .junit .jupiter .api .MethodOrderer ;
15
+ import org .junit .jupiter .api .Order ;
14
16
import org .junit .jupiter .api .Test ;
15
17
import org .junit .jupiter .api .TestInstance ;
18
+ import org .junit .jupiter .api .TestMethodOrder ;
16
19
import org .junit .jupiter .api .extension .ExtendWith ;
17
20
import org .springframework .data .util .Pair ;
18
21
import org .testcontainers .containers .DockerComposeContainer ;
38
41
import java .time .OffsetDateTime ;
39
42
import java .time .temporal .ChronoUnit ;
40
43
import java .util .*;
44
+ import java .util .stream .Collectors ;
41
45
import java .util .zip .GZIPInputStream ;
42
46
import java .util .zip .ZipEntry ;
43
47
import java .util .zip .ZipInputStream ;
47
51
import static gov .cms .ab2d .e2etest .APIClient .PATIENT_EXPORT_PATH ;
48
52
import static java .time .format .DateTimeFormatter .ISO_DATE_TIME ;
49
53
import static java .time .temporal .ChronoUnit .SECONDS ;
50
- import static org .hamcrest .Matchers .greaterThan ;
51
54
import static org .hamcrest .Matchers .matchesPattern ;
52
55
53
56
// Unit tests here can be run from the IDE and will use LOCAL as the default, they can also be run from the TestLauncher
56
59
@ TestInstance (TestInstance .Lifecycle .PER_CLASS )
57
60
@ ExtendWith (TestRunnerParameterResolver .class )
58
61
@ Slf4j
62
+ @ TestMethodOrder (MethodOrderer .OrderAnnotation .class )
59
63
public class TestRunner {
60
64
private static final String FHIR_TYPE = "application/fhir+ndjson" ;
61
65
@@ -214,48 +218,62 @@ private Pair<String, JSONArray> verifyJsonFromStatusResponse(HttpResponse<String
214
218
return Pair .of (url , extension );
215
219
}
216
220
217
- private void verifyJsonFromfileDownload (String fileContent , JSONArray extension , OffsetDateTime since ) throws JSONException {
221
+ private void verifyJsonFromfileDownload (String fileContent , JSONArray extension , OffsetDateTime since , String optOut ) throws JSONException {
218
222
// Some of the data that is returned will be variable and will change from request to request, so not every
219
223
// JSON object can be verified
220
- final JSONObject fileJson = new JSONObject (fileContent );
221
- Assert .assertTrue (validFields (fileJson ));
222
- Assert .assertEquals ("ExplanationOfBenefit" , fileJson .getString ("resourceType" ));
223
- Assert .assertEquals (0 , fileJson .getInt ("precedence" ));
224
- String idString = fileJson .getString ("id" );
225
-
226
- boolean found = false ;
227
- for (String acceptableIdString : acceptableIdStrings ) {
228
- if (idString .startsWith (acceptableIdString )) {
229
- found = true ;
230
- break ;
224
+ String [] jsonLines = fileContent .split ("\n " );
225
+ for (String str : jsonLines ) {
226
+ if (str .isEmpty ()) {
227
+ continue ;
231
228
}
232
- }
233
229
234
- if (!found ) {
235
- Assert .fail ("No acceptable ID string was found, received " + idString );
236
- }
230
+ JSONObject jsonObject = new JSONObject (str );
231
+
232
+ Assert .assertTrue (validFields (jsonObject ));
233
+ Assert .assertEquals ("ExplanationOfBenefit" , jsonObject .getString ("resourceType" ));
234
+ Assert .assertEquals (0 , jsonObject .getInt ("precedence" ));
235
+ String idString = jsonObject .getString ("id" );
236
+
237
+ boolean found = false ;
238
+ for (String acceptableIdString : acceptableIdStrings ) {
239
+ if (idString .startsWith (acceptableIdString )) {
240
+ found = true ;
241
+ break ;
242
+ }
243
+ }
244
+
245
+ if (!found ) {
246
+ Assert .fail ("No acceptable ID string was found, received " + idString );
247
+ }
248
+
249
+ final JSONObject patientJson = jsonObject .getJSONObject ("patient" );
250
+ String referenceString = patientJson .getString ("reference" );
251
+ Assert .assertTrue (referenceString .startsWith ("Patient" ));
252
+
253
+ String patientId = referenceString .substring (referenceString .indexOf ('-' ) + 1 );
254
+ if (optOut != null && patientId .equals (optOut )) {
255
+ Assert .fail ("Patient ID opted out, but showed up in the download" );
256
+ }
237
257
238
- final JSONObject patientJson = fileJson .getJSONObject ("patient" );
239
- String referenceString = patientJson .getString ("reference" );
240
- Assert .assertTrue (referenceString .startsWith ("Patient" ));
241
- final JSONObject typeJson = fileJson .getJSONObject ("type" );
242
- Assert .assertNotNull (typeJson );
243
- final JSONArray codingJson = typeJson .getJSONArray ("coding" );
244
- Assert .assertNotNull (codingJson );
245
- Assert .assertTrue (codingJson .length () >= 4 );
246
- final JSONArray identifierJson = fileJson .getJSONArray ("identifier" );
247
- Assert .assertNotNull (identifierJson );
248
- Assert .assertEquals (2 , identifierJson .length ());
249
- final JSONArray diagnosisJson = fileJson .getJSONArray ("diagnosis" );
250
- Assert .assertNotNull (diagnosisJson );
251
- final JSONArray itemJson = fileJson .getJSONArray ("item" );
252
- Assert .assertNotNull (itemJson );
253
-
254
- final JSONObject metaJson = fileJson .getJSONObject ("meta" );
255
- final String lastUpdated = metaJson .getString ("lastUpdated" );
256
- Instant lastUpdatedInstant = Instant .parse (lastUpdated );
257
- if (since != null ) {
258
- Assert .assertTrue (lastUpdatedInstant .isAfter (since .toInstant ()));
258
+ final JSONObject typeJson = jsonObject .getJSONObject ("type" );
259
+ Assert .assertNotNull (typeJson );
260
+ final JSONArray codingJson = typeJson .getJSONArray ("coding" );
261
+ Assert .assertNotNull (codingJson );
262
+ Assert .assertTrue (codingJson .length () >= 4 );
263
+ final JSONArray identifierJson = jsonObject .getJSONArray ("identifier" );
264
+ Assert .assertNotNull (identifierJson );
265
+ Assert .assertEquals (2 , identifierJson .length ());
266
+ final JSONArray diagnosisJson = jsonObject .getJSONArray ("diagnosis" );
267
+ Assert .assertNotNull (diagnosisJson );
268
+ final JSONArray itemJson = jsonObject .getJSONArray ("item" );
269
+ Assert .assertNotNull (itemJson );
270
+
271
+ final JSONObject metaJson = jsonObject .getJSONObject ("meta" );
272
+ final String lastUpdated = metaJson .getString ("lastUpdated" );
273
+ Instant lastUpdatedInstant = Instant .parse (lastUpdated );
274
+ if (since != null ) {
275
+ Assert .assertTrue (lastUpdatedInstant .isAfter (since .toInstant ()));
276
+ }
259
277
}
260
278
261
279
JSONObject checkSumObject = extension .getJSONObject (0 );
@@ -308,7 +326,7 @@ private boolean validFields(JSONObject jsonObject) {
308
326
return true ;
309
327
}
310
328
311
- private void downloadFile (Pair <String , JSONArray > downloadDetails , OffsetDateTime since ) throws IOException , InterruptedException , JSONException {
329
+ private void downloadFile (Pair <String , JSONArray > downloadDetails , OffsetDateTime since , String optOut ) throws IOException , InterruptedException , JSONException {
312
330
HttpResponse <InputStream > downloadResponse = apiClient .fileDownloadRequest (downloadDetails .getFirst ());
313
331
314
332
Assert .assertEquals (200 , downloadResponse .statusCode ());
@@ -320,7 +338,7 @@ private void downloadFile(Pair<String, JSONArray> downloadDetails, OffsetDateTim
320
338
downloadString = IOUtils .toString (gzipInputStream , Charset .defaultCharset ());
321
339
}
322
340
323
- verifyJsonFromfileDownload (downloadString , downloadDetails .getSecond (), since );
341
+ verifyJsonFromfileDownload (downloadString , downloadDetails .getSecond (), since , optOut );
324
342
}
325
343
326
344
private void downloadZipFile (String url , JSONArray extension , OffsetDateTime since ) throws IOException , InterruptedException , JSONException {
@@ -333,7 +351,7 @@ private void downloadZipFile(String url, JSONArray extension, OffsetDateTime sin
333
351
zipIn .closeEntry ();
334
352
entry = zipIn .getNextEntry ();
335
353
}
336
- verifyJsonFromfileDownload (downloadString .toString (), extension , since );
354
+ verifyJsonFromfileDownload (downloadString .toString (), extension , since , null );
337
355
}
338
356
339
357
public static String extractZipFileData (ZipEntry entry , ZipInputStream zipIn ) throws IOException {
@@ -376,16 +394,18 @@ private Pair<String, JSONArray> performStatusRequests(List<String> contentLocati
376
394
}
377
395
378
396
@ Test
397
+ @ Order (1 )
379
398
public void runSystemWideExport () throws IOException , InterruptedException , JSONException {
380
399
HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , null );
381
400
Assert .assertEquals (202 , exportResponse .statusCode ());
382
401
List <String > contentLocationList = exportResponse .headers ().map ().get ("content-location" );
383
402
384
403
Pair <String , JSONArray > downloadDetails = performStatusRequests (contentLocationList , false , "S0000" );
385
- downloadFile (downloadDetails , null );
404
+ downloadFile (downloadDetails , null , null );
386
405
}
387
406
388
407
@ Test
408
+ @ Order (2 )
389
409
public void runSystemWideExportSince () throws IOException , InterruptedException , JSONException {
390
410
HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , earliest );
391
411
System .out .println (earliest );
@@ -394,11 +414,12 @@ public void runSystemWideExportSince() throws IOException, InterruptedException,
394
414
395
415
Pair <String , JSONArray > downloadDetails = performStatusRequests (contentLocationList , false , "S0000" );
396
416
if (downloadDetails != null ) {
397
- downloadFile (downloadDetails , earliest );
417
+ downloadFile (downloadDetails , earliest , null );
398
418
}
399
419
}
400
420
401
421
@ Test
422
+ @ Order (3 )
402
423
public void runErrorSince () throws IOException , InterruptedException {
403
424
OffsetDateTime timeBeforeEarliest = earliest .minus (1 , ChronoUnit .MINUTES );
404
425
HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , timeBeforeEarliest );
@@ -410,30 +431,34 @@ public void runErrorSince() throws IOException, InterruptedException {
410
431
}
411
432
412
433
@ Test
434
+ @ Order (4 )
413
435
public void runSystemWideZipExport () throws IOException , InterruptedException , JSONException {
414
436
HttpResponse <String > exportResponse = apiClient .exportRequest (ZIPFORMAT , null );
415
437
Assert .assertEquals (400 , exportResponse .statusCode ());
416
438
}
417
439
418
440
@ Test
441
+ @ Order (5 )
419
442
public void runContractNumberExport () throws IOException , InterruptedException , JSONException {
420
443
String contractNumber = "S0000" ;
421
444
HttpResponse <String > exportResponse = apiClient .exportByContractRequest (contractNumber , FHIR_TYPE , null );
422
445
Assert .assertEquals (202 , exportResponse .statusCode ());
423
446
List <String > contentLocationList = exportResponse .headers ().map ().get ("content-location" );
424
447
425
448
Pair <String , JSONArray > downloadDetails = performStatusRequests (contentLocationList , true , contractNumber );
426
- downloadFile (downloadDetails , null );
449
+ downloadFile (downloadDetails , null , null );
427
450
}
428
451
429
452
@ Test
453
+ @ Order (6 )
430
454
void runContractNumberZipExport () throws IOException , InterruptedException , JSONException {
431
455
String contractNumber = "S0000" ;
432
456
HttpResponse <String > exportResponse = apiClient .exportByContractRequest (contractNumber , ZIPFORMAT , null );
433
457
Assert .assertEquals (400 , exportResponse .statusCode ());
434
458
}
435
459
436
460
@ Test
461
+ @ Order (7 )
437
462
public void testDelete () throws IOException , InterruptedException {
438
463
HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , null );
439
464
@@ -447,6 +472,7 @@ public void testDelete() throws IOException, InterruptedException {
447
472
}
448
473
449
474
@ Test
475
+ @ Order (8 )
450
476
public void testUserCannotDownloadOtherUsersJob () throws IOException , InterruptedException , JSONException {
451
477
String contractNumber = "S0000" ;
452
478
HttpResponse <String > exportResponse = apiClient .exportByContractRequest (contractNumber , FHIR_TYPE , null );
@@ -462,6 +488,7 @@ public void testUserCannotDownloadOtherUsersJob() throws IOException, Interrupte
462
488
}
463
489
464
490
@ Test
491
+ @ Order (9 )
465
492
public void testUserCannotDeleteOtherUsersJob () throws IOException , InterruptedException , JSONException {
466
493
HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , null );
467
494
@@ -481,6 +508,7 @@ public void testUserCannotDeleteOtherUsersJob() throws IOException, InterruptedE
481
508
}
482
509
483
510
@ Test
511
+ @ Order (10 )
484
512
public void testUserCannotCheckStatusOtherUsersJob () throws IOException , InterruptedException , JSONException {
485
513
HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , null );
486
514
@@ -508,6 +536,7 @@ private APIClient createSecondUserClient() throws InterruptedException, JSONExce
508
536
}
509
537
510
538
@ Test
539
+ @ Order (11 )
511
540
public void testUserCannotMakeRequestWithoutToken () throws IOException , InterruptedException {
512
541
HttpRequest exportRequest = HttpRequest .newBuilder ()
513
542
.uri (URI .create (AB2D_API_URL + PATIENT_EXPORT_PATH ))
@@ -522,6 +551,7 @@ public void testUserCannotMakeRequestWithoutToken() throws IOException, Interrup
522
551
}
523
552
524
553
@ Test
554
+ @ Order (12 )
525
555
public void testUserCannotMakeRequestWithSelfSignedToken () throws IOException , InterruptedException , JSONException {
526
556
String clientSecret = "wefikjweglkhjwelgkjweglkwegwegewg" ;
527
557
SecretKey sharedSecret = Keys .hmacShaKeyFor (clientSecret .getBytes (StandardCharsets .UTF_8 ));
@@ -558,6 +588,7 @@ public void testUserCannotMakeRequestWithSelfSignedToken() throws IOException, I
558
588
}
559
589
560
590
@ Test
591
+ @ Order (13 )
561
592
public void testBadQueryParameterResource () throws IOException , InterruptedException {
562
593
var params = new HashMap <>(){{
563
594
put ("_type" , "BadParam" );
@@ -568,6 +599,7 @@ public void testBadQueryParameterResource() throws IOException, InterruptedExcep
568
599
}
569
600
570
601
@ Test
602
+ @ Order (14 )
571
603
public void testBadQueryParameterOutputFormat () throws IOException , InterruptedException {
572
604
var params = new HashMap <>(){{
573
605
put ("_outputFormat" , "BadParam" );
@@ -578,9 +610,22 @@ public void testBadQueryParameterOutputFormat() throws IOException, InterruptedE
578
610
}
579
611
580
612
@ Test
613
+ @ Order (15 )
581
614
public void testHealthEndPoint () throws IOException , InterruptedException {
582
615
HttpResponse <String > healthCheckResponse = apiClient .healthCheck ();
583
616
584
617
Assert .assertEquals (200 , healthCheckResponse .statusCode ());
585
618
}
619
+
620
+ @ Test
621
+ @ Order (16 )
622
+ public void testOptOut () throws IOException , InterruptedException , JSONException {
623
+ HttpResponse <String > exportResponse = apiClient .exportRequest (FHIR_TYPE , null );
624
+
625
+ Assert .assertEquals (202 , exportResponse .statusCode ());
626
+ List <String > contentLocationList = exportResponse .headers ().map ().get ("content-location" );
627
+
628
+ Pair <String , JSONArray > downloadDetails = performStatusRequests (contentLocationList , false , "S0000" );
629
+ downloadFile (downloadDetails , null , "19990000002906" );
630
+ }
586
631
}
0 commit comments