Codex/create ai demo video workflow#33
Conversation
- Added support for a demo mode in the posting queue, allowing users to simulate posting without actual uploads. - Introduced new environment variable `NEXT_PUBLIC_QUEUE_DEMO_MODE` to toggle demo functionality. - Updated API endpoints to handle demo requests, including special headers and responses for demo jobs. - Enhanced the `usePostingQueue` and `useQueueJob` hooks to accommodate demo mode logic. - Modified `.gitignore` to exclude demo output files and updated `package.json` with new demo-related scripts. This feature improves the testing and demonstration capabilities of the application by providing a safe environment for users to explore posting functionalities without affecting real data.
- Modified test fixture values for Supabase user IDs to ensure consistency across tests. - Enhanced the product hunt demo test to avoid strict-mode collisions by selecting the first occurrence of the "All done!" text. - Added mock responses for new API endpoints including `/api/reddit/subreddit-info` and `/api/pricing` to prevent live calls during E2E tests. - Implemented a mock for the `/api/admin-check` endpoint to simulate admin status. These changes improve the reliability and accuracy of tests by ensuring that mock data and responses are correctly configured.
📝 WalkthroughWalkthroughThis PR introduces a complete demo-mode infrastructure for deterministic video capture. It adds API routes for simulated queue processing, client-side routing to demo endpoints, Playwright test configuration and test specs, documentation, and export tooling. The demo mode is controlled by the Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant usePostingQueue as usePostingQueue Hook
participant API as API Route
participant DemoQueue as Demo Queue Handler
participant Browser as Browser (Recording)
Client->>usePostingQueue: processBatch() with NEXT_PUBLIC_QUEUE_DEMO_MODE=1
usePostingQueue->>usePostingQueue: Detect demo mode, compute queueEndpoint
usePostingQueue->>API: POST /api/queue/demo (or POST /api/queue with demo headers)
API->>DemoQueue: Route to demo handler
DemoQueue->>DemoQueue: Simulate delays & processing
DemoQueue->>API: Stream JSON lines (started, posting, success, complete)
API->>Browser: Stream progress updates
Browser->>Browser: Record video during streaming responses
DemoQueue->>API: Return final completed status
API->>Client: Close stream
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@pages/api/queue/demo.ts`:
- Around line 58-62: The handler in pages/api/queue/demo.ts lacks the demo-mode
guard, so add the same check used in pages/api/queue/submit.ts and
pages/api/queue/process.ts: verify process.env.NEXT_PUBLIC_QUEUE_DEMO_MODE ===
'true' and that the request includes the x-rmp-demo header (or return 405/403 as
those files do) before executing demo logic in the default exported async
function handler; if the guard fails, short-circuit the request with the same
response/status used by the other demo endpoints to prevent demo responses when
demo mode is disabled.
In `@pages/api/queue/submit.ts`:
- Around line 95-99: The parseInt call when deriving sharedFileCount can yield
unexpected results because it lacks an explicit radix; update the conversion in
submit.ts where sharedFileCountField is parsed (variables: sharedFileCountField,
sharedFileCount) to call parseInt(sharedFileCountField as string, 10) so the
value is always interpreted as base 10.
🧹 Nitpick comments (7)
pages/demo/cards.tsx (1)
38-38: Useconstarrow function instead of function declaration.As per coding guidelines, "Use
constarrow functions instead of function declarations (e.g.,const toggle = () =>)".Proposed fix
-export default function DemoCardsPage() { +const DemoCardsPage = () => {And at the end of the file:
export default DemoCardsPage;scripts/export-demo-video.cjs (1)
40-43:hasFfmpegmay silently treat spawn errors as "not found".When
ffmpegbinary doesn't exist,spawnSyncreturns{ status: null, error: <ENOENT> }. Thestatus === 0check happens to returnfalse, which is the desired behavior, but it conflates "not installed" with "crashed on startup." For a utility script this is acceptable, but a more robust check would inspectresult.error:Optional: more explicit check
const hasFfmpeg = () => { const result = spawnSync('ffmpeg', ['-version'], { stdio: 'ignore' }); - return result.status === 0; + return !result.error && result.status === 0; };package.json (1)
11-12: Cross-platform compatibility: inline env var syntax is Unix-only.
NEXT_PUBLIC_QUEUE_DEMO_MODE=1 npx playwright ...won't work on Windows. If Windows support matters, consider usingcross-env.Optional fix using cross-env
- "demo:video": "NEXT_PUBLIC_QUEUE_DEMO_MODE=1 npx playwright test -c playwright.demo.config.ts tests/demo/product-hunt-demo.spec.ts --project=chromium --workers=1 --headed", + "demo:video": "cross-env NEXT_PUBLIC_QUEUE_DEMO_MODE=1 npx playwright test -c playwright.demo.config.ts tests/demo/product-hunt-demo.spec.ts --project=chromium --workers=1 --headed",pages/api/queue/process.ts (2)
64-114: Demo streaming ignores submitted subreddits in favor of hardcoded list.The demo branch streams results for a hardcoded
['pics', 'images', 'gifs']list, regardless of which subreddits the client actually submitted. This works for the current demo test (which selects exactly those three), but if the demo spec ever changes the selected subreddits, the results will be mismatched.Consider reading the item list from the submit payload (or the request) for resilience, or at minimum add a comment explaining the coupling.
67-69: Minor inconsistency between demo and production streaming headers.Demo path sets
text/plain; charset=utf-8,no-cache, no-transform, while production usestext/plain,no-cache. The difference is harmless but unnecessarily inconsistent.Align headers
- res.setHeader('Content-Type', 'text/plain; charset=utf-8'); - res.setHeader('Cache-Control', 'no-cache, no-transform'); - res.setHeader('Connection', 'keep-alive'); + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive');Also applies to: 153-155
pages/api/queue/demo.ts (2)
69-73: Consider wrapping the streaming section in try/catch.If
res.writeorsleepthrows mid-stream (e.g., client disconnects), the error is unhandled. A defensivetry/catcharound the streaming logic (Lines 69–102) would let you close the response cleanly.Proposed fix
+ try { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Cache-Control', 'no-cache, no-transform'); res.setHeader('Connection', 'keep-alive'); res.flushHeaders?.(); const delaySeconds = 2; writeLine(res, { status: 'started', total: parsed.items.length }); await sleep(700); for (let index = 0; index < parsed.items.length; index++) { ... } writeLine(res, { status: 'completed' }); return res.end(); + } catch { + if (!res.writableEnded) res.end(); + }
58-58: Guideline: preferconstarrow functions over function declarations.The coding guidelines state to use
constarrow functions. However, Next.js API routes conventionally useexport default function handler, so this is a minor stylistic note.As per coding guidelines, "Use
constarrow functions instead of function declarations (e.g.,const toggle = () =>)".
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||
| if (req.method !== 'POST') { | ||
| res.setHeader('Allow', 'POST'); | ||
| return res.status(405).json({ error: 'Method not allowed' }); | ||
| } |
There was a problem hiding this comment.
Missing demo-mode guard — endpoint is accessible even when demo mode is disabled.
Unlike pages/api/queue/submit.ts and pages/api/queue/process.ts, which check NEXT_PUBLIC_QUEUE_DEMO_MODE and the x-rmp-demo header before entering demo logic, this endpoint has no such guard. In production (with demo mode off), anyone can hit /api/queue/demo and get simulated responses.
Proposed fix
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
res.setHeader('Allow', 'POST');
return res.status(405).json({ error: 'Method not allowed' });
}
+ const demoEnv = process.env.NEXT_PUBLIC_QUEUE_DEMO_MODE;
+ const demoHeader = req.headers['x-rmp-demo'];
+ const demoHeaderValue = Array.isArray(demoHeader) ? demoHeader[0] : demoHeader;
+ const isDemoEnabled =
+ (demoEnv === 'true' || demoEnv === '1') && demoHeaderValue === '1';
+
+ if (!isDemoEnabled) {
+ return res.status(403).json({ error: 'Demo mode is not enabled' });
+ }
+
const parsed = parseBody(req);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | |
| if (req.method !== 'POST') { | |
| res.setHeader('Allow', 'POST'); | |
| return res.status(405).json({ error: 'Method not allowed' }); | |
| } | |
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | |
| if (req.method !== 'POST') { | |
| res.setHeader('Allow', 'POST'); | |
| return res.status(405).json({ error: 'Method not allowed' }); | |
| } | |
| const demoEnv = process.env.NEXT_PUBLIC_QUEUE_DEMO_MODE; | |
| const demoHeader = req.headers['x-rmp-demo']; | |
| const demoHeaderValue = Array.isArray(demoHeader) ? demoHeader[0] : demoHeader; | |
| const isDemoEnabled = | |
| (demoEnv === 'true' || demoEnv === '1') && demoHeaderValue === '1'; | |
| if (!isDemoEnabled) { | |
| return res.status(403).json({ error: 'Demo mode is not enabled' }); | |
| } | |
| const parsed = parseBody(req); |
🤖 Prompt for AI Agents
In `@pages/api/queue/demo.ts` around lines 58 - 62, The handler in
pages/api/queue/demo.ts lacks the demo-mode guard, so add the same check used in
pages/api/queue/submit.ts and pages/api/queue/process.ts: verify
process.env.NEXT_PUBLIC_QUEUE_DEMO_MODE === 'true' and that the request includes
the x-rmp-demo header (or return 405/403 as those files do) before executing
demo logic in the default exported async function handler; if the guard fails,
short-circuit the request with the same response/status used by the other demo
endpoints to prevent demo responses when demo mode is disabled.
| // Get shared file count from form data | ||
| const sharedFileCountField = Array.isArray(fields.sharedFileCount) | ||
| ? fields.sharedFileCount[0] | ||
| : fields.sharedFileCount; | ||
| const sharedFileCount = sharedFileCountField ? parseInt(sharedFileCountField as string) : 0; |
There was a problem hiding this comment.
Missing radix parameter in parseInt.
parseInt without an explicit radix can cause unexpected behavior in edge cases. Always pass radix 10.
Proposed fix
- const sharedFileCount = sharedFileCountField ? parseInt(sharedFileCountField as string) : 0;
+ const sharedFileCount = sharedFileCountField ? parseInt(sharedFileCountField as string, 10) : 0;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Get shared file count from form data | |
| const sharedFileCountField = Array.isArray(fields.sharedFileCount) | |
| ? fields.sharedFileCount[0] | |
| : fields.sharedFileCount; | |
| const sharedFileCount = sharedFileCountField ? parseInt(sharedFileCountField as string) : 0; | |
| // Get shared file count from form data | |
| const sharedFileCountField = Array.isArray(fields.sharedFileCount) | |
| ? fields.sharedFileCount[0] | |
| : fields.sharedFileCount; | |
| const sharedFileCount = sharedFileCountField ? parseInt(sharedFileCountField as string, 10) : 0; |
🤖 Prompt for AI Agents
In `@pages/api/queue/submit.ts` around lines 95 - 99, The parseInt call when
deriving sharedFileCount can yield unexpected results because it lacks an
explicit radix; update the conversion in submit.ts where sharedFileCountField is
parsed (variables: sharedFileCountField, sharedFileCount) to call
parseInt(sharedFileCountField as string, 10) so the value is always interpreted
as base 10.
Summary by CodeRabbit
New Features
Documentation