test(apps): customer-flow smoke test for both apps + 4-digit master-code guard

smoke-apps.cjs walks login (master OTP), catalogue, cart cap, order placement
and order history, with the empty-order and unauthenticated negatives: 43/43.
The cart rules are executed from BOTH apps' own source so the result cannot be
carried by v1 alone.

No behavioural divergence found: all 9 stores are byte-identical, and the router,
API client and composables match except v1's protected variant-image picker.

APP-01: both apps' OTP box is exactly 4 inputs and only submits at 4, so a longer
OTP_MASTER_CODE cannot be typed into them. The login UI was left alone; main.ts
now warns at boot when the code is not 4 digits and .env.example states the rule.

APP-02 logged, not fixed: user-app-v2 has no getHomeSlider, so admin-configured
home slides show in v1 only. Changing v2's hero during a delivery is not worth
the risk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
s.esmaeili
2026-09-23 00:22:50 +03:30
co-authored by Claude Opus 5
parent 0983c42788
commit 8b7da90f51
4 changed files with 370 additions and 1 deletions
+15 -1
View File
@@ -8,7 +8,7 @@ import { AppModule } from './app.module';
import { prepareDatabase } from './prisma/bootstrap';
import { PrismaExceptionFilter } from './prisma/prisma-exception.filter';
import { assertSmsConfigured } from './modules/sms/sms.service';
import { masterOtpEnabled } from './modules/customer-auth/customer-auth.service';
import { masterOtpCode, masterOtpEnabled } from './modules/customer-auth/customer-auth.service';
// serve-static's bundled `mime` version predates .avif, so it falls back to
// application/octet-stream — browsers still render the image (they sniff
@@ -25,6 +25,14 @@ async function bootstrap() {
// D20 — a loud, unmissable banner whenever the delivery bypass is armed, so
// it cannot reach the real release unnoticed.
if (masterOtpEnabled()) {
const code = masterOtpCode() ?? '';
// Both apps render the login OTP as FOUR single-character boxes and only
// submit on @on-complete, i.e. at exactly 4 digits
// (LoginView.vue:43 in each app, OtpInput.vue:43). The API itself accepts
// any length, so a longer master code works over HTTP but can never be
// typed into the apps. Warn rather than reject: an operator may be using it
// for API testing only.
const typable = /^\d{4}$/.test(code);
// eslint-disable-next-line no-console
console.warn(
'\n' +
@@ -32,6 +40,12 @@ async function bootstrap() {
'\n ⚠ OTP_MASTER_CODE فعال است — قبل از انتشار اصلی حذف شود\n' +
' ⚠ OTP_MASTER_CODE IS ACTIVE: any active customer can sign in with\n' +
' ⚠ this fixed code. Remove it from the environment before release.\n' +
(typable
? ''
: ' ⚠\n' +
' ⚠ NOTE: this code is not 4 digits, so it CANNOT be entered in the\n' +
' ⚠ customer apps — their OTP box has exactly 4 boxes. Use a\n' +
' ⚠ 4-digit code for the apps; this one works over the API only.\n') +
'='.repeat(72) +
'\n',
);