sanctions block list
All checks were successful
Deploy / build-deploy (push) Successful in 2m18s

This commit is contained in:
Jorijn van der Graaf 2026-08-15 03:22:25 +02:00
commit b0666841f6
7 changed files with 139 additions and 31 deletions

View file

@ -193,6 +193,23 @@ int main() {
Check(us.value.country == "US", "checkout: refused country echoed back");
}
// Sanctioned destinations: same gate, different sentence. The message has
// to name the law rather than shop policy — a buyer told "Catcrafts does
// not sell to Russia" would reasonably email to ask; one told the EU
// forbids it knows nothing can be arranged.
Check(!withCountry("RU").Ok(), "checkout: RU refused");
Check(!withCountry("BY").Ok(), "checkout: BY refused");
Check(!withCountry("KP").Ok(), "checkout: KP refused");
Check(!withCountry("ru").Ok(), "checkout: lowercase RU refused too");
{
auto ru = withCountry("RU");
Check(ru.errors.size() == 1 && ru.errors[0].field == "country",
"checkout: sanctions refusal is a country error, nothing else");
Check(ru.errors[0].message == Catcrafts::Form::kSanctionsMessage,
"checkout: sanctions refusal names the law, not shop policy");
Check(ru.value.country == "RU", "checkout: sanctioned country echoed back");
}
// The shipping refusals. These are templates rather than plain strings
// because the buy page fills the same ones client-side, so the substitution
// has to work on both {cc} and {n} — a template that silently kept its
@ -212,12 +229,16 @@ int main() {
"shipping copy: with nothing fitting it does not promise a quantity");
Check(FillShipMessage("{cc} {n} {cc}", "NL", 2) == "NL 2 NL",
"shipping copy: every placeholder is filled, not just the first");
// Both messages must offer the way out, since the shop is refusing
// business it would otherwise take.
Check(none.find("orders@catcrafts.net") != std::string::npos
&& heavy.find("orders@catcrafts.net") != std::string::npos
&& nofit.find("orders@catcrafts.net") != std::string::npos,
"shipping copy: every refusal names a human to email");
// Only the splittable refusal offers a way to order anyway — that
// buyer's order works as several smaller ones. The other two are
// final: what the carrier can't take, the shop doesn't ship, and a
// refusal that invites hand-arranging would promise exactly the
// ad-hoc export the shop decided against.
Check(heavy.find("orders@catcrafts.net") != std::string::npos,
"shipping copy: the splittable refusal names a human to email");
Check(none.find("orders@catcrafts.net") == std::string::npos
&& nofit.find("orders@catcrafts.net") == std::string::npos,
"shipping copy: unshippable refusals are final, no workaround offered");
}
// A rejected field must still come back, or the visitor has to retype the