This commit is contained in:
parent
749f525f83
commit
b0666841f6
7 changed files with 139 additions and 31 deletions
|
|
@ -74,6 +74,15 @@ int main() {
|
|||
"policy: the rest of the world still sells");
|
||||
Check(SellsTo("us"), "policy: matched on the normalised code, like membership");
|
||||
Check(ZoneFor("US") == Zone::World, "zone: refused countries still classify");
|
||||
// Sanctioned destinations refuse through the same gate, but the two lists
|
||||
// stay distinguishable — the checkout error names the law for one and the
|
||||
// shop's own choice for the other.
|
||||
Check(!SellsTo("RU") && !SellsTo("BY") && !SellsTo("KP"),
|
||||
"sanctions: RU, BY and KP refused");
|
||||
Check(IsSanctioned("RU") && IsSanctioned("BY") && IsSanctioned("KP"),
|
||||
"sanctions: the list knows its members");
|
||||
Check(!IsSanctioned("US") && !IsSanctioned("NL"),
|
||||
"sanctions: the insurance refusal is not a sanctions refusal");
|
||||
|
||||
// ── carrier weight brackets ───────────────────────────────────────
|
||||
// The only shipping prices that exist. A ladder covering 2 kg / 10 kg /
|
||||
|
|
@ -150,6 +159,10 @@ int main() {
|
|||
Check(!CurrencyFor(cc).has_value(),
|
||||
"fx: refused destinations have no display currency", cc);
|
||||
}
|
||||
for (const std::string_view cc : SanctionedCountries()) {
|
||||
Check(!CurrencyFor(cc).has_value(),
|
||||
"fx: sanctioned destinations have no display currency", cc);
|
||||
}
|
||||
|
||||
// ── rates loader ──────────────────────────────────────────────────
|
||||
const Rates r = LoadRates(
|
||||
|
|
|
|||
|
|
@ -480,6 +480,15 @@ void AlwaysOnValidation(TestServer& srv) {
|
|||
"email=ca%40example.org&name=Terry&street=1%20Bloor%20St&postal=M4W&city=Toronto&country=CA");
|
||||
srv.CheckStatus("/shop/fp6-pmos", "422", "POST",
|
||||
"email=us%40example.org&name=Pat&street=1%20Main%20St&postal=43004&city=Columbus&country=us");
|
||||
// Sanctioned destinations (Money::SanctionedCountries) refuse through the
|
||||
// same always-on gate — this refusal is the law, so of all the checks in
|
||||
// this file it is the one that must survive every refactor.
|
||||
srv.CheckStatus("/shop/fp6-pmos", "422", "POST",
|
||||
"email=ru%40example.org&name=Sasha&street=1%20Tverskaya&postal=125009&city=Moscow&country=RU");
|
||||
srv.CheckStatus("/shop/fp6-pmos", "422", "POST",
|
||||
"email=by%40example.org&name=Vanya&street=1%20Kastrychnitskaya&postal=220030&city=Minsk&country=BY");
|
||||
srv.CheckStatus("/shop/fp6-pmos", "422", "POST",
|
||||
"email=ru%40example.org&name=Sasha&street=1%20Tverskaya&postal=125009&city=Moscow&country=ru");
|
||||
// Refused in validation means nothing reached the ledger and no payment
|
||||
// link was ever created.
|
||||
Check(LedgerLines(srv).size() == before, "a refused destination creates no order record");
|
||||
|
|
@ -514,13 +523,28 @@ void RejectedFormEcho(TestServer& srv) {
|
|||
Check(refused.body.find("value=\"Pat\"") != std::string::npos,
|
||||
"a refused submission keeps what was typed");
|
||||
}
|
||||
// A sanctioned destination gets the sanctions sentence, not the policy
|
||||
// one — the buyer should learn the law forbids the sale, not wonder what
|
||||
// insurance has to do with Moscow.
|
||||
{
|
||||
const auto refused = srv.Post("/shop/fp6-pmos",
|
||||
"email=ru%40example.org&name=Sasha&street=1%20Tverskaya&postal=125009&city=Moscow&country=RU");
|
||||
Check(refused.body.find("EU sanctions prohibit") != std::string::npos,
|
||||
"sanctions refusal explains itself on the form");
|
||||
Check(refused.body.find("field__error\">Catcrafts does not sell") == std::string::npos,
|
||||
"sanctions refusal is not worded as the policy one");
|
||||
}
|
||||
// The buy panel warns before anyone fills it in, and the preview script
|
||||
// carries the same list so it cannot quote a total the server would
|
||||
// carries the same lists so it cannot quote a total the server would
|
||||
// refuse.
|
||||
srv.BodyHas("/shop/fp6-pmos", "does not sell or ship to the United States or Canada",
|
||||
"buy panel states where the shop does not sell");
|
||||
srv.BodyHas("/shop/fp6-pmos", "cannot sell or ship to Russia, Belarus or North Korea",
|
||||
"buy panel states where the law forbids selling");
|
||||
srv.BodyHas("/shop/fp6-pmos", ""x":["US","CA"]",
|
||||
"total preview knows the refused destinations");
|
||||
srv.BodyHas("/shop/fp6-pmos", ""s":["RU","BY","KP"]",
|
||||
"total preview knows the sanctioned destinations");
|
||||
// The honeypot message must not name the trap, or it teaches the next
|
||||
// bot. Only the ERROR NOTICE is inspected: the re-rendered form
|
||||
// legitimately contains the name="website" field itself — that IS the
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue