Files
pwa/scripts/verify-coach-completion.ts
ar.badieiandCursor 370ac15471 Ship Novya PWA with backend for deployable production use.
Fix production TypeScript builds and add DEPLOY.md so the app can be hosted from this repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 21:01:52 +03:30

116 lines
4.1 KiB
TypeScript

/**
* Offline tests: AI Coach session completion schema + validation.
* Run: npx tsx --tsconfig tsconfig.json scripts/verify-coach-completion.ts
*/
import { getSessionByOrder } from '../src/data/program/sessions';
import { buildAiCoachSessionContext } from '../src/lib/aiCoach/sessionContext';
import {
COACH_SESSION_COMPLETE_CLOSE,
COACH_SESSION_COMPLETE_OPEN,
exampleCompletionSignal,
extractSessionExerciseLabels,
extractSessionHeadings,
parseCoachCompletionFromAssistantText,
validateCoachSessionCompletionSignal,
type CoachSessionCompletionSignal
} from '../src/lib/aiCoach/sessionCompletion';
import { buildCoachSystemPrompt } from '../src/lib/aiCoach/buildCoachSystemPrompt';
const USER_ID = 'verify-coach-user';
const session1 = getSessionByOrder(1);
if (!session1) throw new Error('Session 1 missing');
const ctx = buildAiCoachSessionContext(session1);
const prompt = buildCoachSystemPrompt(ctx);
const validSignal = exampleCompletionSignal(ctx);
const wrap = (signal: CoachSessionCompletionSignal, visible = 'جلسه به پایان رسید.') =>
`${visible}\n${COACH_SESSION_COMPLETE_OPEN}\n${JSON.stringify(signal)}\n${COACH_SESSION_COMPLETE_CLOSE}`;
let passed = 0;
let failed = 0;
const assert = (name: string, condition: boolean) => {
if (condition) {
passed += 1;
return;
}
failed += 1;
console.error(`FAIL: ${name}`);
};
// --- parse ---
const parsed = parseCoachCompletionFromAssistantText(wrap(validSignal));
assert('parse extracts signal', parsed.signal?.sessionComplete === true);
assert('parse strips marker from display', !parsed.displayContent.includes(COACH_SESSION_COMPLETE_OPEN));
assert('parse keeps visible Persian text', parsed.displayContent.includes('جلسه به پایان رسید'));
// --- validate valid ---
const ok = validateCoachSessionCompletionSignal(validSignal, ctx, {
userId: USER_ID,
userTurnCount: 3
});
assert('valid signal passes', ok.valid === true);
// --- reject: single user turn ---
const lowTurns = validateCoachSessionCompletionSignal(validSignal, ctx, {
userId: USER_ID,
userTurnCount: 1
});
assert('rejects single user turn', lowTurns.valid === false);
// --- reject: session id mismatch ---
const badId = validateCoachSessionCompletionSignal(
{ ...validSignal, sessionId: 'session-99' },
ctx,
{ userId: USER_ID, userTurnCount: 3 }
);
assert('rejects sessionId mismatch', badId.valid === false);
// --- reject: user not confirmed ---
const noConfirm = validateCoachSessionCompletionSignal(
{ ...validSignal, userConfirmed: false },
ctx,
{ userId: USER_ID, userTurnCount: 3 }
);
assert('rejects missing userConfirmed', noConfirm.valid === false);
// --- reject: missing exercise ---
const missingExercise = validateCoachSessionCompletionSignal(
{ ...validSignal, completedExercises: [] },
ctx,
{ userId: USER_ID, userTurnCount: 3 }
);
assert('rejects missing completed exercises', missingExercise.valid === false);
// --- reject: insufficient covered topics ---
const thinTopics = validateCoachSessionCompletionSignal(
{ ...validSignal, coveredTopics: ['نامرتبط'] },
ctx,
{ userId: USER_ID, userTurnCount: 3 }
);
assert('rejects insufficient coveredTopics', thinTopics.valid === false);
// --- prompt includes completion instructions ---
assert('prompt includes COACH_SESSION_COMPLETE marker', prompt.includes('COACH_SESSION_COMPLETE'));
assert('prompt includes sessionComplete field', prompt.includes('"sessionComplete":true'));
assert('prompt includes userConfirmed rule', prompt.includes('userConfirmed'));
// --- session 1 has pretest exercise in labels ---
const exerciseLabels = extractSessionExerciseLabels(ctx.blocks);
assert('session 1 has pretest exercise label', exerciseLabels.some((l) => l.includes('پیش‌آزمون')));
const headings = extractSessionHeadings(ctx.blocks);
assert('session 1 has headings', headings.length > 0);
console.log(JSON.stringify({ passed, failed, exerciseLabels, headingCount: headings.length }, null, 2));
if (failed > 0) {
console.error(`Verification failed: ${failed} check(s)`);
process.exit(1);
}
console.log('Coach completion schema + validation OK');