Compare commits

...

7 Commits

12 changed files with 505 additions and 127 deletions

View File

@ -71,7 +71,7 @@ The package can be installed by adding `iban_ex` to your list of dependencies in
```elixir
def deps do
[
{:iban_ex, "~> 0.1.6"}
{:iban_ex, "~> 0.1.8"}
]
end
```

View File

@ -41,6 +41,7 @@ defmodule IbanEx.Country do
"IE" => IbanEx.Country.IE,
"IL" => IbanEx.Country.IL,
"IT" => IbanEx.Country.IT,
"IS" => IbanEx.Country.IS,
"JO" => IbanEx.Country.JO,
"KZ" => IbanEx.Country.KZ,
"KW" => IbanEx.Country.KW,

View File

@ -1,4 +1,6 @@
defmodule IbanEx.Country.BG do
# TODO Bulgaria IBAN contains account type (first 2 digits of account number)
@moduledoc """
Bulgaria IBAN parsing rules

45
lib/iban_ex/country/is.ex Normal file
View File

@ -0,0 +1,45 @@
defmodule IbanEx.Country.IS do
# TODO Iceland IBAN contains identification number (last 10 digits of account number)
@moduledoc """
Island IBAN parsing rules
## Examples
```elixir
iex> %IbanEx.Iban{
...> country_code: "IS",
...> check_digits: "14",
...> bank_code: "0159",
...> branch_code: "26",
...> national_check: nil,
...> account_number: "0076545510730339"
...> }
...> |> IbanEx.Country.IS.to_string()
"IS 14 0159 26 0076545510730339"
```
"""
@size 26
@rule ~r/^(?<bank_code>[0-9]{4})(?<branch_code>[0-9]{2})(?<account_number>[0-9]{16})$/i
use IbanEx.Country.Template
@impl IbanEx.Country.Template
@spec to_string(Iban.t()) :: binary()
@spec to_string(Iban.t(), binary()) :: binary()
def to_string(
%Iban{
country_code: country_code,
check_digits: check_digits,
bank_code: bank_code,
branch_code: branch_code,
national_check: _national_check,
account_number: account_number
} = _iban,
joiner \\ " "
) do
[country_code, check_digits, bank_code, branch_code, account_number]
|> Enum.join(joiner)
end
end

View File

@ -9,7 +9,10 @@ defmodule IbanEx.Country.Template do
@callback size() :: size()
@callback rule() :: rule()
@callback incomplete_rule() :: rule()
@callback rules() :: []
@callback rules_map() :: %{}
@callback bban_fields() :: [atom()]
@callback bban_size() :: non_neg_integer()
@callback to_string(Iban.t(), joiner()) :: String.t()
@callback to_string(Iban.t()) :: String.t()
@ -47,26 +50,50 @@ defmodule IbanEx.Country.Template do
@spec rule() :: Regex.t()
def rule(), do: @rule
@doc """
Return Regex without trailing $ for parsing incomplete BBAN (part of IBAN string) (for partial suggestions)
"""
@impl IbanEx.Country.Template
@spec incomplete_rule() :: Regex.t()
def incomplete_rule() do
@spec bban_size() :: integer()
def bban_size() do
{_rules, bban_size} = calculate_rules()
bban_size
end
@impl IbanEx.Country.Template
@spec bban_fields() :: []
def bban_fields(), do: rules_map() |> Map.keys()
@impl IbanEx.Country.Template
@spec rules_map() :: %{}
def rules_map(), do: rules() |> Map.new()
@impl IbanEx.Country.Template
@spec rules() :: []
def rules() do
{rules, _bban_size} = calculate_rules()
rules
end
defp calculate_rules() do
scanner = ~r/\(\?\<([\w_]+)\>(([^{]+)\{(\d+)\})\)/i
source =
@rule
|> Regex.source()
|> String.slice(0..-2//1)
|> String.replace("{", "{0,")
opts =
@rule
|> Regex.opts()
{list, bban_length} =
Regex.scan(scanner, source)
|> Enum.reduce({[], 0}, fn [_part, k, r, _syms, l], {list, position} = acc ->
key = String.to_atom(k)
{:ok, regex} = Regex.compile(r, "i")
length = String.to_integer(l)
left = position
right = left + length - 1
{[{key, %{regex: regex, range: left..right}} | list], right + 1}
end)
Regex.compile!(source, opts)
{Enum.reverse(list), bban_length}
end
defoverridable to_string: 1, to_string: 2, size: 0, rule: 0, incomplete_rule: 0
defoverridable to_string: 1, to_string: 2, size: 0, rule: 0
end
end
end

View File

@ -9,6 +9,10 @@ defmodule IbanEx.Error do
| :can_not_parse_map
| :length_to_long
| :length_to_short
| :invalid_bank_code
| :invalid_account_number
| :invalid_branch_code
| :invalid_national_check
| atom()
@type errors() :: [error()]
@errors [
@ -18,7 +22,11 @@ defmodule IbanEx.Error do
:invalid_checksum,
:can_not_parse_map,
:length_to_long,
:length_to_short
:length_to_short,
:invalid_bank_code,
:invalid_account_number,
:invalid_branch_code,
:invalid_national_check
]
@messages [
@ -28,8 +36,12 @@ defmodule IbanEx.Error do
invalid_checksum: "IBAN's checksum is invalid",
can_not_parse_map: "Can't parse map to IBAN struct",
length_to_long: "IBAN longer then required length",
length_to_short: "IBAN shorter then required length"
]
length_to_short: "IBAN shorter then required length",
invalid_bank_code: "Bank code violates required format",
invalid_account_number: "Account number violates required format",
invalid_branch_code: "Branch code violates required format",
invalid_national_check: "National check symbols violates required format",
]
@spec message(error()) :: String.t()
def message(error) when error in @errors, do: @messages[error]

View File

@ -2,7 +2,7 @@ defmodule IbanEx.Parser do
@moduledoc false
alias IbanEx.{Country, Iban, Validator}
import IbanEx.Commons, only: [normalize_and_slice: 2, blank: 1]
import IbanEx.Commons, only: [normalize_and_slice: 2]
@type iban_string() :: String.t()
@type country_code_string() :: <<_::16>>
@ -20,8 +20,9 @@ defmodule IbanEx.Parser do
@spec parse({:ok, binary()}) :: iban_or_error()
def parse({:ok, iban_string}), do: parse(iban_string)
@spec parse(binary()) :: iban_or_error()
def parse(iban_string) do
def parse(iban_string, options \\ [incomplete: false])
def parse(iban_string, incomplete: false) do
case Validator.validate(iban_string) do
{:ok, valid_iban} ->
iban_map = %{
@ -41,26 +42,65 @@ defmodule IbanEx.Parser do
end
end
def parse(iban_string, incomplete: true) do
iban_map = %{
country_code: country_code(iban_string),
check_digits: check_digits(iban_string)
}
bban = bban(iban_string)
case Country.is_country_code_supported?(iban_map.country_code) do
true ->
result =
parse_bban(bban, iban_map.country_code, incomplete: true)
|> Map.merge(iban_map)
{:ok, struct(Iban, result)}
false ->
{:error, :unsupported_country_code}
end
end
@spec parse_bban(binary(), <<_::16>>) :: map()
def parse_bban(bban_string, country_code, options \\ [incomplete: false])
def parse_bban(bban_string, country_code, incomplete: true) do
Country.country_module(country_code).incomplete_rule()
|> parse_bban_by_regex(bban_string)
case Country.is_country_code_supported?(country_code) do
true ->
country_code
|> Country.country_module()
|> parse_bban_by_country_rules(bban_string)
false ->
%{}
end
end
def parse_bban(bban_string, country_code, _options) do
Country.country_module(country_code).rule()
|> parse_bban_by_regex(bban_string)
def parse_bban(bban_string, country_code, incomplete: false) do
case Country.is_country_code_supported?(country_code) do
true ->
Country.country_module(country_code).rule()
|> parse_bban_by_regex(bban_string)
false ->
%{}
end
end
defp parse_bban_by_country_rules(country_module, bban_string) do
for {field, rule} <- country_module.rules,
into: %{},
do: {field, normalize_and_slice(bban_string, rule.range)}
end
defp parse_bban_by_regex(_regex, nil), do: %{}
defp parse_bban_by_regex(regex, bban_string) do
case Regex.named_captures(regex, bban_string) do
map when is_map(map) ->
for {key, val} <- map,
into: %{},
do: {String.to_atom(key), blank(val)}
do: {String.to_atom(key), val}
nil ->
%{}
@ -68,11 +108,11 @@ defmodule IbanEx.Parser do
end
@spec country_code(iban_string()) :: country_code_string()
def country_code(iban_string), do: normalize_and_slice(iban_string, 0..1) |> blank()
def country_code(iban_string), do: normalize_and_slice(iban_string, 0..1)
@spec check_digits(binary()) :: check_digits_string()
def check_digits(iban_string), do: normalize_and_slice(iban_string, 2..3) |> blank()
def check_digits(iban_string), do: normalize_and_slice(iban_string, 2..3)
@spec bban(binary()) :: binary()
def bban(iban_string), do: normalize_and_slice(iban_string, 4..-1//1) |> blank()
def bban(iban_string), do: normalize_and_slice(iban_string, 4..-1//1)
end

View File

@ -3,10 +3,11 @@ defmodule IbanEx.Validator do
alias IbanEx.{Country, Parser}
alias IbanEx.Validator.Replacements
import IbanEx.Commons, only: [normalize: 1]
import IbanEx.Commons, only: [normalize: 1, normalize_and_slice: 2]
defp error_accumulator(acc, error_message)
defp error_accumulator(acc, {:error, error}), do: [error | acc]
# defp error_accumulator(acc, list) when is_list(list), do: list ++ acc
defp error_accumulator(acc, _), do: acc
defp violation_functions(),
@ -15,12 +16,25 @@ defmodule IbanEx.Validator do
{&__MODULE__.iban_unsupported_country?/1, {:error, :unsupported_country_code}},
{&__MODULE__.iban_violates_length?/1, {:error, :invalid_length}},
{&__MODULE__.iban_violates_country_rule?/1, {:error, :invalid_format_for_country}},
{&__MODULE__.iban_violates_checksum?/1, {:error, :invalid_checksum}}
{&__MODULE__.iban_violates_bank_code_format?/1, {:error, :invalid_bank_code}},
{&__MODULE__.iban_violates_account_number_format?/1, {:error, :invalid_account_number}},
{&__MODULE__.iban_violates_branch_code_format?/1, {:error, :invalid_branch_code}},
{&__MODULE__.iban_violates_national_check_format?/1, {:error, :invalid_national_check}},
{&__MODULE__.iban_violates_checksum?/1, {:error, :invalid_checksum}},
]
@doc """
Accumulate check results in the list of errors
Check iban_violates_format?, iban_unsupported_country?, iban_violates_length?, iban_violates_country_rule?, iban_violates_checksum?
Check
iban_violates_format?,
iban_unsupported_country?,
iban_violates_length?,
iban_violates_country_rule?,
iban_violates_bank_code_format?,
iban_violates_account_number_format?
iban_violates_branch_code_format?,
iban_violates_national_check_format?,
iban_violates_checksum?,
"""
@spec violations(String.t()) :: [] | [atom()]
def violations(iban) do
@ -36,8 +50,11 @@ defmodule IbanEx.Validator do
iban_unsupported_country?,
iban_violates_length?,
iban_violates_country_rule?,
iban_violates_checksum?
iban_violates_bank_code_format?,
iban_violates_account_number_format?,
iban_violates_branch_code_format?,
iban_violates_national_check_format?,
iban_violates_checksum?,
"""
@type iban() :: binary()
@type iban_or_error() ::
@ -46,6 +63,10 @@ defmodule IbanEx.Validator do
| {:invalid_format, binary()}
| {:invalid_length, binary()}
| {:unsupported_country_code, binary()}
| {:invalid_bank_code, binary()}
| {:invalid_account_number, binary()}
| {:invalid_branch_code, binary()}
| {:invalid_national_check, binary()}
@spec validate(String.t()) :: {:ok, String.t()} | {:error, atom()}
def validate(iban) do
@ -54,6 +75,10 @@ defmodule IbanEx.Validator do
iban_unsupported_country?(iban) -> {:error, :unsupported_country_code}
iban_violates_length?(iban) -> {:error, :invalid_length}
iban_violates_country_rule?(iban) -> {:error, :invalid_format_for_country}
iban_violates_bank_code_format?(iban) -> {:error, :invalid_bank_code}
iban_violates_account_number_format?(iban) -> {:error, :invalid_account_number}
iban_violates_branch_code_format?(iban) -> {:error, :invalid_branch_code}
iban_violates_national_check_format?(iban) -> {:error, :invalid_national_check}
iban_violates_checksum?(iban) -> {:error, :invalid_checksum}
true -> {:ok, normalize(iban)}
end
@ -71,6 +96,34 @@ defmodule IbanEx.Validator do
def iban_violates_format?(iban),
do: Regex.match?(~r/[^A-Z0-9]/i, normalize(iban))
# - Check whether a given IBAN violates the required format in bank_code.
@spec iban_violates_bank_code_format?(binary()) :: boolean
def iban_violates_bank_code_format?(iban), do: iban_violates_bban_part_format?(iban, :bank_code)
# - Check whether a given IBAN violates the required format in branch_code.
@spec iban_violates_branch_code_format?(binary()) :: boolean
def iban_violates_branch_code_format?(iban), do: iban_violates_bban_part_format?(iban, :branch_code)
# - Check whether a given IBAN violates the required format in account_number.
@spec iban_violates_account_number_format?(binary()) :: boolean
def iban_violates_account_number_format?(iban), do: iban_violates_bban_part_format?(iban, :account_number)
# - Check whether a given IBAN violates the required format in national_check.
@spec iban_violates_national_check_format?(binary()) :: boolean
def iban_violates_national_check_format?(iban), do: iban_violates_bban_part_format?(iban, :national_check)
defp iban_violates_bban_part_format?(iban, part) do
with country <- Parser.country_code(iban),
bban <- Parser.bban(iban),
true <- Country.is_country_code_supported?(country),
country_module <- Country.country_module(country),
{:ok, rule} <- Map.fetch(country_module.rules_map(), part) do
!Regex.match?(rule.regex, normalize_and_slice(bban, rule.range))
else
_ -> false
end
end
# - Check whether a given IBAN violates the supported countries.
@spec iban_unsupported_country?(String.t()) :: boolean
def iban_unsupported_country?(iban) do
@ -123,7 +176,7 @@ defmodule IbanEx.Validator do
rule <- country_module.rule() do
!Regex.match?(rule, bban)
else
{:error, _error} -> true
_ -> true
end
end

