@@ -49,9 +49,11 @@ final class ForecastSpecControllerTestIT extends DataApiTestIT {
4949
5050 @ BeforeAll
5151 static void create () throws Exception {
52+ deleteSpec ();
5253 createLocation (locationId , true , OFFICE );
5354 createLocation (locationId2 , true , OFFICE );
5455 createTimeSeries (locationId );
56+ createTimeSeries (locationId2 );
5557 createTimeseries (OFFICE , "TsBinTestLoc.Elev.Inst.~1Day.0.SPK-cavi-fct" );
5658 createTimeseries (OFFICE , "TsBinTestLoc.Flow-Outflow.Inst.~1Day.0.SPK-cavi-fct" );
5759 createTimeseries (OFFICE , "TsBinTestLoc2.Elev.Inst.~1Day.0.SPK-cavi-fct" );
@@ -88,17 +90,64 @@ static void truncateFcstTimeSeries() throws SQLException {
8890 }
8991
9092 static void deleteSpec () throws SQLException {
91- try {
9293 CwmsDataApiSetupCallback .getDatabaseLink ()
9394 .connection (c -> {
94- CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID , "designator" ,
95- DeleteRule .DELETE_ALL .getRule (), OFFICE );
96- CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "-NULL-DESIGNATOR" , null ,
97- DeleteRule .DELETE_ALL .getRule (), OFFICE );
95+ try {
96+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID , "designator" ,
97+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
98+ } catch (DataAccessException e ) {
99+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
100+ }
101+ try {
102+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "-NULL-DESIGNATOR" , null ,
103+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
104+ } catch (DataAccessException e ) {
105+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
106+ }
107+ try {
108+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "-TEST" , "designator" ,
109+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
110+ } catch (DataAccessException e ) {
111+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
112+ }
113+ try {
114+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "TEST" , "designator" ,
115+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
116+ } catch (DataAccessException e ) {
117+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
118+ }
119+ try {
120+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "TEST-2" , "designator" ,
121+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
122+ } catch (DataAccessException e ) {
123+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
124+ }
125+ try {
126+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "-TEST-2" , "designator" ,
127+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
128+ } catch (DataAccessException e ) {
129+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
130+ }
131+ try {
132+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), "TEST_SPEC_2" , "designator" ,
133+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
134+ } catch (DataAccessException e ) {
135+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
136+ }
137+ try {
138+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "-LRTS" , "designator" ,
139+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
140+ } catch (DataAccessException e ) {
141+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
142+ }
143+ try {
144+ CWMS_FCST_PACKAGE .call_DELETE_FCST_SPEC (OracleDSL .using (c ).configuration (), SPEC_ID + "-2" , "designator" ,
145+ DeleteRule .DELETE_ALL .getRule (), OFFICE );
146+ } catch (DataAccessException e ) {
147+ LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
148+ }
98149 });
99- } catch (DataAccessException e ) {
100- LOGGER .atFine ().withCause (e ).log ("Couldn't clean up forecast spec before executing tests. Probably didn't exist" );
101- }
150+
102151 }
103152
104153
@@ -477,6 +526,155 @@ void create_getAll_delete_getAll(String format) throws Exception {
477526 ;
478527 }
479528
529+ @ ParameterizedTest
530+ @ ValueSource (strings = {Formats .JSONV2 , Formats .DEFAULT })
531+ void create_then_create_with_diff_location_then_getAll_then_delete (String format ) throws Exception {
532+
533+ // Structure of test:
534+ // 1) Create a spec with a location id
535+ // 2) Create a forecast instance with that spec
536+ // 3) Create a second spec with the same designator but different location id
537+ // 4) Call getAll and verify a list/array is returned containing only one spec with the updated location id
538+ // 5) Verify the forecast instance is still associated with the spec with the updated location id
539+ // 6) Delete spec and forecast instance
540+ // 7) Call getAll again and verify they are not returned
541+
542+ // Step 1) Create two specs
543+ InputStream resource = this .getClass ().getResourceAsStream ("/cwms/cda/api/spk/forecast_spec_create.json" );
544+ assertNotNull (resource );
545+ String tsData = IOUtils .toString (resource , StandardCharsets .UTF_8 );
546+ assertNotNull (tsData );
547+
548+ String specId = SPEC_ID + "TEST" ;
549+ tsData = tsData .replace ("\" spec-id\" : \" " + SPEC_ID + "\" " , "\" spec-id\" : \" " + specId + "\" " );
550+
551+ String tsData2 = tsData .replace (locationId , locationId2 );
552+
553+ TestAccounts .KeyUser user = TestAccounts .KeyUser .SPK_NORMAL ;
554+
555+ // Create first spec
556+ given ()
557+ .log ().ifValidationFails (LogDetail .ALL , true )
558+ .accept (format )
559+ .contentType (Formats .JSONV2 )
560+ .body (tsData )
561+ .header (AUTH_HEADER , user .toHeaderValue ())
562+ .when ()
563+ .redirects ().follow (true )
564+ .redirects ().max (3 )
565+ .post (PATH )
566+ .then ()
567+ .log ().ifValidationFails (LogDetail .ALL , true )
568+ .assertThat ()
569+ .statusCode (is (HttpServletResponse .SC_CREATED ));
570+
571+ // Step 2) Create a forecast instance for the first spec (at locationId)
572+ InputStream instResource = this .getClass ().getResourceAsStream ("/cwms/cda/api/spk/forecast_inst_create.json" );
573+ assertNotNull (instResource );
574+ String instData = IOUtils .toString (instResource , StandardCharsets .UTF_8 );
575+ // Ensure the instance spec-id and timeseries location match the spec we just created
576+ instData = instData .replace ("\" spec-id\" : \" " + SPEC_ID + "\" " , "\" spec-id\" : \" " + specId + "\" " );
577+ // The instance resource uses FcstInstTestLoc.* TSIDs; switch them to our locationId TSIDs
578+ instData = instData .replace ("FcstInstTestLoc" , locationId );
579+
580+ given ()
581+ .log ().ifValidationFails (LogDetail .ALL , true )
582+ .accept (format )
583+ .contentType (Formats .JSONV2 )
584+ .body (instData )
585+ .header (AUTH_HEADER , user .toHeaderValue ())
586+ .when ()
587+ .redirects ().follow (true )
588+ .redirects ().max (3 )
589+ .post (ForecastInstanceControllerTestIT .PATH )
590+ .then ()
591+ .log ().ifValidationFails (LogDetail .ALL , true )
592+ .assertThat ()
593+ .statusCode (is (HttpServletResponse .SC_CREATED ));
594+
595+ // Create second spec
596+ given ()
597+ .log ().ifValidationFails (LogDetail .ALL , true )
598+ .accept (format )
599+ .contentType (Formats .JSONV2 )
600+ .body (tsData2 )
601+ .header (AUTH_HEADER , user .toHeaderValue ())
602+ .when ()
603+ .redirects ().follow (true )
604+ .redirects ().max (3 )
605+ .post (PATH )
606+ .then ()
607+ .log ().ifValidationFails (LogDetail .ALL , true )
608+ .assertThat ()
609+ .statusCode (is (HttpServletResponse .SC_CREATED ));
610+
611+ // Step 3) getAll should return a list only containing the one spec with the updated location id
612+ given ()
613+ .log ().ifValidationFails (LogDetail .ALL , true )
614+ .accept (format )
615+ .queryParam (Controllers .OFFICE , OFFICE )
616+ .queryParam (Controllers .DESIGNATOR_MASK , "*" )
617+ .queryParam (Controllers .ID_MASK , specId )
618+ .queryParam (Controllers .SOURCE_ENTITY , ".*" )
619+ .when ()
620+ .redirects ().follow (true )
621+ .redirects ().max (3 )
622+ .get (PATH )
623+ .then ()
624+ .log ().ifValidationFails (LogDetail .ALL , true )
625+ .assertThat ()
626+ .statusCode (is (HttpServletResponse .SC_OK ))
627+ // verify it is an array with 2 elements and contains both spec-ids
628+ .body ("size()" , equalTo (1 ))
629+ .body ("[0].designator" , equalTo (designator ))
630+ .body ("[0].location-id" , equalTo (locationId2 ))
631+ ;
632+
633+ // Step 4) Verify the forecast instance is still retrievable and points to the updated spec/location
634+ given ()
635+ .log ().ifValidationFails (LogDetail .ALL , true )
636+ .accept (format )
637+ .queryParam (Controllers .OFFICE , OFFICE )
638+ .queryParam (Controllers .DESIGNATOR , designator )
639+ // These match the values in forecast_inst_create.json
640+ .queryParam (Controllers .FORECAST_DATE , "2021-06-21T14:00:00+00:00" )
641+ .queryParam (Controllers .ISSUE_DATE , "2022-05-22T12:03:00+00:00" )
642+ .when ()
643+ .redirects ().follow (true )
644+ .redirects ().max (3 )
645+ .get (ForecastInstanceControllerTestIT .PATH + specId )
646+ .then ()
647+ .log ().ifValidationFails (LogDetail .ALL , true )
648+ .assertThat ()
649+ .statusCode (is (HttpServletResponse .SC_OK ))
650+ .body ("spec.spec-id" , equalTo (specId ))
651+ .body ("spec.designator" , equalTo (designator ))
652+ .body ("spec.location-id" , equalTo (locationId2 ));
653+
654+ // Step 5) Delete specs/instances
655+ truncateFcstTimeSeries ();
656+
657+ // Step 6) Verify getAll no longer returns the deleted specs
658+ given ()
659+ .log ().ifValidationFails (LogDetail .ALL , true )
660+ .accept (format )
661+ .queryParam (Controllers .OFFICE , OFFICE )
662+ .queryParam (DESIGNATOR , "*" )
663+ .queryParam (ID_MASK , specId + "*" )
664+ .queryParam (Controllers .SOURCE_ENTITY , ".*" )
665+ .when ()
666+ .redirects ().follow (true )
667+ .redirects ().max (3 )
668+ .get (PATH )
669+ .then ()
670+ .log ().ifValidationFails (LogDetail .ALL , true )
671+ .assertThat ()
672+ .statusCode (is (HttpServletResponse .SC_OK ))
673+ // Expect empty array
674+ .body ("size()" , equalTo (0 ))
675+ ;
676+ }
677+
480678 @ ParameterizedTest
481679 @ ValueSource (strings = {Formats .JSONV2 , Formats .DEFAULT })
482680 void create_getAll_with_entity_like_delete_getAll (String format ) throws Exception {
0 commit comments