Spaces:
Sleeping
Sleeping
deploy: scraper pipeline (Postgres storage, ranking, expiry, tracker, hardened LLM plumbing)
6fb7573 Download scripts/test-e2e-http.mjs from mahrooshishaq/SmartfolioBackend: direct link, hf CLI and curl.
- Browser
- Download file 7.25 kB
-
https://huggingface.co/spaces/mahrooshishaq/SmartfolioBackend/resolve/main/scripts/test-e2e-http.mjs
- Command line
-
hf download hf://spaces/mahrooshishaq/SmartfolioBackend/scripts/test-e2e-http.mjs
-
curl -L -o test-e2e-http.mjs https://huggingface.co/spaces/mahrooshishaq/SmartfolioBackend/resolve/main/scripts/test-e2e-http.mjs
7.25 kB
| // Full HTTP E2E against the running backend (port from .env) + docker Postgres. | |
| // Covers Phase 1 (tiers) + Phase 2 (resume tailoring, adaptive follow-ups, model | |
| // answers) + Phase 4.4 (seniority/focus knobs). Forges a JWT for the seeded user. | |
| // Usage: E2E_USER_ID=<seeded-user-uuid> node scripts/test-e2e-http.mjs | |
| // or: node scripts/test-e2e-http.mjs <seeded-user-uuid> | |
| import 'dotenv/config'; | |
| import assert from 'node:assert/strict'; | |
| import { createRequire } from 'node:module'; | |
| const require = createRequire(import.meta.url); | |
| const jwt = require('jsonwebtoken'); | |
| const PORT = (process.env.PORT || '3000').trim(); | |
| const BASE = `http://localhost:${PORT}`; | |
| const userId = (process.env.E2E_USER_ID || process.argv[2] || '').trim(); | |
| if (!userId) { | |
| console.error('Missing seeded user id: set E2E_USER_ID or pass it as the first argument.'); | |
| process.exit(1); | |
| } | |
| const token = jwt.sign( | |
| { sub: userId, email: 'e2e-test@smartfolio.local' }, | |
| process.env.JWT_ACCESS_SECRET, | |
| { expiresIn: '1h' }, | |
| ); | |
| const H = { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }; | |
| const JOB = `We are hiring a Backend Engineer with strong Node.js, TypeScript and PostgreSQL experience. | |
| You will design scalable REST services and own database schema decisions.`; | |
| (async () => { | |
| console.log(`Server: ${BASE} | user: ${userId}\n`); | |
| // 1) GENERATE (standard tier + seniority + focus + resume tailoring) | |
| process.stdout.write('[POST /generate standard +knobs +resume] β¦ '); | |
| let res = await fetch(`${BASE}/mock-interview/generate`, { | |
| method: 'POST', headers: H, | |
| body: JSON.stringify({ | |
| jobDescription: JOB, | |
| lengthTier: 'standard', | |
| seniority: 'senior', | |
| focusAreas: ['Node.js', 'PostgreSQL'], | |
| useResume: true, | |
| }), | |
| }); | |
| assert.equal(res.status, 200, `generate status ${res.status}`); | |
| const gen = await res.json(); | |
| assert.equal(gen.lengthTier, 'standard'); | |
| assert.equal(gen.questions.length, 10, `expected 10, got ${gen.questions.length}`); | |
| const mcq = gen.questions.find((q) => q.type === 'mcq'); | |
| assert.ok(mcq?.options?.length === 4, 'MCQ must expose 4 options'); | |
| for (const q of gen.questions) { | |
| assert.equal(q.correctIndex, undefined, `correctIndex leaked on q${q.id}`); | |
| assert.equal(q.evaluationCriteria, undefined, `evaluationCriteria leaked on q${q.id}`); | |
| } | |
| console.log(`OK β 10 Q, resume+knobs accepted, no keys leaked`); | |
| // 2) ADAPTIVE FOLLOW-UP on a behavioral question with a vague answer | |
| process.stdout.write('[POST /follow-up (vague behavioral answer)] β¦ '); | |
| const behavioral = gen.questions.find((q) => q.type === 'behavioral'); | |
| res = await fetch(`${BASE}/mock-interview/follow-up`, { | |
| method: 'POST', headers: H, | |
| body: JSON.stringify({ sessionId: gen.sessionId, questionId: behavioral.id, answer: 'I worked on some stuff and it went fine.' }), | |
| }); | |
| assert.equal(res.status, 200, `follow-up status ${res.status}`); | |
| const fu = await res.json(); | |
| assert.ok(fu.followUp === null || (typeof fu.followUp === 'string' && fu.followUp.length > 0), 'followUp must be string|null'); | |
| const followUpAnswers = fu.followUp | |
| ? [{ parentQuestionId: behavioral.id, answer: 'Specifically, I migrated a monolith to microservices, cutting p95 latency by 40%.' }] | |
| : []; | |
| console.log(fu.followUp ? `OK β got follow-up: "${fu.followUp.slice(0, 60)}β¦"` : 'OK β model chose no follow-up (valid)'); | |
| // follow-up must not be asked on MCQ types | |
| process.stdout.write('[POST /follow-up on MCQ -> expect null] β¦ '); | |
| res = await fetch(`${BASE}/mock-interview/follow-up`, { | |
| method: 'POST', headers: H, | |
| body: JSON.stringify({ sessionId: gen.sessionId, questionId: mcq.id, answer: 'A' }), | |
| }); | |
| const fuMcq = await res.json(); | |
| assert.equal(fuMcq.followUp, null, 'MCQ must never get a follow-up'); | |
| console.log('OK β null'); | |
| // 3) SUBMIT with follow-up answers -> evaluation + model answers | |
| process.stdout.write('[POST /submit +followUpAnswers] β¦ '); | |
| const answers = gen.questions.map((q) => ({ | |
| questionId: q.id, | |
| answer: q.type === 'mcq' ? 0 : 'I approached it methodically, weighed trade-offs, acted, and measured the outcome.', | |
| })); | |
| res = await fetch(`${BASE}/mock-interview/submit`, { | |
| method: 'POST', headers: H, | |
| body: JSON.stringify({ sessionId: gen.sessionId, answers, followUpAnswers }), | |
| }); | |
| assert.equal(res.status, 200, `submit status ${res.status}`); | |
| const sub = await res.json(); | |
| assert.equal(sub.evaluation.perQuestion.length, 10); | |
| const withIdeal = sub.evaluation.perQuestion.filter((p) => typeof p.idealAnswer === 'string' && p.idealAnswer.length > 0).length; | |
| assert.ok(withIdeal >= 8, `expected most perQuestion to carry idealAnswer, got ${withIdeal}/10`); | |
| // Phase 4.2 β delivery/communication scoring | |
| const d = sub.evaluation.delivery; | |
| assert.ok(d, 'delivery block present'); | |
| assert.equal(typeof d.clarity, 'number'); | |
| assert.equal(typeof d.fillerWords, 'number'); | |
| console.log(`OK β overall ${sub.evaluation.overallScore}/100, model answers on ${withIdeal}/10, delivery {clarity:${d.clarity}, filler:${d.fillerWords}}`); | |
| // 4) GET detail returns tier + followUps | |
| process.stdout.write('[GET /:id] β¦ '); | |
| res = await fetch(`${BASE}/mock-interview/${gen.sessionId}`, { headers: H }); | |
| const detail = await res.json(); | |
| assert.equal(detail.lengthTier, 'standard'); | |
| assert.ok(Array.isArray(detail.followUps)); | |
| console.log(`OK β tier=${detail.lengthTier}, followUps=${detail.followUps.length}, submitted=${!!detail.submittedAt}`); | |
| // 4b) GET progress aggregates the submitted session | |
| process.stdout.write('[GET /progress] β¦ '); | |
| res = await fetch(`${BASE}/mock-interview/progress`, { headers: H }); | |
| assert.equal(res.status, 200, `progress status ${res.status}`); | |
| const prog = await res.json(); | |
| assert.ok(prog.summary.attempts >= 1, 'expected >=1 attempt'); | |
| assert.ok(prog.points.length >= 1, 'expected >=1 point'); | |
| assert.equal(typeof prog.summary.latest, 'number'); | |
| assert.ok(prog.points[prog.points.length - 1].roundScores, 'points carry roundScores'); | |
| console.log(`OK β attempts=${prog.summary.attempts}, latest=${prog.summary.latest}, best=${prog.summary.best}`); | |
| // 4c) TTS proxy β 200 if Kokoro is installed on the Python service, else a graceful 503 | |
| process.stdout.write('[POST /tts] β¦ '); | |
| res = await fetch(`${BASE}/mock-interview/tts`, { | |
| method: 'POST', headers: H, | |
| body: JSON.stringify({ text: 'Tell me about yourself.' }), | |
| }); | |
| assert.ok(res.status === 200 || res.status === 503, `tts status ${res.status} (want 200 or 503)`); | |
| console.log(res.status === 200 | |
| ? `OK β neural audio (${res.headers.get('content-type')})` | |
| : 'OK β 503 (Kokoro not installed; client falls back to browser TTS)'); | |
| // 5) Double submit rejected | |
| process.stdout.write('[POST /submit again -> 400] β¦ '); | |
| res = await fetch(`${BASE}/mock-interview/submit`, { | |
| method: 'POST', headers: H, | |
| body: JSON.stringify({ sessionId: gen.sessionId, answers }), | |
| }); | |
| assert.equal(res.status, 400); | |
| console.log('OK β rejected'); | |
| console.log('\nFull HTTP E2E (Phase 1 + 2 + 4.4) passed β '); | |
| })().catch((e) => { | |
| console.error('\nβ E2E failed:', e?.message || e); | |
| process.exit(1); | |
| }); | |