Skip to content

Commit 1f53ee7

Browse files
committed
Wrote unit tests for Card model
1 parent 3bb1f56 commit 1f53ee7

File tree

3 files changed

+271
-10
lines changed

3 files changed

+271
-10
lines changed

lib/src/models/card.dart

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@ class PaymentCard {
6464

6565
String get type {
6666
// If type is empty and the number isn't empty
67-
if (StringUtils.isEmpty(_type) && !StringUtils.isEmpty(number)) {
68-
for (var cardType in cardTypes) {
69-
if (cardType.hasFullMatch(number)) {
70-
return cardType.toString();
67+
if (StringUtils.isEmpty(_type)) {
68+
if (!StringUtils.isEmpty(number)) {
69+
for (var cardType in cardTypes) {
70+
if (cardType.hasFullMatch(number)) {
71+
return cardType.toString();
72+
}
7173
}
7274
}
7375
return CardType.unknown;
@@ -161,17 +163,16 @@ class PaymentCard {
161163
/// Validates the CVC or CVV of a card.
162164
/// Returns true if CVC is valid and false otherwise
163165
bool validCVC(String cardCvc) {
164-
if (cardCvc == null) {
165-
cardCvc = this.cvc;
166-
}
166+
cardCvc ??= this.cvc;
167+
167168
if (cardCvc == null || cardCvc.trim().isEmpty) return false;
168169

169170
var cvcValue = cardCvc.trim();
170171
bool validLength =
171172
((_type == null && cvcValue.length >= 3 && cvcValue.length <= 4) ||
172173
(CardType.americanExpress == _type && cvcValue.length == 4) ||
173174
(CardType.americanExpress != _type && cvcValue.length == 3));
174-
return !(!CardUtils.isWholeNumberPositive(cvcValue) || !validLength);
175+
return (CardUtils.isWholeNumberPositive(cvcValue) && validLength);
175176
}
176177

177178
/// Validates the number of the card
@@ -279,7 +280,8 @@ abstract class CardType {
279280
static final startingPatternAmericanExpress = RegExp(r'((34)|(37))');
280281
static final startingPatternDinersClub =
281282
RegExp(r'((30[0-5])|(3[89])|(36)|(3095))');
282-
static final startingPatternJCB = RegExp(r'(352[89]|35[3-8][0-9])');
283+
static final startingPatternJCB =
284+
RegExp(r'(2131)|(1800)(352[89])|(35[3-8]*[0-9])');
283285
static final startingPatternVerve = RegExp(r'((506(0|1))|(507(8|9))|(6500))');
284286
static final startingPatternDiscover = RegExp(r'((6[45])|(6011))');
285287

test/src/common/case.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:meta/meta.dart';
33
class Case {
44
dynamic inp;
55
dynamic out;
6+
String desc;
67

7-
Case({@required this.inp, @required this.out});
8+
Case({@required this.inp, @required this.out, this.desc});
89
}

test/src/models/card_test.dart

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
import 'package:flutter_paystack/flutter_paystack.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
4+
import '../common/case.dart';
5+
6+
void main() {
7+
group("$PaymentCard", () {
8+
group("#type", () {
9+
final cases = [
10+
Case(inp: null, out: CardType.unknown),
11+
Case(inp: "9765478765567656765", out: CardType.unknown),
12+
Case(inp: "4111111111111111", out: CardType.visa),
13+
Case(inp: "4222 22222 2222", out: CardType.visa),
14+
Case(inp: "5500000000000004", out: CardType.masterCard),
15+
Case(inp: "3400 0000 0000 009", out: CardType.americanExpress),
16+
Case(inp: "30000000000004", out: CardType.dinersClub),
17+
Case(inp: "6011000000000004", out: CardType.discover),
18+
Case(inp: "3530111333300000", out: CardType.jcb),
19+
Case(inp: "5060666666666666666", out: CardType.verve),
20+
Case(
21+
desc: "doesn't return verve type for Ameerican Express number",
22+
inp: "378282246310005",
23+
out: isNot(equals(CardType.verve)),
24+
),
25+
Case(
26+
desc: "returns Unknown for empty number",
27+
inp: "",
28+
out: CardType.unknown,
29+
),
30+
];
31+
cases.forEach((c) {
32+
test(c.desc ?? "returns ${c.out} for ${c.inp}", () {
33+
final value = PaymentCard.empty()..number = c.inp;
34+
expect(value.type, c.out);
35+
});
36+
});
37+
});
38+
39+
group("#getTypeForIIN", () {
40+
final cases = [
41+
Case(inp: null, out: CardType.unknown),
42+
Case(inp: "", out: CardType.unknown),
43+
Case(inp: "000", out: CardType.unknown),
44+
Case(inp: "4", out: CardType.visa),
45+
Case(inp: "4", out: CardType.visa),
46+
Case(inp: "55", out: CardType.masterCard),
47+
Case(inp: "51", out: CardType.masterCard),
48+
Case(inp: "3782", out: CardType.americanExpress),
49+
Case(inp: "305", out: CardType.dinersClub),
50+
Case(inp: "6011", out: CardType.discover),
51+
Case(inp: "3561", out: CardType.jcb),
52+
Case(inp: "5060", out: CardType.verve),
53+
Case(inp: "506 0", out: CardType.verve),
54+
Case(
55+
desc: "doesn't return Ameerican Express type for VISA number",
56+
inp: "4",
57+
out: isNot(equals(CardType.americanExpress))),
58+
];
59+
cases.forEach((c) {
60+
test(c.desc ?? "returns ${c.out} for ${c.inp}", () {
61+
final value = PaymentCard.empty()..number = c.inp;
62+
expect(value.getTypeForIIN(value.number), c.out);
63+
});
64+
});
65+
});
66+
67+
group("#number", () {
68+
final cases = [
69+
Case(inp: "4 22 22222 2222 2", out: "2222"),
70+
Case(
71+
desc:
72+
"setting empty space as card number doesn't generate empty last4Digits",
73+
inp: " ",
74+
out: ""),
75+
Case(inp: "12", out: "12"),
76+
Case(inp: "1298", out: "1298"),
77+
Case(inp: "976 5478 765567656 765", out: "6765"),
78+
Case(inp: "5060666666666666666", out: "6666"),
79+
Case(inp: "随机你,等等", out: ""),
80+
Case(
81+
desc:
82+
"setting 340000000000009 as card number doesn't generate 7688 as last4Digits",
83+
inp: "340000000000009",
84+
out: isNot(equals("7688"))),
85+
];
86+
cases.forEach((c) {
87+
test(
88+
c.desc ??
89+
"setting ${c.inp} as card number generates ${c.out} as last4Digits",
90+
() {
91+
final value = PaymentCard.empty()..number = c.inp;
92+
expect(value.last4Digits, c.out);
93+
});
94+
});
95+
});
96+
97+
group("#number", () {
98+
final cases = [
99+
Case(inp: "4222 22 22 2222 2", out: "4222222222222"),
100+
Case(inp: "5060666666666666666", out: "5060666666666666666"),
101+
Case(inp: "3YSHHjj40000B000000A009", out: "340000000000009"),
102+
Case(inp: "随机你,等等", out: ""),
103+
Case(inp: "随机你,等等124", out: "124"),
104+
];
105+
cases.forEach((c) {
106+
test("${c.inp} is cleaned and assigned as ${c.out}", () {
107+
final value = PaymentCard.empty()..number = c.inp;
108+
expect(value.number, c.out);
109+
});
110+
});
111+
});
112+
113+
group("#cvv", () {
114+
final cases = [
115+
Case(inp: "123", out: "123"),
116+
Case(inp: "așa", out: ""),
117+
Case(inp: "8777等等", out: "8777"),
118+
Case(inp: "9 11 1", out: "9111"),
119+
Case(inp: "随机你,等等124", out: "124"),
120+
Case(
121+
desc: "какая is not assigned as какая",
122+
inp: "какая",
123+
out: isNot(equals("какая"))),
124+
];
125+
cases.forEach((c) {
126+
test(c.desc ?? "${c.inp} is cleaned and assigned as ${c.out}", () {
127+
final value = PaymentCard.empty()..cvc = c.inp;
128+
expect(value.cvc, c.out);
129+
});
130+
});
131+
});
132+
133+
group("#isValid", () {
134+
final cases = [
135+
Case(desc: "empty details", inp: PaymentCard.empty(), out: false),
136+
Case(
137+
desc: "null details",
138+
inp: PaymentCard(
139+
number: null, cvc: null, expiryMonth: null, expiryYear: null),
140+
out: false),
141+
Case(
142+
desc: "empty details",
143+
inp:
144+
PaymentCard(number: "", cvc: "", expiryMonth: 0, expiryYear: 0),
145+
out: false),
146+
Case(
147+
desc: "invalid number and other valid details",
148+
inp: PaymentCard(
149+
number: "9876567876567",
150+
cvc: "123",
151+
expiryMonth: 12,
152+
expiryYear: 12),
153+
out: false),
154+
Case(
155+
desc: "invalid cvv and other valid details",
156+
inp: PaymentCard(
157+
number: "4111111111111111",
158+
cvc: "12333",
159+
expiryMonth: 12,
160+
expiryYear: 12),
161+
out: false),
162+
Case(
163+
desc: "invalid month and other valid details",
164+
inp: PaymentCard(
165+
number: "4111111111111111",
166+
cvc: "123",
167+
expiryMonth: 90,
168+
expiryYear: 12),
169+
out: false),
170+
Case(
171+
desc: "negative month and other valid details",
172+
inp: PaymentCard(
173+
number: "6011000000000004",
174+
cvc: "123",
175+
expiryMonth: -9,
176+
expiryYear: 12),
177+
out: false),
178+
Case(
179+
desc: "negative year and other valid details",
180+
inp: PaymentCard(
181+
number: "4111111111111111",
182+
cvc: "123",
183+
expiryMonth: -9,
184+
expiryYear: -2020),
185+
out: false),
186+
Case(
187+
desc: "four digit year and other valid details",
188+
inp: PaymentCard(
189+
number: "4111111111111111",
190+
cvc: "123",
191+
expiryMonth: 09,
192+
expiryYear: 2021),
193+
out: true),
194+
Case(
195+
desc: "expired card",
196+
inp: PaymentCard(
197+
number: "4111111111111111",
198+
cvc: "123",
199+
expiryMonth: 09,
200+
expiryYear: 2002),
201+
out: false),
202+
Case(
203+
desc: "valid card",
204+
inp: PaymentCard(
205+
number: "6011000000000004",
206+
cvc: "123",
207+
expiryMonth: 09,
208+
expiryYear: 20),
209+
out: true),
210+
];
211+
212+
cases.forEach((c) {
213+
test("returns ${c.out} for ${c.desc}", () {
214+
var valid = c.inp.isValid();
215+
expect(valid, c.out);
216+
});
217+
});
218+
});
219+
220+
group("#validCVC", () {
221+
final cases = [
222+
Case(
223+
desc: "returns false for empty cvv",
224+
inp: PaymentCard.empty()..cvc = "",
225+
out: false),
226+
Case(
227+
desc: "returns false for empty cvv",
228+
inp: PaymentCard.empty()..cvc = null,
229+
out: false),
230+
Case(
231+
desc: "returns false for two character length cvv",
232+
inp: PaymentCard.empty()..cvc = "12",
233+
out: false),
234+
Case(
235+
desc: "returns false for alphanumeric characters",
236+
inp: PaymentCard.empty()..cvc = "A9ri12499",
237+
out: false),
238+
Case(
239+
desc: "returns true for 3 character length cvv",
240+
inp: PaymentCard.empty()..cvc = "123",
241+
out: true),
242+
Case(
243+
desc:
244+
"returns false for 3 character length cvv American Express cards",
245+
inp: PaymentCard.empty()
246+
..type = CardType.americanExpress
247+
..cvc = "123",
248+
out: false),
249+
];
250+
cases.forEach((c) {
251+
test("returns ${c.out} for ${c.inp}", () {
252+
var valid = c.inp.validCVC(null);
253+
expect(valid, c.out);
254+
});
255+
});
256+
});
257+
});
258+
}

0 commit comments

Comments
 (0)