View File

@ -2,7 +2,7 @@ defmodule IbanEx.MixProject do
use Mix.Project
@source_url "https://g.tulz.dev/opensource/iban-ex"
@version "0.1.6"
@version "0.1.8"
def project do
[

View File

@ -0,0 +1,172 @@
defmodule IbanExParserTest do
alias IbanEx.{Country, Iban, Parser}
use ExUnit.Case, async: true
@ibans [
"AL47212110090000000235698741",
"AD1200012030200359100100",
"AT611904300234573201",
"AZ21NABZ00000000137010001944",
"BH67BMAG00001299123456",
"BE68539007547034",
"BA391290079401028494",
"BR1800360305000010009795493C1",
"BG80BNBG96611020345678",
"CR05015202001026284066",
"HR1210010051863000160",
"CY17002001280000001200527600",
"CZ6508000000192000145399",
"DK5000400440116243",
"DO28BAGR00000001212453611324",
"EG380019000500000000263180002",
"SV62CENR00000000000000700025",
"EE382200221020145685",
"FO6264600001631634",
"FI2112345600000785",
"FR1420041010050500013M02606",
"GE29NB0000000101904917",
"DE89370400440532013000",
"GI75NWBK000000007099453",
"GR1601101250000000012300695",
"GL8964710001000206",
"GT82TRAJ01020000001210029690",
"HU42117730161111101800000000",
"IS140159260076545510730339",
"IE29AIBK93115212345678",
"IL620108000000099999999",
"IT60X0542811101000000123456",
"JO94CBJO0010000000000131000302",
"KZ86125KZT5004100100",
"XK051212012345678906",
"KW81CBKU0000000000001234560101",
"LV80BANK0000435195001",
"LB62099900000001001901229114",
"LI21088100002324013AA",
"LT121000011101001000",
"LU280019400644750000",
"MK07250120000058984",
"MT84MALT011000012345MTLCAST001S",
"MR1300020001010000123456753",
"MC5811222000010123456789030",
"ME25505000012345678951",
"NL91ABNA0417164300",
"NO9386011117947",
"PK36SCBL0000001123456702",
"PL61109010140000071219812874",
"PT50000201231234567890154",
"QA58DOHB00001234567890ABCDEFG",
"MD24AG000225100013104168",
"RO49AAAA1B31007593840000",
"SM86U0322509800000000270100",
"SA0380000000608010167519",
"RS35260005601001611379",
"SK3112000000198742637541",
"SI56263300012039086",
"ES9121000418450200051332",
"SE4550000000058398257466",
"CH9300762011623852957",
"TL380080012345678910157",
"TR330006100519786457841326",
"UA213223130000026007233566001",
"AE070331234567890123456",
"GB29NWBK60161331926819",
"VA59001123000012345678",
"VG96VPVG0000012345678901"
]
test "parsing valid IBANs from available countries returns {:ok, %IbanEx.Iban{}}" do
Enum.all?(@ibans, fn iban ->
iban_country =
iban
|> String.upcase()
|> String.slice(0..1)
result =
case {Country.is_country_code_supported?(iban_country), Parser.parse(iban)} do
{true, {:ok, %Iban{}}} ->
true
_ ->
false
end
assert(result, iban)
end)
end
test "parsing invalid IBANs from unavailable countries returns {:error, :unsupported_country_code}" do
invalid_ibans =
[
# Fake country codes
"SD3112000000198742637541",
"SU56263300012039086",
"ZZ9121000418450200051332",
"FU4550000000058398257466",
"GF9300762011623852957",
"FX380080012345678910157",
"RT330006100519786457841326",
"UL213223130000026007233566001",
"AP070331234567890123456",
"FF29NWBK60161331926819",
"VV59001123000012345678",
"GV96VPVG0000012345678901",
# Unsupported now by library
"AA0096VPVG0000012345",
"AO213223130000026",
"AX00213223130000026007",
"BF3112000000198742637541375",
"BI31120000001987",
"BJ31120000001987426375413750",
"BL3112000000198742637541375",
"BY31120000001987426375413754",
"CF3112000000198742637541375",
"CG3112000000198742637541375",
"CI31120000001987426375413750",
"CM3112000000198742637541375",
"CV31120000001987426375413",
"DJ3112000000198742637541375",
"DZ3112000000198742637541",
"GA3112000000198742637541375",
"GF3112000000198742637541375",
"GP3112000000198742637541375",
"GQ3112000000198742637541375",
"GW31120000001987426375413",
"HN31120000001987426375413759",
"IQ311200000019874263754",
"IR311200000019874263754137",
"KM3112000000198742637541375",
"LC311200000019874263754",
"MA31120000001987426375413750",
"MF3112000000198742637541375",
"MG3112000000198742637541375",
"ML31120000001987426375413750",
"MQ3112000000198742637541375",
"MU3112000000198742637541375000",
"MZ31120000001987426375413",
"NC3112000000198742637541375",
"NE31120000001987426375413750",
"NI311200000019874263754137500000",
"PF3112000000198742637541375",
"PM3112000000198742637541375",
"PS311200000019874263754137500",
"RE3112000000198742637541375",
"SC311200000019874263754137500000",
"SN31120000001987426375413750",
"ST31120000001987426375413",
"TD3112000000198742637541375",
"TF3112000000198742637541375",
"TG31120000001987426375413750",
"TN3112000000198742637541",
"WF3112000000198742637541375",
"YT3112000000198742637541375"
]
Enum.all?(
invalid_ibans,
&assert(
match?({:error, :unsupported_country_code}, Parser.parse(&1)),
"expected #{&1} to match {:error, :unsupported_country_code}"
)
)
end
end

View File

@ -1,6 +1,6 @@
defmodule IbanExTest do
alias IbanEx.{Country, Iban, Parser}
use ExUnit.Case, async: true
doctest_file "README.md"
doctest IbanEx.Country.AD
doctest IbanEx.Country.AE
@ -37,6 +37,7 @@ defmodule IbanExTest do
doctest IbanEx.Country.IE
doctest IbanEx.Country.IL
doctest IbanEx.Country.IT
doctest IbanEx.Country.IS
doctest IbanEx.Country.KZ
doctest IbanEx.Country.KW
doctest IbanEx.Country.LB
@ -70,93 +71,4 @@ defmodule IbanExTest do
doctest IbanEx.Country.VA
doctest IbanEx.Country.VG
doctest IbanEx.Country.XK
@ibans [
"AL47212110090000000235698741",
"AD1200012030200359100100",
"AT611904300234573201",
"AZ21NABZ00000000137010001944",
"BH67BMAG00001299123456",
"BE68539007547034",
"BA391290079401028494",
"BR1800360305000010009795493C1",
"BG80BNBG96611020345678",
"CR05015202001026284066",
"HR1210010051863000160",
"CY17002001280000001200527600",
"CZ6508000000192000145399",
"DK5000400440116243",
"DO28BAGR00000001212453611324",
"EG380019000500000000263180002",
"SV62CENR00000000000000700025",
"EE382200221020145685",
"FO6264600001631634",
"FI2112345600000785",
"FR1420041010050500013M02606",
"GE29NB0000000101904917",
"DE89370400440532013000",
"GI75NWBK000000007099453",
"GR1601101250000000012300695",
"GL8964710001000206",
"GT82TRAJ01020000001210029690",
"HU42117730161111101800000000",
"IS140159260076545510730339",
"IE29AIBK93115212345678",
"IL620108000000099999999",
"IT60X0542811101000000123456",
"JO94CBJO0010000000000131000302",
"KZ86125KZT5004100100",
"XK051212012345678906",
"KW81CBKU0000000000001234560101",
"LV80BANK0000435195001",
"LB62099900000001001901229114",
"LI21088100002324013AA",
"LT121000011101001000",
"LU280019400644750000",
"MK07250120000058984",
"MT84MALT011000012345MTLCAST001S",
"MR1300020001010000123456753",
"MC5811222000010123456789030",
"ME25505000012345678951",
"NL91ABNA0417164300",
"NO9386011117947",
"PK36SCBL0000001123456702",
"PL61109010140000071219812874",
"PT50000201231234567890154",
"QA58DOHB00001234567890ABCDEFG",
"MD24AG000225100013104168",
"RO49AAAA1B31007593840000",
"SM86U0322509800000000270100",
"SA0380000000608010167519",
"RS35260005601001611379",
"SK3112000000198742637541",
"SI56263300012039086",
"ES9121000418450200051332",
"SE4550000000058398257466",
"CH9300762011623852957",
"TL380080012345678910157",
"TR330006100519786457841326",
"UA213223130000026007233566001",
"AE070331234567890123456",
"GB29NWBK60161331926819",
"VA59001123000012345678",
"VG96VPVG0000012345678901"
]
test "parsing valid IBANs from available countries returns {:ok, %IbanEx.Iban{}}" do
assert Enum.all?(@ibans, fn iban ->
iban_country = iban |> String.upcase() |> String.slice(0..1)
case {Country.is_country_code_supported?(iban_country), Parser.parse(iban)} do
{true, {:ok, %Iban{}}} ->
true
{false, {:error, :unsupported_country_code}} ->
true
_ ->
false
end
end)
end
end

View File

@ -2,7 +2,121 @@ defmodule IbanExValidatorTest do
alias IbanEx.{Validator}
use ExUnit.Case, async: true
test "check IBANs length" do
@ibans [
"AL47212110090000000235698741",
"AD1200012030200359100100",
"AT611904300234573201",
"AZ21NABZ00000000137010001944",
"BH67BMAG00001299123456",
"BE68539007547034",
"BA391290079401028494",
"BR1800360305000010009795493C1",
"BG80BNBG96611020345678",
"CR05015202001026284066",
"HR1210010051863000160",
"CY17002001280000001200527600",
"CZ6508000000192000145399",
"DK5000400440116243",
"DO28BAGR00000001212453611324",
"EG380019000500000000263180002",
"SV62CENR00000000000000700025",
"EE382200221020145685",
"FO6264600001631634",
"FI2112345600000785",
"FR1420041010050500013M02606",
"GE29NB0000000101904917",
"DE89370400440532013000",
"GI75NWBK000000007099453",
"GR1601101250000000012300695",
"GL8964710001000206",
"GT82TRAJ01020000001210029690",
"HU42117730161111101800000000",
"IS140159260076545510730339",
"IE29AIBK93115212345678",
"IL620108000000099999999",
"IT60X0542811101000000123456",
"JO94CBJO0010000000000131000302",
"KZ86125KZT5004100100",
"XK051212012345678906",
"KW81CBKU0000000000001234560101",
"LV80BANK0000435195001",
"LB62099900000001001901229114",
"LI21088100002324013AA",
"LT121000011101001000",
"LU280019400644750000",
"MK07250120000058984",
"MT84MALT011000012345MTLCAST001S",
"MR1300020001010000123456753",
"MC5811222000010123456789030",
"ME25505000012345678951",
"NL91ABNA0417164300",
"NO9386011117947",
"PK36SCBL0000001123456702",
"PL61109010140000071219812874",
"PT50000201231234567890154",
"QA58DOHB00001234567890ABCDEFG",
"MD24AG000225100013104168",
"RO49AAAA1B31007593840000",
"SM86U0322509800000000270100",
"SA0380000000608010167519",
"RS35260005601001611379",
"SK3112000000198742637541",
"SI56263300012039086",
"ES9121000418450200051332",
"SE4550000000058398257466",
"CH9300762011623852957",
"TL380080012345678910157",
"TR330006100519786457841326",
"UA213223130000026007233566001",
"AE070331234567890123456",
"GB29NWBK60161331926819",
"VA59001123000012345678",
"VG96VPVG0000012345678901"
]
test "Check Account number format positive cases" do
Enum.all?(@ibans, &assert(!Validator.iban_violates_account_number_format?(&1), &1))
end
test "Check National check format positive cases" do
Enum.all?(@ibans, &assert(!Validator.iban_violates_national_check_format?(&1), &1))
end
test "Check Branch code format positive cases" do
Enum.all?(@ibans, &assert(!Validator.iban_violates_branch_code_format?(&1), &1))
end
test "Check Bank code format positive cases" do
Enum.all?(@ibans, &assert(!Validator.iban_violates_bank_code_format?(&1), &1))
end
test "Check Account number format negative cases" do
cases = [
# shorter then need
{"AL4721211009000000023568741", true},
{"AD120001203020035900100", true},
{"AZ21NABZ0000000013701000944", true},
# invalid characters (leters) in number
{"AT6119043002A4573201", true},
{"BH67BMAG000012991A3456", true},
{"BE685390075X7034", true},
{"BA391290079401S28494", true},
{"BR180036030500001000979549CC1", true},
{"HR12100100518630001", true},
# shorter then need and has
# invalid characters (leters) in number
{"BR18003603050000100097CC1", true},
{"CR050152020010262806Ї", true},
# FIXME it is invalid IBAN for Bulgaria — need to change a rules function in Country Template module
# {"BG80BNBG9661102034567Ї", true},
]
Enum.all?(cases, fn {iban, result} ->
assert(Validator.iban_violates_account_number_format?(iban) == result, iban)
end)
end
test "Check IBANs length" do
cases = [
{"FG2112345CC6000007", {:error, :unsupported_country_code}},
{"UK2112345CC6000007", {:error, :unsupported_country_code}},