1313import care .smith .fts .api .cda .DataSelector ;
1414import care .smith .fts .api .cda .Deidentificator ;
1515import care .smith .fts .cda .TransferProcessRunner .Phase ;
16+ import care .smith .fts .cda .TransferProcessStatus .Step ;
1617import ch .qos .logback .classic .Level ;
1718import ch .qos .logback .classic .Logger ;
1819import ch .qos .logback .classic .spi .ILoggingEvent ;
2122import java .time .Duration ;
2223import java .util .List ;
2324import java .util .concurrent .atomic .AtomicBoolean ;
25+ import java .util .concurrent .atomic .AtomicInteger ;
2426import org .hl7 .fhir .r4 .model .Bundle ;
2527import org .junit .jupiter .api .BeforeEach ;
2628import org .junit .jupiter .api .Test ;
@@ -37,6 +39,9 @@ class DefaultTransferProcessRunnerTest {
3739 private static final String PATIENT_IDENTIFIER_2 = "patient-142391" ;
3840 private static final ConsentedPatient PATIENT_2 =
3941 new ConsentedPatient (PATIENT_IDENTIFIER_2 , "system" );
42+ private static final String PATIENT_IDENTIFIER_3 = "patient-293847" ;
43+ private static final ConsentedPatient PATIENT_3 =
44+ new ConsentedPatient (PATIENT_IDENTIFIER_3 , "system" );
4045
4146 private DefaultTransferProcessRunner runner ;
4247
@@ -166,12 +171,7 @@ void ttl() throws InterruptedException {
166171
167172 sleep (110L ); // wait TTL seconds for process 1 and 2 to be removed
168173
169- create (runner .statuses ())
170- .assertNext (
171- r -> {
172- assertThat (r .size ()).isEqualTo (0 );
173- })
174- .verifyComplete ();
174+ create (runner .statuses ()).assertNext (r -> assertThat (r .size ()).isEqualTo (0 )).verifyComplete ();
175175 }
176176
177177 @ Test
@@ -236,7 +236,7 @@ void errorInBundleSenderSkipsBundleAndContinues() {
236236 new TransferProcessDefinition (
237237 "test" ,
238238 rawConfig ,
239- pids -> fromIterable (List .of (PATIENT , DefaultTransferProcessRunnerTest . PATIENT_2 )),
239+ pids -> fromIterable (List .of (PATIENT , PATIENT_2 )),
240240 p -> fromIterable (List .of (new ConsentedPatientBundle (new Bundle (), p ))),
241241 b -> just (new TransportBundle (new Bundle (), "transferId" )),
242242 errorOnSecond (new Result ()));
@@ -255,6 +255,133 @@ private static BundleSender errorOnSecond(Result result) {
255255 : Mono .error (new RuntimeException ("Cannot send bundle" ));
256256 }
257257
258+ private static DataSelector failOnSecondCall (Bundle bundle ) {
259+ var counter = new AtomicInteger (0 );
260+ return p ->
261+ counter .incrementAndGet () == 2
262+ ? Flux .error (new RuntimeException ("Cannot select data" ))
263+ : Flux .just (bundle ).map (b -> new ConsentedPatientBundle (b , p ));
264+ }
265+
266+ private static Deidentificator failOnSecondCall (TransportBundle bundle ) {
267+ var counter = new AtomicInteger (0 );
268+ return p ->
269+ counter .incrementAndGet () == 2
270+ ? Mono .error (new RuntimeException ("Cannot deidentify bundle" ))
271+ : just (bundle );
272+ }
273+
274+ private static BundleSender failOnSecondCall (Result result ) {
275+ var counter = new AtomicInteger (0 );
276+ return p ->
277+ counter .incrementAndGet () == 2
278+ ? Mono .error (new RuntimeException ("Cannot send bundle" ))
279+ : just (result );
280+ }
281+
282+ @ Test
283+ void errorInDataSelectorRecordsFailedPatient () {
284+ var process =
285+ new TransferProcessDefinition (
286+ "test" ,
287+ rawConfig ,
288+ pids -> fromIterable (List .of (PATIENT , PATIENT_2 , PATIENT_3 )),
289+ failOnSecondCall (new Bundle ()),
290+ b -> just (new TransportBundle (new Bundle (), "transferId" )),
291+ b -> just (new Result ()));
292+
293+ var processId = runner .start (process , List .of ());
294+ waitForCompletion (processId );
295+
296+ create (runner .status (processId ))
297+ .assertNext (
298+ r -> {
299+ assertThat (r .phase ()).isEqualTo (Phase .COMPLETED_WITH_ERROR );
300+ assertThat (r .sentBundles ()).isEqualTo (2 );
301+ assertThat (r .failedPatients ()).hasSize (1 );
302+ assertThat (r .failedPatients ().getFirst ().patientId ()).isEqualTo (PATIENT_IDENTIFIER_2 );
303+ assertThat (r .failedPatients ().getFirst ().step ()).isEqualTo (Step .SELECT_DATA );
304+ assertThat (r .failedPatients ().getFirst ().errorMessage ())
305+ .isEqualTo ("Cannot select data" );
306+ })
307+ .verifyComplete ();
308+ }
309+
310+ @ Test
311+ void errorInDeidentificatorRecordsFailedPatient () {
312+ var process =
313+ new TransferProcessDefinition (
314+ "test" ,
315+ rawConfig ,
316+ pids -> fromIterable (List .of (PATIENT , PATIENT_2 , PATIENT_3 )),
317+ p -> fromIterable (List .of (new ConsentedPatientBundle (new Bundle (), p ))),
318+ failOnSecondCall (new TransportBundle (new Bundle (), "transferId" )),
319+ b -> just (new Result ()));
320+
321+ var processId = runner .start (process , List .of ());
322+ waitForCompletion (processId );
323+
324+ create (runner .status (processId ))
325+ .assertNext (
326+ r -> {
327+ assertThat (r .phase ()).isEqualTo (Phase .COMPLETED_WITH_ERROR );
328+ assertThat (r .sentBundles ()).isEqualTo (2 );
329+ assertThat (r .failedPatients ()).hasSize (1 );
330+ assertThat (r .failedPatients ().getFirst ().step ()).isEqualTo (Step .DEIDENTIFY );
331+ assertThat (r .failedPatients ().getFirst ().errorMessage ())
332+ .isEqualTo ("Cannot deidentify bundle" );
333+ })
334+ .verifyComplete ();
335+ }
336+
337+ @ Test
338+ void errorInBundleSenderRecordsFailedPatient () {
339+ var process =
340+ new TransferProcessDefinition (
341+ "test" ,
342+ rawConfig ,
343+ pids -> fromIterable (List .of (PATIENT , PATIENT_2 , PATIENT_3 )),
344+ p -> fromIterable (List .of (new ConsentedPatientBundle (new Bundle (), p ))),
345+ b -> just (new TransportBundle (new Bundle (), "transferId" )),
346+ failOnSecondCall (new Result ()));
347+
348+ var processId = runner .start (process , List .of ());
349+ waitForCompletion (processId );
350+
351+ create (runner .status (processId ))
352+ .assertNext (
353+ r -> {
354+ assertThat (r .phase ()).isEqualTo (Phase .COMPLETED_WITH_ERROR );
355+ assertThat (r .sentBundles ()).isEqualTo (2 );
356+ assertThat (r .failedPatients ()).hasSize (1 );
357+ assertThat (r .failedPatients ().getFirst ().step ()).isEqualTo (Step .SEND_BUNDLE );
358+ assertThat (r .failedPatients ().getFirst ().errorMessage ())
359+ .isEqualTo ("Cannot send bundle" );
360+ })
361+ .verifyComplete ();
362+ }
363+
364+ @ Test
365+ void successfulTransferHasNoFailedPatients () {
366+ var process =
367+ new TransferProcessDefinition (
368+ "test" ,
369+ rawConfig ,
370+ pids -> fromIterable (List .of (PATIENT )),
371+ p -> fromIterable (List .of (new ConsentedPatientBundle (new Bundle (), PATIENT ))),
372+ b -> just (new TransportBundle (new Bundle (), "transferId" )),
373+ b -> just (new Result ()));
374+
375+ var processId = runner .start (process , List .of ());
376+ create (runner .status (processId ))
377+ .assertNext (
378+ r -> {
379+ assertThat (r .phase ()).isEqualTo (Phase .COMPLETED );
380+ assertThat (r .failedPatients ()).isEmpty ();
381+ })
382+ .verifyComplete ();
383+ }
384+
258385 @ Test
259386 void logErrorIncludesExceptionWhenDebugEnabled () {
260387 var event = runWithLogLevel (Level .DEBUG );
0 commit comments