-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PP-12107: Move BinRangeTest from pay-cardid-data (#1206)
* PP-12107: Move BinRangeTest from pay-cardid-data Moving BinRangeTest from pay-cardid-data into this repo as the build-and-push-cardid-candidate CI run will copy the real bin ranges files from s3 into src/main/resources/data-sources. Thus the test will run against real card data in a CI build. To make the test work overlapping ranges in the current test data (in src/main/resources/data-sources) needed to be amended/deleted. Also removed the long comment in CardIdResourceITest as it claims the usefulness of it's tests isn't clear. On the contrary the test is useful as it verifies the API actually works. Removed `-DskipTests` from the Dockerfile as we want to verify the bin ranges don't overlap (via running BinRangesTest) whilst building the Docker image.
- Loading branch information
1 parent
9707636
commit 8a17646
Showing
6 changed files
with
136 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
01,START,END,TYPE,BRAND | ||
02,71221111,71221400,CD,DISCOVER | ||
02,71231111,71241400,CD,DISCOVER | ||
02,71231111,71241100,CD,DISCOVER | ||
02,71241111,71251400,CD,DISCOVER | ||
02,60110009,60110010,CD,DISCOVER | ||
09 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
src/test/java/uk/gov/pay/card/bin_ranges/BinRangeTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package uk.gov.pay.card.bin_ranges; | ||
|
||
|
||
import com.google.common.collect.Range; | ||
import com.google.common.collect.RangeSet; | ||
import com.google.common.collect.TreeRangeSet; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Test; | ||
import uk.gov.pay.card.db.loader.BinRangeParser; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.InputStreamReader; | ||
import java.net.URL; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
|
||
import static java.lang.String.format; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.core.Is.is; | ||
import static org.junit.jupiter.api.Assertions.fail; | ||
|
||
class BinRangeTest { | ||
|
||
private static final int CARD_RANGE_LENGTH = 18; | ||
private static List<Range<Long>> worldpayData; | ||
private static List<Range<Long>> testCardData; | ||
private static List<Range<Long>> discoverData; | ||
|
||
private static final Function<String[], Range<Long>> cardRangeExtractor = entry -> { | ||
Long minCardDigit = BinRangeParser.calculateMinDigitForCardLength(Long.valueOf(entry[1]), CARD_RANGE_LENGTH); | ||
Long maxCardDigit = BinRangeParser.calculateMaxDigitForCardLength(Long.valueOf(entry[2]), CARD_RANGE_LENGTH); | ||
return Range.closed(minCardDigit, maxCardDigit); | ||
}; | ||
|
||
/** | ||
* In the cardid CI pipeline, real bin ranges files will be written to the data-sources directory | ||
*/ | ||
@BeforeAll | ||
static void beforeAll() { | ||
URL worldpayDataSource = Thread.currentThread().getContextClassLoader().getResource("data-sources/worldpay-v3.csv"); | ||
URL testDataSource = Thread.currentThread().getContextClassLoader().getResource("data-sources/test-cards.csv"); | ||
URL discoverDataSource = Thread.currentThread().getContextClassLoader().getResource("data-sources/discover.csv"); | ||
|
||
worldpayData = loadData(worldpayDataSource, "01"); | ||
testCardData = loadData(testDataSource, "02"); | ||
discoverData = loadData(discoverDataSource, "02"); | ||
} | ||
|
||
@Test | ||
void shouldHaveNoOverlapBetweenTestAndWorldpayBinRanges() { | ||
checkForBinRangeOverlaps("Worldpay", worldpayData, "Test Card", testCardData); | ||
} | ||
|
||
@Test | ||
void shouldHaveNoOverlapBetweenTestAndDiscoverBinRanges() { | ||
checkForBinRangeOverlaps("Discover", discoverData, "Test Card", testCardData); | ||
} | ||
|
||
@Test | ||
void shouldHaveNoOverlapBetweenDiscoverAndWorldpayBinRanges() { | ||
checkForBinRangeOverlaps("Worldpay", worldpayData, "Discover", discoverData); | ||
} | ||
|
||
private void checkForBinRangeOverlaps(String ranges1Type, List<Range<Long>> ranges1, String ranges2Type, List<Range<Long>> ranges2) { | ||
RangeSet<Long> rangeSet = TreeRangeSet.create(); | ||
ranges1.forEach(rangeSet::add); | ||
|
||
List<String> overlappingRanges = new ArrayList<>(); | ||
ranges2.forEach(cardRange -> { | ||
if (rangeSet.intersects(cardRange)) { | ||
overlappingRanges.add(String.format("%s range %s intersects %s ranges %s", | ||
ranges1Type, cardRange, ranges2Type, rangeSet.subRangeSet(cardRange))); | ||
} | ||
}); | ||
|
||
if (!overlappingRanges.isEmpty()) { | ||
fail("Found overlaps in ranges:\n" + String.join("\n" , overlappingRanges)); | ||
} | ||
} | ||
|
||
/** | ||
We need to check there is no overlap between ranges across all data sources because some ranges are associated | ||
with corporate card numbers, as identified by the 4th "CP" column in the example line entry from the Worldpay data source: | ||
01,511226661120000000,511226661121000000,CP,DINERS DISCOVER,.... etc | ||
Having no overlap ranges is essential to cardid figuring out with confidence if a card is a corporate card or not. | ||
*/ | ||
@Test | ||
void verifyNoOverlappingRanges() { | ||
RangeSet<Long> setOfBinRanges = TreeRangeSet.create(); | ||
Map<Range<Long>, String> mapOfRanges = new HashMap<>(); | ||
List<Range<Long>> rangesNotInHashMap = new ArrayList<>(); | ||
worldpayData.forEach(cardRange -> { | ||
setOfBinRanges.add(cardRange); | ||
mapOfRanges.put(cardRange, "worldpay"); | ||
}); | ||
discoverData.forEach(cardRange -> { | ||
setOfBinRanges.add(cardRange); | ||
mapOfRanges.put(cardRange, "discover"); | ||
}); | ||
testCardData.forEach(cardRange -> { | ||
setOfBinRanges.add(cardRange); | ||
mapOfRanges.put(cardRange, "test"); | ||
}); | ||
setOfBinRanges.asRanges().forEach(range -> { | ||
String contains = mapOfRanges.get(range); | ||
if (contains == null) { | ||
rangesNotInHashMap.add(range); | ||
} | ||
}); | ||
assertThat(rangesNotInHashMap.size(), is(0)); | ||
} | ||
|
||
private static List<Range<Long>> loadData(URL source, String dataRowIdentifier) { | ||
List<Range<Long>> listOfRanges = new ArrayList<>(); | ||
|
||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source.openStream()))) { | ||
bufferedReader.lines().forEach(line -> { | ||
String[] entry = line.split(","); | ||
if (dataRowIdentifier.equals(entry[0])) { | ||
Range<Long> cardRange = cardRangeExtractor.apply(entry); | ||
listOfRanges.add(cardRange); | ||
} | ||
}); | ||
return listOfRanges; | ||
} catch (Exception e) { | ||
throw new RuntimeException(format("Exception loading file at: %s", source == null ? "(source is null)" : source.toString()), e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters