Weekend Build Part 5

Testing, Review & Deployment

Sunday evening in the tutorial plan. After verifying the app locally, run tests, a security review, and a demo deployment check before sharing it.

Last reviewed: May 26 2026

About this series: The Weekend Build is an illustrated tutorial, not a published reference implementation. Test output, audit findings, timing, and deployment output below are examples to reproduce and verify in your own build. No companion repository or live demo is currently published for this series.

M10Testing, Review & Deployment

AI-Generated Test Suite

AI can quickly draft test cases, but a generated suite is only useful after you review its assertions and run it against the actual code. Give AI the implementation to test, then inspect failures and missing coverage.

Backend API Tests

You

Write a complete test suite for the Taskflow API using Vitest and supertest.

Here's the Express app: [paste index.ts]
Auth routes: [paste auth.ts]
Project routes: [paste projects.ts]
Task routes: [paste tasks.ts]

Test structure:
1. Auth tests: register (success + duplicate email + validation), login (success + wrong password + wrong email), token validation
2. Project tests: create (success + missing name), list (only own projects), archive, verify archived projects hidden from list
3. Task tests: create (success + validation), update, delete, move (status change + position calculation), verify completed_at set when moved to done
4. Authorization tests: verify all endpoints reject requests without token, verify users can't access other users' projects

Use a fresh in-memory SQLite database for each test file. Create helper functions for registering a test user and getting a token.

// test/helpers.ts
import { app } from '../src/index';
import request from 'supertest';

export async function createTestUser(overrides = {}) {
  const userData = {
    email: `test-${crypto.randomUUID()}@taskflow.app`,
    password: 'testpassword123',
    name: 'Test User',
    ...overrides,
  };

  const res = await request(app)
    .post('/auth/register')
    .send(userData);

  return {
    user: res.body.user,
    token: res.body.token,
    ...userData,
  };
}

export async function createTestProject(token: string, name = 'Test Project') {
  const res = await request(app)
    .post('/projects')
    .set('Authorization', `Bearer ${token}`)
    .send({ name });

  return res.body.project;
}

Run the test suite. The terminal block below is an example of the target shape of a successful run; your counts and duration will vary:

$ cd server && npx vitest run

✓ auth.test.ts (7 tests)
✓ projects.test.ts (6 tests)
✓ tasks.test.ts (10 tests)
✓ authorization.test.ts (5 tests)

Test Files 4 passed (4)
Tests 28 passed (28)
Duration 1.8s

Treat the displayed 28 passing tests as a tutorial target, not a recorded benchmark. If tests fail, determine whether the implementation or the assertion is wrong, make the appropriate fix, and re-run.

Pro Tip: Fix Failing Tests Critically

When AI-generated tests fail, don't blindly fix the test to match the code. Ask: is the code wrong or is the test wrong? A failing test may reveal either a faulty assumption or an implementation bug, so investigate both before accepting a change.


Security Audit

Before deploying anything, run a security-focused review. This is the checklist from Chapter 11 of the manual, applied to our specific codebase.

You

Perform a security audit on the complete Taskflow codebase.

Backend: [paste all server files]
Frontend: [paste all client files]

Check for:
- SQL injection vulnerabilities
- XSS in rendered content
- Hardcoded secrets or credentials
- Missing input validation
- Authentication bypasses
- Insecure dependencies
- Data exposure in API responses (e.g., returning password_hash)

Rate each finding as Critical, High, or Medium. Show the fix for each.

Example Audit Findings To Verify

SQL injection — All queries parameterized
PASS
Hardcoded secrets — JWT_SECRET from env variable
PASS
User response includes password_hash — strip before returning
CRITICAL
No rate limiting on auth endpoints — add in production
HIGH
CORS allows localhost only — update for production domain
HIGH
XSS — React escapes by default, no dangerouslySetInnerHTML
PASS
Missing Content-Security-Policy header
MEDIUM

If your audit confirms the critical example finding — password_hash exposed in API responses — fix it immediately: add a sanitizeUser function that strips password_hash before returning any user object.

// utils/sanitize.ts
import type { User } from '@taskflow/shared';

export function sanitizeUser(row: Record<string, unknown>): User {
  const { password_hash, ...user } = row;
  return user as User;
}

// Apply in auth routes:
// Before: res.json({ token, user })
// After:  res.json({ token, user: sanitizeUser(user) })
Example Critical Failure

Returning a database row that contains password_hash is a realistic failure mode and a useful audit case. Check your actual auth responses line by line; if they include password hashes, remove them before any deployment.


Deployment

For a weekend build, we want the simplest possible deployment. Two options, both prompt-driven:

Option A: Railway (Recommended)

You

Prepare the Taskflow app for deployment to Railway.

