Repository navigation
fix: validate question input and keep question order unique - #219
Merged
TheCodeHeist-Coder merged 1 commit intoOct 26, 2026
Merged
TheCodeHeist-Coder merged 1 commit into
TheCodeHeist-Coder merged 1 commit into
Conversation
POST /quizzes/:quizId/questions now validates the body with zod and returns a 400 naming the field instead of saving unplayable questions or crashing with a 500. order is max(order) + 1 inside a transaction that locks the quiz row, and deleting a question renumbers the rest. The Add Question form checks Secs (5 to 300) before sending and shows the server error.
TheCodeHeist-Coder
approved these changes
Oct 26, 2026
7 of 19 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adding a question used to trust whatever the request body said. This covers the three things in the issue:
POST /api/v1/quizzes/:quizId/questionsnow validates the body with zod (newutils/questionInput.ts) and returns a 400 that starts with the field name, e.g.timeLimit must be a whole number from 5 to 300. Rules:textnon-empty string,timeLimitinteger 5 to 300 (same bounds as the AI generator on the frontend),difficultyLow/Medium/High (default Medium),answersan array of 2 to 6{ text, isCorrect }with at least one correct. I kept "at least one" correct (not exactly one) so nothing that works today breaks.orderis nowmax(order) + 1, computed in a transaction that locks the quiz row. Deleting a question renumbers the rest to 1..n in the same transaction (utils/questionOrder.ts), which also cleans up quizzes that already have ties.QuizBuilder.tsx: Secs is checked before sending (so-5is no longer saved) and the server error is shown in the form withrole="alert", the same waydeleteErroris shown. Before it was only aconsole.error.I did not add the
@@unique([quizId, order])constraint on purpose. It needs a migration (and a check that existing databases have no ties first), so I think it is better as a follow up.http-server had no test setup, so I added vitest the same way ws-server has it, a
testscript, and a "Test http-server" step inci.yml. The lockfile only gets the one new vitest entry for http-server.Typing -5 in Secs:
Server message shown in the form (I forced a bad value through with Playwright, the normal form can't send one anymore, which is why the box says 20):
Related issue
Closes #213
Type of change
Which services does this touch?
apps/frontendapps/http-serverapps/ws-serverapps/genAIpackages/db(schema or Prisma changes)How did you test this?
Unit tests:
pnpm --filter http-server run test: 23 tests pass (validation schema and the renumber helper).pnpm --filter frontend run test: 81 tests pass, including new ones for bad Secs values not being sent, the empty field falling back to 15, and the server error showing in the form.pnpm --filter ws-server run test: 42 pass (untouched, just checking).Against a real Postgres (throwaway PG 18 with
prisma migrate deployapplied) and the built http-server, I ran the curl cases from the issue before and after:answers needs at least one correct optionanswers: []answers must be an array of 2 to 6 optionstimeLimit: -5timeLimit must be a whole number from 5 to 300answers: "nope"answers must be an array of 2 to 6 optionstimeLimit: 99999999999timeLimit must be a whole number from 5 to 300In the browser (vite + http-server + that database) I opened
/quiz/<id>/edit, clicked Add Question and typed-5in Secs: the form shows "Seconds must be a whole number from 5 to 300" and no request is sent. To see the server message I let a bad value through by rewriting the request body in Playwright (timeLimit: 99999999999), and the form showedtimeLimit must be a whole number from 5 to 300.I only tested with one user. I did not run the live quiz (ws-server) against the renumbered questions, only checked the order values in the database.
Checklist
pnpm run lint(it ran, but the frontend already has ~19 eslint errors on main and exits non-zero;QuizBuilder.tsxhas the same 6 findings as before my change)pnpm run check-typespnpm run builddocker-compose up.envfile, API key or other secretFirst pull request here? CONTRIBUTING.md walks through the
whole process, and a checkbox left unticked is not a problem — say so and we
will help.