@@ -72,6 +72,8 @@ public void setUp() {
7272 downloadWfsDataService = new DownloadWfsDataService (
7373 wfsServer , restTemplate , pretendUserEntity , 16384 , new ObjectMapper ()
7474 );
75+
76+ downloadWfsDataService .self = downloadWfsDataService ;
7577 }
7678
7779 /**
@@ -268,6 +270,7 @@ public void verifyRequestUrlGenerateCorrect() {
268270 );
269271 assertEquals ("https://test.com/geoserver/wfs?VERSION=1.0.0&typeName=test:layer&SERVICE=WFS&REQUEST=GetFeature&outputFormat=shape-zip&cql_filter=((timestamp DURING 2024-01-01T00:00:00Z/2024-12-31T23:59:59Z))" , result , "Correct url 1" );
270272 }
273+
271274 /**
272275 * Make sure the url generated contains the correct polygon
273276 *
@@ -302,6 +305,7 @@ public void verifyRequestUrlGenerateCorrectWithPolygon() throws JsonProcessingEx
302305 result ,
303306 "Correct url 1" );
304307 }
308+
305309 /**
306310 * Verify estimate size on success request
307311 */
@@ -315,25 +319,28 @@ void shouldReturnEstimatedSizeWhenBothRequestsSucceed() {
315319 List <String > fields = List .of ("name" , "area" );
316320 String format = "application/json" ;
317321
318- // 1. Count response: GeoJSON with totalFeatures (1 record requested, but totalFeatures = full count)
322+ // 1. Count response: GeoJSON with totalFeatures (1 record requested, but totalFeatures = full count).
323+ // Returned for BOTH the subset-filtered count and the unfiltered count probe inside
324+ // getBytesPerRecord — both URLs use maxFeatures=1.
319325 String countJson = "{\" totalFeatures\" : 227193, \" features\" : []}" ;
320326 ResponseEntity <String > countResponse = new ResponseEntity <>(countJson , HttpStatus .OK );
321327
322- // 2. Sample response (small payload in requested format)
323- byte [] sampleBytes = "fake data" .getBytes ();
328+ // 2. Sample response. Use a payload >= SAMPLES_SIZE so bytesPerRecord = sampleBytes / 500
329+ // yields a non-zero integer (10 bytes/record here).
330+ byte [] sampleBytes = new byte [DownloadWfsDataService .SAMPLES_SIZE * 10 ];
324331 ResponseEntity <byte []> sampleResponse = new ResponseEntity <>(sampleBytes , HttpStatus .OK );
325332
326333 doReturn (countResponse )
327334 .when (restTemplate ).exchange (
328- argThat ((String url ) -> url != null && url .contains ("maxFeatures=1" )),
329- eq (HttpMethod .GET ),
330- any (HttpEntity .class ),
331- eq (String .class ));
335+ argThat ((String url ) -> url != null && url .contains ("maxFeatures=1" )),
336+ eq (HttpMethod .GET ),
337+ any (HttpEntity .class ),
338+ eq (String .class ));
332339
333340 doReturn (sampleResponse )
334341 .when (restTemplate ).exchange (
335- argThat ((String url ) -> url != null && url .contains ("maxFeatures=" + DownloadWfsDataService .SAMPLES_SIZE )),
336- eq (HttpMethod .GET ), any (), eq (byte [].class ));
342+ argThat ((String url ) -> url != null && url .contains ("maxFeatures=" + DownloadWfsDataService .SAMPLES_SIZE )),
343+ eq (HttpMethod .GET ), any (), eq (byte [].class ));
337344
338345 doReturn (Optional .of ("http://dummy.com/wfs" ))
339346 .when (wfsServer ).getFeatureServerUrl (eq (uuid ), anyString ());
@@ -350,26 +357,45 @@ void shouldReturnEstimatedSizeWhenBothRequestsSucceed() {
350357 BigInteger size = downloadWfsDataService .estimateDownloadSize (
351358 uuid , layer , start , end , multiPolygon , fields , format );
352359
353- // Should call with maxFeatures=1 to get totalFeatures count via GeoJSON
360+ // Subset-filtered count (carries the cql_filter built from start/end dates)
354361 verify (restTemplate ).exchange (
355- argThat ((String url ) -> url != null && url .contains ("maxFeatures=1" ) && url .contains ("outputFormat=application" )),
362+ argThat ((String url ) -> url != null
363+ && url .contains ("maxFeatures=1" )
364+ && url .contains ("outputFormat=application" )
365+ && url .contains ("cql_filter" )),
356366 eq (HttpMethod .GET ),
357367 any (),
358368 eq (String .class )
359369 );
360370
361- // Should also call with maxFeatures=500 to sample bytes for size interpolation
371+ // Unfiltered count probe issued inside getBytesPerRecord — same maxFeatures=1
372+ // pattern but without cql_filter. Acceptance criterion: sample/count path ignores subsetting.
362373 verify (restTemplate ).exchange (
363- argThat ((String url ) -> url != null && url .contains ("maxFeatures=" + DownloadWfsDataService .SAMPLES_SIZE )),
374+ argThat ((String url ) -> url != null
375+ && url .contains ("maxFeatures=1" )
376+ && url .contains ("outputFormat=application" )
377+ && !url .contains ("cql_filter" )),
378+ eq (HttpMethod .GET ),
379+ any (),
380+ eq (String .class )
381+ );
382+
383+ // Sample download with maxFeatures=500, also without subset params.
384+ verify (restTemplate ).exchange (
385+ argThat ((String url ) -> url != null
386+ && url .contains ("maxFeatures=" + DownloadWfsDataService .SAMPLES_SIZE )
387+ && !url .contains ("cql_filter" )),
364388 eq (HttpMethod .GET ),
365389 any (),
366390 eq (byte [].class )
367391 );
368392
369- // totalFeatures=227193, sampleBytes=9 bytes, SAMPLES_SIZE=500
370- long expected = 227193L * sampleBytes .length / DownloadWfsDataService .SAMPLES_SIZE ;
393+ // bytesPerRecord = sampleBytes.length / SAMPLES_SIZE; total = featureCount * bytesPerRecord
394+ long bytesPerRecord = sampleBytes .length / DownloadWfsDataService .SAMPLES_SIZE ;
395+ long expected = 227193L * bytesPerRecord ;
371396 assertEquals (BigInteger .valueOf (expected ), size , "Size match" );
372397 }
398+
373399 @ Test
374400 void shouldReturnZeroWhenTotalFeaturesIsZero () {
375401 String uuid = "lyr-123" ;
@@ -385,10 +411,10 @@ void shouldReturnZeroWhenTotalFeaturesIsZero() {
385411
386412 doReturn (countResponse )
387413 .when (restTemplate ).exchange (
388- argThat ((String url ) -> url != null && url .contains ("maxFeatures=1" )),
389- eq (HttpMethod .GET ),
390- any (HttpEntity .class ),
391- eq (String .class ));
414+ argThat ((String url ) -> url != null && url .contains ("maxFeatures=1" )),
415+ eq (HttpMethod .GET ),
416+ any (HttpEntity .class ),
417+ eq (String .class ));
392418
393419 doReturn (Optional .of ("http://dummy.com/wfs" ))
394420 .when (wfsServer ).getFeatureServerUrl (eq (uuid ), anyString ());
@@ -490,8 +516,53 @@ void returnsNullWhenParserThrowsException() {
490516 .when (wfsServer ).getDownloadableFields (eq (uuid ), any (WfsServer .WfsFeatureRequest .class ));
491517
492518 BigInteger size = downloadWfsDataService .estimateDownloadSize (
493- uuid , layer , start , end , multiPolygon , fields , format );
519+ uuid , layer , start , end , multiPolygon , fields , format );
494520
495521 assertNull (size , "Size should be null when JSON parsing fails" );
496522 }
523+
524+ @ Test
525+ void sampleRequestIgnoresSubsetFilter () throws JsonProcessingException {
526+ String uuid = "lyr-123" ;
527+ String layer = "test:layer" ;
528+ String start = "2024-01-01" ;
529+ String end = "2024-12-31" ;
530+ Object multiPolygon = new ObjectMapper ().readValue (
531+ "{ \" type\" : \" MultiPolygon\" , \" coordinates\" : [[[[0,0],[1,0],[1,1],[0,1],[0,0]]]] }" ,
532+ HashMap .class
533+ );
534+ List <String > fields = List .of ("name" , "area" );
535+ String format = "text/csv" ;
536+
537+ String countJson = "{\" totalFeatures\" : 1000, \" features\" : []}" ;
538+ ResponseEntity <String > countResponse = new ResponseEntity <>(countJson , HttpStatus .OK );
539+ byte [] sampleBytes = new byte [DownloadWfsDataService .SAMPLES_SIZE * 4 ];
540+ ResponseEntity <byte []> sampleResponse = new ResponseEntity <>(sampleBytes , HttpStatus .OK );
541+
542+ doReturn (countResponse )
543+ .when (restTemplate ).exchange (
544+ argThat ((String url ) -> url != null && url .contains ("maxFeatures=1" )),
545+ eq (HttpMethod .GET ), any (HttpEntity .class ), eq (String .class ));
546+ doReturn (sampleResponse )
547+ .when (restTemplate ).exchange (
548+ argThat ((String url ) -> url != null
549+ && url .contains ("maxFeatures=" + DownloadWfsDataService .SAMPLES_SIZE )),
550+ eq (HttpMethod .GET ), any (), eq (byte [].class ));
551+
552+ doReturn (Optional .of ("http://dummy.com/wfs" ))
553+ .when (wfsServer ).getFeatureServerUrl (eq (uuid ), anyString ());
554+ doReturn (createTestWFSFieldModel ())
555+ .when (wfsServer ).getDownloadableFields (eq (uuid ), any (WfsServer .WfsFeatureRequest .class ));
556+
557+ downloadWfsDataService .estimateDownloadSize (uuid , layer , start , end , multiPolygon , fields , format );
558+
559+ // Sample URL must NOT carry the subset filter (no cql_filter, no DURING, no INTERSECTS).
560+ verify (restTemplate ).exchange (
561+ argThat ((String url ) -> url != null
562+ && url .contains ("maxFeatures=" + DownloadWfsDataService .SAMPLES_SIZE )
563+ && !url .contains ("cql_filter" )
564+ && !url .contains ("DURING" )
565+ && !url .contains ("INTERSECTS" )),
566+ eq (HttpMethod .GET ), any (), eq (byte [].class ));
567+ }
497568}
0 commit comments