Current structure: monorepo with /client (Vite + React) and /server (Express + SQLite).

I need:
1. Production build script for the client (outputs to /server/public)
2. Express serves the static frontend in production
3. Environment variables: JWT_SECRET, NODE_ENV, PORT
4. CORS updated to allow the production domain
5. SQLite database file persists in a Railway volume
6. Dockerfile or railway.json config

// server/src/index.ts — production static serving
import path from 'path';

if (process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, '../public')));

  // SPA fallback: serve index.html for all non-API routes
  app.get('*', (req, res, next) => {
    if (req.path.startsWith('/auth') || req.path.startsWith('/projects') || req.path.startsWith('/tasks')) {
      return next();
    }
    res.sendFile(path.join(__dirname, '../public/index.html'));
  });
}

Option B: VPS with Docker

If you prefer a VPS, ask AI to generate a Dockerfile and docker-compose.yml. The same build process applies — client builds to static files, server serves them. Add a reverse proxy (Caddy is simplest) for HTTPS.

The following deployment output is illustrative. Run the equivalent build and deploy commands for your chosen host, then record your own URL and checks.

$ npm run build
-- Building client...
-- Client built to server/public (480KB)
-- Building server...
-- Server built to server/dist

$ railway up
-- Deployed to https://taskflow-abc123.railway.app

Demo vs Production: Draw the Line

A weekend build can be deployable without being production-grade in every dimension. Make the boundary explicit so you know what is "good enough for demo" versus what is required before real customer traffic.

Demo Gate (Verify Before Sharing)

Auth + authorization work correctly across all routes
OK
Core CRUD + kanban flows covered by tests and manual smoke test
OK
No obvious critical leaks (e.g., password_hash removed from responses)
OK
Basic monitoring: logs visible and deploy/rollback path tested once
OK

Production-Required (Before Real Users)

Rate limiting + brute-force protection on auth endpoints
REQUIRED
Reliable persistent storage + backups + restore drill
REQUIRED
Security headers (CSP, HSTS) and strict CORS allowlist
REQUIRED
Error tracking + uptime alerts + incident response basics
REQUIRED
Data/privacy readiness (retention policy, deletion path, legal pages)
REQUIRED
Decision Rule

Demo deployment is for feedback and learning. Production deployment is a reliability and risk commitment. If you cannot confidently answer "yes" to every production-required item above, keep the launch labeled as beta/demo and limit exposure.

M10 Target Checkpoint — Tests pass, confirmed findings addressed, demo deployment verified

The Retrospective

Once you have completed and verified your build, take time for the Meta Loop from Chapter 19: what worked, what did not, and what you would do differently.

You

I followed the Taskflow tutorial using an AI-assisted workflow. Retrospective time:

1. What was the most valuable prompt or technique?
2. Where did AI waste the most time (wrong output, needed heavy editing)?
3. What should I add to my personal prompt library?
4. If I started this project over, what would I do differently?

AI

Most valuable: The Design → Critique step in Part 1 can surface risks such as password hash exposure, ordering decisions, and missing CORS before they become deployment blockers.

Most time wasted: The drag-and-drop integration. AI-generated DnD code often has subtle bugs with drop zone detection and position calculation. This required the most manual debugging. Next time: start with a minimal DnD prototype, test it works, then integrate into the full board.

Save to prompt library: The authFetch helper pattern, the security audit prompt, and the "fix top 5, leave the rest" refactor prompt.

Differently: Define the /auth/me endpoint in Part 1 instead of discovering it during frontend implementation. More generally: walk through the frontend component list in planning and identify every API endpoint they'll need — not just the CRUD endpoints.

This Is How You Improve

A retrospective captures what to repeat, what to change, and which checks belong in the next project. Reviewing those notes over multiple builds can improve your workflow without pretending that every mistake is preventable.


Weekend Complete

The tutorial's target outcome is a locally verified Taskflow implementation and, if you complete the deployment checks, a limited demo deployment. Your time, test count, security findings, and deployment result depend on what you implement and verify.

The durable methodology is: design before code, critique before implementation, review before sharing — with AI assisting and you verifying each result.

That is the AI-assisted workflow this tutorial is designed to practice.


Complete Tutorial — Target Scope

Jump to Any Part

01
Part 1 — Planning & Architecture System design, milestones, and shared types.
02
Part 2 — Backend: API & Database Express routes, auth, and SQLite persistence.
03
Part 3 — Frontend: Core UI Auth flow, dashboard shell, and kanban core.
04
Part 4 — Polish & Features Drag-and-drop, filtering, and responsive polish.
05
Part 5 — Testing, Review & Deployment Quality gates, hardening, and demo deployment verification.
Previous Part Part 4 — Polish & Features
Back to Start Part 1 — Planning & Architecture