Files
tehran_tala/apps/api/scripts/test-master-otp-live.cjs
s.esmaeiliandClaude Opus 5 676ec3ed72 test(api): live D20 verification + document every env var
Two live scripts against a running API, covering both halves of the master-OTP
contract:
  * with OTP_MASTER_CODE set (8/8): signs in, the JWT works on customer/me,
    Persian digits accepted, and the RULE E1 negatives — near-miss code,
    unknown mobile, inactive customer — all still rejected.
  * without it (6/6): the same code returns 401, no boot banner, a real code
    still logs in, and replaying it fails.

Also confirmed from the boot log that with no SMS credentials the server starts
normally and declines to send instead of erroring (D20).

.env.example gained the auth, SMS and upload variables, names only, no values,
with OTP_MASTER_CODE marked delivery-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 21:28:17 +03:30

64 lines
2.9 KiB
JavaScript

/**
* Live end-to-end check of the D20 master OTP against a running API.
* All Persian text is a literal in this file, never a shell argument (D15).
*/
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const API = 'http://localhost:3000/api';
let pass = 0, fail = 0;
const chk = (c, m) => { c ? pass++ : fail++; console.log((c ? ' PASS ' : ' FAIL ') + m); };
const MASTER = '778899';
(async () => {
const c = await prisma.customer.findFirst({
where: { isActive: true, isArchived: false }, select: { id: true, mobile: true, fullName: true },
});
if (!c) { console.log('no active customer to test with'); process.exit(1); }
console.log(`Testing against customer ${c.fullName} (${c.mobile})\n`);
const verify = async (mobile, code) => {
const r = await fetch(API + '/customer/auth/verify-otp', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: mobile, password: code }),
});
return { status: r.status, body: await r.json().catch(() => ({})) };
};
console.log('CASE 1 — the master code signs an active customer in');
const a = await verify(c.mobile, MASTER);
chk(a.status === 200 || a.status === 201, `HTTP ${a.status}`);
chk(typeof a.body.token === 'string' && a.body.token.length > 20, 'a JWT came back');
chk(a.body.customer?.mobile === c.mobile, 'it is THIS customer, not another');
console.log('\n the token actually works on a protected customer route');
const me = await fetch(API + '/customer/me', { headers: { Authorization: 'Bearer ' + a.body.token } });
chk(me.status === 200, `GET customer/me -> ${me.status}`);
console.log('\nCASE 1b — Persian digits for the same master code');
const p = await verify(c.mobile, '۷۷۸۸۹۹');
chk(p.status === 200 || p.status === 201, `HTTP ${p.status} — faToEn applied to the code`);
console.log('\nCASE 2 — NEGATIVE: a near-miss code is still rejected');
const bad = await verify(c.mobile, '778898');
chk(bad.status === 401, `HTTP ${bad.status} (expected 401)`);
chk(!bad.body.token, 'no token issued');
console.log('\nCASE 2b — NEGATIVE: an unknown mobile is rejected even with the master code');
const unknown = await verify('09999999999', MASTER);
chk(unknown.status === 401, `HTTP ${unknown.status} (expected 401)`);
console.log('\nCASE 2c — NEGATIVE: an INACTIVE customer is rejected even with the master code');
const inactive = await prisma.customer.findFirst({ where: { isActive: false }, select: { mobile: true } });
if (inactive) {
const r = await verify(inactive.mobile, MASTER);
chk(r.status === 401, `HTTP ${r.status} (expected 401)`);
} else {
console.log(' (skipped — no inactive customer in the DB)');
}
console.log(`\npassed: ${pass} failed: ${fail}`);
await prisma.$disconnect();
process.exit(fail ? 1 : 0);
})().catch(async (e) => { console.error(e); await prisma.$disconnect(); process.exit(1); });