4444import org .junit .jupiter .api .BeforeEach ;
4545import org .junit .jupiter .api .Test ;
4646import org .mockito .MockitoAnnotations ;
47+ import org .slf4j .Logger ;
48+ import org .slf4j .LoggerFactory ;
4749import org .springframework .beans .factory .annotation .Autowired ;
4850import org .springframework .boot .test .autoconfigure .web .servlet .AutoConfigureMockMvc ;
4951import org .springframework .boot .test .context .SpringBootTest ;
7375import static org .gridsuite .computation .service .NotificationService .*;
7476import static org .gridsuite .securityanalysis .server .SecurityAnalysisProviderMock .*;
7577import static org .gridsuite .securityanalysis .server .service .SecurityAnalysisService .COMPUTATION_TYPE ;
78+ import static org .gridsuite .securityanalysis .server .util .CsvExportUtils .csvRowsToZippedCsv ;
7679import static org .gridsuite .securityanalysis .server .util .DatabaseQueryUtils .assertRequestsCount ;
7780import static org .gridsuite .securityanalysis .server .util .TestUtils .assertLogMessage ;
81+ import static org .gridsuite .securityanalysis .server .util .TestUtils .readLinesFromFilePath ;
7882import static org .hamcrest .MatcherAssert .assertThat ;
7983import static org .junit .jupiter .api .Assertions .*;
8084import static org .mockito .ArgumentMatchers .any ;
9397@ SpringBootTest
9498@ ContextConfigurationWithTestChannel
9599class SecurityAnalysisControllerTest {
100+ private static final Logger LOGGER = LoggerFactory .getLogger (SecurityAnalysisControllerTest .class );
96101
97102 private static final UUID NETWORK_UUID = UUID .fromString ("7928181c-7977-4592-ba19-88027e4254e4" );
98103 private static final UUID NETWORK_STOP_UUID = UUID .fromString ("7928181c-7977-4592-ba19-88027e4254e6" );
@@ -512,7 +517,7 @@ private void checkNResultEnumFilters(UUID resultUuid) throws Exception {
512517 List <LimitViolationType > limitTypes = mapper .readValue (mvcResult .getResponse ().getContentAsString (), new TypeReference <>() { });
513518 Assertions .assertThat (limitTypes ).hasSameElementsAs (expectedLimitTypes );
514519
515- List <ThreeSides > expectedSides = nResults .stream ().map (result -> result .getLimitViolation ().getSide ()).distinct ().filter (side -> side != null ).toList ();
520+ List <ThreeSides > expectedSides = nResults .stream ().map (result -> result .getLimitViolation ().getSide ()).distinct ().filter (Objects :: nonNull ).toList ();
516521 mvcResult = mockMvc .perform (get ("/" + VERSION + "/results/{resultUuid}/n-branch-sides" , resultUuid ))
517522 .andExpectAll (
518523 status ().isOk (),
@@ -835,7 +840,7 @@ void getDefaultProviderTest() throws Exception {
835840
836841 @ Test
837842 void getZippedCsvResults () throws Exception {
838- // running computation to create result
843+ // running computation to create some results
839844 MvcResult mvcResult = mockMvc .perform (post ("/" + VERSION + "/networks/" + NETWORK_UUID + "/run-and-save?reportType=SecurityAnalysis&contingencyListName=" + CONTINGENCY_LIST_NAME
840845 + "&receiver=me&variantId=" + VARIANT_2_ID + "&provider=OpenLoadFlow" + "&loadFlowParametersUuid=" + UUID .randomUUID ())
841846 .header (HEADER_USER_ID , "testUserId" )
@@ -856,14 +861,50 @@ void getZippedCsvResults() throws Exception {
856861 checkAllZippedCsvResults ();
857862 }
858863
864+ @ Test
865+ void getZippedCsvResultsFromCustomData () throws Exception {
866+ final String expectedCsvFile = "/results/n-result-fr-custom.csv" ;
867+ final String lang = "fr" ;
868+ final List <String > header = getCsvHeaderFromResource (expectedCsvFile , lang );
869+
870+ // Build binary/zipped data from custom DTOs rather than from AS results.
871+ PreContingencyLimitViolationResultDTO dto = PreContingencyLimitViolationResultDTO .builder ()
872+ .subjectId ("Ouvrage" )
873+ .limitViolation (LimitViolationDTO .builder ()
874+ .locationId ("BUS" )
875+ .limitName ("lim_name" )
876+ .acceptableDuration (100 )
877+ .patlLoading (111.5 )
878+ .patlLimit (80.66 )
879+ .upcomingAcceptableDuration (Integer .MAX_VALUE )
880+ .build ())
881+ .build ();
882+ PreContingencyLimitViolationResultDTO dto2 = PreContingencyLimitViolationResultDTO .builder ()
883+ .subjectId ("Ouvrage2" )
884+ .limitViolation (LimitViolationDTO .builder ()
885+ .locationId ("BUS2" )
886+ .limitName ("lim_name2" )
887+ .acceptableDuration (100 )
888+ .patlLoading (111.5 )
889+ .patlLimit (80.66 )
890+ .upcomingAcceptableDuration (1000 )
891+ .build ())
892+ .build ();
893+ List <List <String >> csvRows = List .of (dto .toCsvRow (ENUM_TRANSLATIONS_FR , lang ), dto2 .toCsvRow (ENUM_TRANSLATIONS_FR , lang ));
894+ byte [] resultAsByteArray = csvRowsToZippedCsv (header , lang , csvRows );
895+ // and compare with the expected file
896+ checkCsvResultFromBytes (expectedCsvFile , resultAsByteArray );
897+ }
898+
899+ private List <String > getCsvHeaderFromResource (String resourcePath , String lang ) {
900+ List <String > lines = readLinesFromFilePath (resourcePath , 1 );
901+ String header = lines .isEmpty () ? "" : lines .getFirst ();
902+ return Arrays .asList (header .strip ().split ("en" .equalsIgnoreCase (lang ) ? "," : ";" ));
903+ }
904+
859905 private void checkAllZippedCsvResults () throws Exception {
860906 SQLStatementCountValidator .reset ();
861- checkZippedCsvResult ("n-result" , "/results/n-result-en.csv" ,
862- CsvTranslationDTO .builder ()
863- .headers (List .of ("Equipment" , "Violation type" , "Limit name" , "Limit value (A or kV)" , "Calculated value (A or kV)" , "Load (%)" , "Overload" , "Side" ))
864- .enumValueTranslations (ENUM_TRANSLATIONS_EN )
865- .language ("en" )
866- .build ());
907+ checkZippedCsvResult ("n-result" , "/results/n-result-en.csv" , "en" );
867908 /*
868909 * SELECT
869910 * assert result exists
@@ -872,20 +913,11 @@ private void checkAllZippedCsvResults() throws Exception {
872913 assertRequestsCount (2 , 0 , 0 , 0 );
873914
874915 SQLStatementCountValidator .reset ();
875- checkZippedCsvResult ("n-result" , "/results/n-result-fr.csv" ,
876- CsvTranslationDTO .builder ()
877- .headers (List .of ("Ouvrage" , "Type de contrainte" , "Nom du seuil" , "Valeur du seuil (A ou kV)" , "Valeur calculée (A ou kV)" , "Charge (%)" , "Surcharge" , "Côté" ))
878- .enumValueTranslations (ENUM_TRANSLATIONS_FR )
879- .language ("fr" )
880- .build ());
916+ checkZippedCsvResult ("n-result" , "/results/n-result-fr.csv" , "fr" );
881917
882918 SQLStatementCountValidator .reset ();
883- checkZippedCsvResult ("nmk-contingencies-result" , "/results/nmk-contingencies-result-en.csv" ,
884- CsvTranslationDTO .builder ()
885- .headers (List .of ("Contingency ID" , "Status" , "Constraint" , "Bus" , "Violation type" , "Limit name" , "Limit value (A or kV)" , "Calculated value (A or kV)" , "Load (%)" , "Overload" , "Side" ))
886- .enumValueTranslations (ENUM_TRANSLATIONS_EN )
887- .language ("en" )
888- .build ());
919+ checkZippedCsvResult ("nmk-contingencies-result" , "/results/nmk-contingencies-result-en.csv" , "en" );
920+
889921 /*
890922 * SELECT
891923 * assert result exists
@@ -896,21 +928,11 @@ private void checkAllZippedCsvResults() throws Exception {
896928 assertRequestsCount (4 , 0 , 0 , 0 );
897929
898930 SQLStatementCountValidator .reset ();
899- checkZippedCsvResult ("nmk-contingencies-result" , "/results/nmk-contingencies-result-fr.csv" ,
900- CsvTranslationDTO .builder ()
901- .headers (List .of ("Id aléa" , "Statut" , "Contrainte" , "Noeud électrique" , "Type de contrainte" , "Nom du seuil" , "Valeur du seuil (A ou kV)" , "Charge (%)" , "Surcharge" , "Côté" ))
902- .enumValueTranslations (ENUM_TRANSLATIONS_FR )
903- .language ("fr" )
904- .build ());
931+ checkZippedCsvResult ("nmk-contingencies-result" , "/results/nmk-contingencies-result-fr.csv" , "fr" );
905932 assertRequestsCount (4 , 0 , 0 , 0 );
906933
907934 SQLStatementCountValidator .reset ();
908- checkZippedCsvResult ("nmk-constraints-result" , "/results/nmk-constraints-result-en.csv" ,
909- CsvTranslationDTO .builder ()
910- .headers (List .of ("Constraint" , "Contingency ID" , "Status" , "Bus" , "Violation type" , "Limit name" , "Limit value (A or kV)" , "Calculated value (A or kV)" , "Load (%)" , "Overload" , "Side" ))
911- .enumValueTranslations (ENUM_TRANSLATIONS_EN )
912- .language ("en" )
913- .build ());
935+ checkZippedCsvResult ("nmk-constraints-result" , "/results/nmk-constraints-result-en.csv" , "en" );
914936 /*
915937 * SELECT
916938 * assert result exists
@@ -921,47 +943,55 @@ private void checkAllZippedCsvResults() throws Exception {
921943 assertRequestsCount (4 , 0 , 0 , 0 );
922944
923945 SQLStatementCountValidator .reset ();
924- checkZippedCsvResult ("nmk-constraints-result" , "/results/nmk-constraints-result-fr.csv" ,
925- CsvTranslationDTO .builder ()
926- .headers (List .of ("Contrainte" , "ID aléa" , "Statut" , "Noeud électrique" , "Type de contrainte" , "Nom du seuil" , "Valeur du seuil (A ou kV)" , "Valeur calculée (A ou kV)" , "Charge (%)" , "Surcharge" , "Côté" ))
927- .enumValueTranslations (ENUM_TRANSLATIONS_FR )
928- .language ("fr" )
929- .build ());
946+ checkZippedCsvResult ("nmk-constraints-result" , "/results/nmk-constraints-result-fr.csv" , "fr" );
930947 assertRequestsCount (4 , 0 , 0 , 0 );
931948 }
932949
933- private void checkZippedCsvResult (String resultType , String resourcePath , CsvTranslationDTO csvTranslationDTO ) throws Exception {
934- // get csv file
935- byte [] resultAsByteArray = mockMvc .perform (post ("/" + VERSION + "/results/" + RESULT_UUID + "/" + resultType + "/csv" )
936- .contentType (MediaType .APPLICATION_JSON )
937- .content (mapper .writeValueAsString (csvTranslationDTO )))
938- .andExpectAll (
939- status ().isOk (),
940- content ().contentType (APPLICATION_OCTET_STREAM_VALUE )
941- ).andReturn ().getResponse ().getContentAsByteArray ();
942-
950+ private void checkCsvResultFromBytes (String expectedCsvResource , byte [] resultAsByteArray ) throws Exception {
943951 // get zip file stream
944952 try (ZipInputStream zin = new ZipInputStream (new ByteArrayInputStream (resultAsByteArray ));
945- ByteArrayOutputStream contentOutputStream = new ByteArrayOutputStream ();
946- ByteArrayOutputStream expectedContentOutputStream = new ByteArrayOutputStream ()) {
953+ ByteArrayOutputStream contentOutputStream = new ByteArrayOutputStream ();
954+ ByteArrayOutputStream expectedContentOutputStream = new ByteArrayOutputStream ()) {
947955 // get first entry
948956 ZipEntry zipEntry = zin .getNextEntry ();
949957 // check zip entry name
950958 assertEquals (CsvExportUtils .CSV_RESULT_FILE_NAME , zipEntry .getName ());
951-
952959 // get entry content as outputStream
953960 StreamUtils .copy (zin , contentOutputStream );
954961
955962 // get expected content as outputStream
956- InputStream csvStream = getClass ().getResourceAsStream (resourcePath );
963+ InputStream csvStream = getClass ().getResourceAsStream (expectedCsvResource );
957964 StreamUtils .copy (csvStream , expectedContentOutputStream );
958965
966+ // For debug
967+ LOGGER .info ("CSV result :\n {}" , contentOutputStream );
968+ LOGGER .info ("CSV expected:\n {}" , expectedContentOutputStream );
969+
959970 // using bytearray comparison to check BOM presence in CSV files
960- Assertions .assertThat (expectedContentOutputStream .toByteArray ()).isEqualTo (contentOutputStream .toByteArray ());
971+ Assertions .assertThat (contentOutputStream .toByteArray ()).isEqualTo (expectedContentOutputStream .toByteArray ());
961972 zin .closeEntry ();
962973 }
963974 }
964975
976+ private void checkZippedCsvResult (String resultType , String expectedCsvResource , String lang ) throws Exception {
977+ CsvTranslationDTO csvTranslationDTO = CsvTranslationDTO .builder ()
978+ .headers (getCsvHeaderFromResource (expectedCsvResource , lang ))
979+ .enumValueTranslations ("en" .equalsIgnoreCase (lang ) ? ENUM_TRANSLATIONS_EN : ENUM_TRANSLATIONS_FR )
980+ .language (lang )
981+ .build ();
982+
983+ // get csv file as binary (zip)
984+ byte [] resultAsByteArray = mockMvc .perform (post ("/" + VERSION + "/results/" + RESULT_UUID + "/" + resultType + "/csv" )
985+ .contentType (MediaType .APPLICATION_JSON )
986+ .content (mapper .writeValueAsString (csvTranslationDTO )))
987+ .andExpectAll (
988+ status ().isOk (),
989+ content ().contentType (APPLICATION_OCTET_STREAM_VALUE )
990+ ).andReturn ().getResponse ().getContentAsByteArray ();
991+
992+ checkCsvResultFromBytes (expectedCsvResource , resultAsByteArray );
993+ }
994+
965995 private void assertResultNotFound (UUID resultUuid ) throws Exception {
966996 mockMvc .perform (get ("/" + VERSION + "/results/" + resultUuid + "/n-result" ))
967997 .andExpect (status ().isNotFound ());
0 commit comments