Skip to main content

Testing Guide

This guide covers testing OneLibro locally before deployment, including Plaid sandbox testing, email testing, subdomain routing, and end-to-end user workflows.


Overview

OneLibro testing includes:

  1. Local Development Testing - Running and testing the app locally
  2. Subdomain Testing - Testing finance and admin subdomains on localhost
  3. Plaid Sandbox Testing - Testing bank connections without real accounts
  4. Email Testing - Testing email templates and delivery
  5. End-to-End Testing - Complete user workflow validation
  6. Database Testing - Verifying data integrity and RLS policies

Local Development Setup

Step 1: Install Dependencies

# Clone repository
git clone https://github.com/yourusername/yatheesh-portfolio.git
cd yatheesh-portfolio

# Install dependencies
npm install

# Or with clean install
npm ci

Step 2: Configure Environment Variables

Create .env.local file in project root:

# Supabase (use your development project)
NEXT_PUBLIC_SUPABASE_URL=https://[your-project].supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...

# Plaid (IMPORTANT: Use sandbox for local testing)
PLAID_CLIENT_ID=your_client_id
PLAID_SECRET=your_sandbox_secret
PLAID_ENV=sandbox

# Encryption (generate with: openssl rand -hex 32)
ENCRYPTION_KEY=your_64_char_hex_string

# Email (Resend - use test mode)
RESEND_API_KEY=re_...
RESEND_FROM_EMAIL=onboarding@resend.dev
RESEND_FROM_NAME=OneLibro

# Cron (for local testing)
CRON_SECRET=local_test_secret

# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
NODE_ENV=development

CRITICAL: Always use PLAID_ENV=sandbox for local development!


Step 3: Start Development Server

# Start Next.js development server with Turbopack
npm run dev

Output:

▲ Next.js 15.0.0 (turbo)
- Local: http://localhost:3000
- Environments: .env.local

✓ Ready in 2.1s

Access Applications:


Testing Subdomain Routing Locally

OneLibro uses subdomain routing to serve different apps from one codebase.

How Subdomain Routing Works Locally

Middleware (middleware.ts) rewrites subdomain requests:

// finance.localhost:3000 → /finance route
if (hostname.startsWith('finance.')) {
url.pathname = `/finance${url.pathname}`;
return NextResponse.rewrite(url);
}

// admin.localhost:3000 → /admin route
if (hostname.startsWith('admin.')) {
url.pathname = `/admin${url.pathname}`;
return NextResponse.rewrite(url);
}

Testing Subdomains

Test Finance Subdomain:

  1. Navigate to: http://finance.localhost:3000
  2. Should show finance app login page
  3. URL should remain finance.localhost:3000 (not redirect to /finance)
  4. Clicking links should stay on finance.localhost:3000

Test Admin Subdomain:

  1. Navigate to: http://admin.localhost:3000
  2. Should show admin login page
  3. URL should remain admin.localhost:3000
  4. Test admin TOTP 2FA flow

Test Main Portfolio:

  1. Navigate to: http://localhost:3000
  2. Should show portfolio homepage
  3. Should NOT redirect to finance or admin

Troubleshooting Subdomain Routing

Issue: finance.localhost:3000 shows 404 or portfolio instead of finance app

Solution 1: Check Browser Compatibility

  • Chrome/Edge: Works by default
  • Firefox: May need network.dns.localDomains configuration
  • Safari: Works by default

Solution 2: Use Hosts File (if subdomain.localhost doesn't work):

Windows (C:\Windows\System32\drivers\etc\hosts):

127.0.0.1 finance.local
127.0.0.1 admin.local

Mac/Linux (/etc/hosts):

127.0.0.1 finance.local
127.0.0.1 admin.local

Then access:

  • http://finance.local:3000
  • http://admin.local:3000

Solution 3: Test with URL Rewrites:

If subdomains don't work, test routes directly:


Plaid Sandbox Testing

The Plaid sandbox environment allows testing without real bank accounts.

Sandbox vs Real Environments

FeatureSandboxDevelopmentProduction
CostFreeFree (100 items)Paid
Real BanksNo (test mode)YesYes
Real CredentialsNoYesYes
Transaction DataFakeRealReal
Rate LimitsNoneLimitedLimited
Use CaseLocal testingPre-launch testingLive app

Always use sandbox for local development.


Sandbox Test Credentials

Plaid provides test credentials for different scenarios:

Successful Connection (most common):

Username: user_good
Password: pass_good

Failed Connection (invalid credentials):

Username: user_bad
Password: pass_bad

MFA Required (multi-factor auth flow):

Username: user_good
Password: pass_good
MFA Code: 1234 (any 4 digits works)

Locked Account (account temporarily locked):

Username: user_account_locked
Password: pass_good

Additional Verification (requires extra step):

Username: user_custom
Password: pass_good

Testing Bank Connection Flow

Step-by-Step:

  1. Start Plaid Link:

    • Navigate to http://finance.localhost:3000
    • Click "Connect Bank Account"
    • Plaid Link modal opens
  2. Search for Test Bank:

    • In search bar, type: "Platypus"
    • Select "First Platypus Bank" (Plaid's test institution)
    • Or search "Chase" for real bank simulation
  3. Enter Test Credentials:

    • Username: user_good
    • Password: pass_good
    • Click "Submit"
  4. Select Accounts:

    • Sandbox returns fake accounts (checking, savings, credit)
    • Select accounts to connect
    • Click "Continue"
  5. Grant Permissions:

    • Review requested permissions
    • Click "Allow" or "Continue"
  6. Verify Success:

    • Plaid Link closes
    • Accounts appear in OneLibro dashboard
    • Check browser console for any errors

Testing Transaction Sync

After connecting a sandbox account:

Initial Sync:

// Plaid sandbox returns fake transactions automatically
// Check database: transactions table should populate

Manual Sync:

  1. Go to Accounts page: http://finance.localhost:3000/accounts
  2. Find connected account
  3. Click "Sync" button
  4. Wait for spinner
  5. Check for success toast: "Synced! Added: X, Modified: Y"

Verify in Database:

-- Check transactions table in Supabase
SELECT * FROM transactions
WHERE account_id = 'your_account_id'
ORDER BY transaction_date DESC
LIMIT 10;

Sandbox Helper Functions

OneLibro includes sandbox testing helpers in lib/plaid.ts:

Reset Login Flow (simulate expired credentials):

import { sandboxResetLogin } from '@/lib/plaid';

// In a test script or API route
await sandboxResetLogin(accessToken);

// Next time user tries to sync, they'll be prompted to re-authenticate

Fire Webhook Manually (test webhook handling):

import { sandboxFireWebhook } from '@/lib/plaid';

// Trigger a transaction update webhook
await sandboxFireWebhook(accessToken, 'DEFAULT_UPDATE');

// Webhook endpoint receives event
// Check /api/plaid/webhook for processing

Webhook Test Codes:

  • DEFAULT_UPDATE - Standard transaction update
  • HISTORICAL_UPDATE - Historical data available
  • INITIAL_UPDATE - Initial transaction fetch complete
  • ITEM_LOGIN_REQUIRED - User needs to relink

Testing Error Scenarios

Invalid Access Token:

// Use expired or wrong access token
// Should trigger error handling and prompt re-authentication

Rate Limiting:

// Make multiple sync requests rapidly
// Should handle gracefully with error message

Network Failure:

// Turn off network mid-request
// Should show error state and retry option

Email Testing

Local Email Testing with Resend

Test Mode: Resend allows testing without sending real emails.

Option 1: Use Resend Test Domain:

RESEND_FROM_EMAIL=onboarding@resend.dev
  • Emails appear in Resend dashboard logs
  • Not delivered to inbox (safe for testing)

Option 2: Use Personal Email:

RESEND_FROM_EMAIL=your-email@gmail.com
  • Emails sent to your inbox
  • Verify templates render correctly
  • Check spam folder if not received

Testing Email Templates

OneLibro has 7+ email templates. Test each:

1. Welcome Email (on signup):

# Sign up new user
# Check email for welcome message
# Verify branding and formatting

2. Invite Request Confirmation:

# Request invite code
# Check email confirming request received

3. Invite Code Email:

# Admin creates invite code
# User receives invite code email
# Verify code is readable and copy-pasteable

4. Budget Alert Email:

# Create budget with threshold
# Manually trigger alert or wait for cron
# Verify alert shows budget name, amount, percentage

5. Password Reset (if implemented):

# Click "Forgot Password"
# Check email for reset link
# Verify link works

Manual Email Testing

Test Send via API Route:

Create temporary API route for testing:

// app/api/test-email/route.ts
import { sendEmail } from '@/lib/email';

export async function GET() {
const result = await sendEmail({
to: 'your-email@example.com',
subject: 'Test Email from OneLibro',
template: 'welcome',
data: {
name: 'Test User',
email: 'test@example.com',
},
});

return Response.json(result);
}

Test:

curl http://localhost:3000/api/test-email

Verify:

  • Check Resend dashboard for delivery status
  • Check inbox for email
  • Inspect template rendering
  • Test on different email clients (Gmail, Outlook, Apple Mail)

Checking Resend Logs

  1. Go to Resend Dashboard
  2. Click "Logs"
  3. View recent emails:
    • Sent: Successfully delivered
    • Delivered: Confirmed receipt
    • Opened: Recipient opened email
    • Clicked: Recipient clicked link
    • Bounced: Failed delivery
    • Complained: Marked as spam

End-to-End Testing

Test complete user workflows from start to finish.

Test 1: New User Signup Flow

Scenario: New user signs up with invite code

Steps:

  1. Navigate to http://finance.localhost:3000
  2. Click "Don't have an invite code? Request one"
  3. Enter email: test@example.com
  4. Submit request
  5. Verify in database: invite_code_requests table has entry
  6. Go to admin dashboard: http://admin.localhost:3000
  7. Login as admin (requires TOTP 2FA setup)
  8. Navigate to Invite Requests
  9. Approve the request
  10. Verify email sent with invite code
  11. Copy invite code from email
  12. Return to finance app signup
  13. Enter email, password, name, and invite code
  14. Submit signup form
  15. Verify:
    • User created in users table
    • Welcome email sent
    • Redirected to dashboard
    • Can access finance app

Test 2: Bank Connection Flow

Scenario: User connects bank account via Plaid

Steps:

  1. Login to finance app
  2. Navigate to dashboard
  3. Click "Connect Bank Account"
  4. Plaid Link opens
  5. Search for "First Platypus Bank"
  6. Select institution
  7. Enter credentials: user_good / pass_good
  8. Select accounts (checking + savings)
  9. Grant permissions
  10. Plaid Link closes
  11. Verify:
    • Accounts appear in dashboard
    • Balances displayed correctly
    • Account cards show institution name
    • Can click "Sync" to refresh transactions

Test 3: Transaction Management

Scenario: View, filter, and export transactions

Steps:

  1. Connect bank account (if not already)
  2. Navigate to Transactions page
  3. Verify transactions displayed
  4. Test date filter:
    • Select "Last 7 days"
    • Verify only recent transactions shown
  5. Test category filter:
    • Select "Food & Drink"
    • Verify filtered results
  6. Test search:
    • Type merchant name (e.g., "Starbucks")
    • Verify search results
  7. Test export:
    • Click "Export to CSV"
    • Verify CSV downloads
    • Open CSV and check data format
  8. Test manual transaction:
    • Click "Add Transaction"
    • Fill form with test data
    • Submit
    • Verify appears in list

Test 4: Budget Creation and Alerts

Scenario: Create budget and trigger alert

Steps:

  1. Navigate to Budgets page
  2. Click "Create Budget"
  3. Fill form:
    • Name: "Groceries Test"
    • Category: "Food & Drink"
    • Amount: $50
    • Period: Monthly
  4. Submit form
  5. Verify budget appears in list
  6. Create transactions exceeding 90% of budget:
    • Add manual transactions totaling $46+ (> 90% of $50)
  7. Trigger budget alert cron manually:
    curl http://localhost:3000/api/cron/budget-alerts \
    -H "Authorization: Bearer local_test_secret"
  8. Verify:
    • Alert email sent
    • budget_alert_history table has entry
    • Duplicate alert prevented (run cron again, no new email)

Test 5: Admin Workflows

Scenario: Admin manages users and invites

Steps:

Setup Admin Account:

  1. Create user in Supabase users table
  2. Set is_admin = true
  3. Generate TOTP secret and save
  4. Setup 2FA in authenticator app

Test Admin Login:

  1. Navigate to http://admin.localhost:3000
  2. Enter admin email and password
  3. Enter TOTP code from authenticator
  4. Verify access to admin dashboard

Test User Management:

  1. Navigate to Users section
  2. View list of users
  3. Check user details (name, email, signup date)
  4. Verify admin flag displayed correctly

Test Invite Code Creation:

  1. Navigate to Invite Codes
  2. Click "Create Invite Code"
  3. Fill form:
    • Code: TESTCODE123
    • Max Uses: 5
    • Expiration: 7 days from now
  4. Submit
  5. Verify code appears in list
  6. Test code usage:
    • Signup with code
    • Check invite_codes table: used_count incremented

Test Invite Request Approval:

  1. User requests invite code (from finance app)
  2. Admin sees request in Invite Requests
  3. Admin clicks "Approve"
  4. Verify:
    • Invite code generated
    • Email sent to requester
    • Request status updated

Database Testing

Testing RLS Policies

Row-Level Security (RLS) ensures users can only access their own data.

Test User Data Isolation:

-- Login as User A
-- Try to query User B's transactions
SELECT * FROM transactions WHERE user_id = 'user_b_id';

-- Should return 0 rows (blocked by RLS)

Test Admin Access:

-- Login as admin user (is_admin = true)
-- Should be able to query all users' data
SELECT * FROM transactions;

-- Should return all transactions

Test Public Access:

-- Without authentication
SELECT * FROM invite_codes WHERE code = 'TESTCODE123';

-- Should succeed (invite codes are publicly readable for validation)

Testing Data Integrity

Foreign Key Constraints:

-- Try to create transaction with non-existent account
INSERT INTO transactions (account_id, amount, ...)
VALUES ('invalid_account_id', 1000, ...);

-- Should fail with foreign key violation

Unique Constraints:

-- Try to create duplicate invite code
INSERT INTO invite_codes (code, ...)
VALUES ('TESTCODE123', ...);

-- Should fail if code already exists

Check Constraints:

-- Try to create budget with negative amount
INSERT INTO budgets (amount_cents, ...)
VALUES (-1000, ...);

-- Should fail (amount must be positive)

Performance Testing

Load Testing Dashboard

Test with Multiple Accounts and Transactions:

  1. Connect 5+ sandbox accounts
  2. Generate 100+ transactions
  3. Navigate to dashboard
  4. Measure load time (should be < 2 seconds)
  5. Check browser console for performance warnings

Tools:

  • Chrome DevTools Performance tab
  • Lighthouse audit
  • React DevTools Profiler

API Response Times

Test API Endpoints:

# Test transaction fetch
time curl http://localhost:3000/api/transactions?userId=test_user_id

# Should respond in < 500ms

Optimize Slow Queries:

  • Add database indexes
  • Use pagination
  • Implement caching (SWR on client)

Automated Testing (Future)

While OneLibro currently uses manual testing, consider adding:

Unit Tests (Jest + React Testing Library):

// Example: Test budget calculation
import { calculateBudgetProgress } from '@/lib/budgets';

test('calculates budget progress correctly', () => {
const budget = { amount_cents: 50000 }; // $500
const spent = 45000; // $450
const progress = calculateBudgetProgress(budget, spent);
expect(progress).toBe(90); // 90%
});

Integration Tests (Playwright or Cypress):

// Example: Test signup flow
test('user can sign up with invite code', async ({ page }) => {
await page.goto('http://finance.localhost:3000');
await page.click('text=Sign Up');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'SecurePass123');
await page.fill('#inviteCode', 'TESTCODE123');
await page.click('button[type=submit]');
await expect(page).toHaveURL(/dashboard/);
});

API Tests (Supertest):

import request from 'supertest';

test('GET /api/accounts requires authentication', async () => {
const response = await request(app).get('/api/accounts');
expect(response.status).toBe(401);
});

Testing Checklist

Before deploying to production, verify:

Local Testing

  • Development server starts without errors
  • All environment variables loaded correctly
  • Finance subdomain works: finance.localhost:3000
  • Admin subdomain works: admin.localhost:3000
  • Main portfolio works: localhost:3000

Plaid Testing

  • Can connect sandbox bank account
  • Accounts appear after connection
  • Transactions sync successfully
  • Manual sync button works
  • Sandbox credentials work: user_good / pass_good
  • Error handling works for bad credentials
  • Access tokens encrypted before storing

Email Testing

  • Welcome email sent on signup
  • Invite code email sent when created
  • Budget alert emails sent
  • Email templates render correctly
  • No emails go to spam
  • Resend logs show successful delivery

User Flows

  • Invite request flow works end-to-end
  • Signup with invite code works
  • Login and session persistence works
  • Bank connection flow completes successfully
  • Transaction filtering and search works
  • CSV export downloads correctly
  • Budget creation and tracking works
  • Budget alerts trigger correctly

Admin Testing

  • Admin login with TOTP 2FA works
  • User management displays correctly
  • Invite code creation works
  • Invite request approval works
  • Admin can view all users' data

Database Testing

  • RLS policies prevent unauthorized access
  • Foreign key constraints enforced
  • Data validation works (no negative amounts, etc.)
  • Transactions isolated per user
  • Admin flag grants full access

Performance

  • Dashboard loads in < 2 seconds
  • API responses < 500ms
  • No console errors or warnings
  • No memory leaks (check with long sessions)

Troubleshooting Common Issues

Issue: "Failed to fetch" errors in console

Cause: API routes not accessible or CORS issue

Solution:

  1. Verify development server running
  2. Check API route exists in app/api/
  3. Check network tab for actual error
  4. Verify environment variables loaded

Cause: Link token creation failing

Solution:

  1. Check PLAID_CLIENT_ID and PLAID_SECRET in .env.local
  2. Verify PLAID_ENV=sandbox
  3. Check browser console for errors
  4. Test link token endpoint manually:
    curl -X POST http://localhost:3000/api/plaid/create-link-token \
    -H "Content-Type: application/json" \
    -d '{"userId": "test_user_id"}'

Issue: Emails not sending

Cause: Resend API key invalid or rate limit

Solution:

  1. Verify RESEND_API_KEY in .env.local
  2. Check Resend dashboard for errors
  3. Try test domain: onboarding@resend.dev
  4. Check Resend logs for delivery status

Issue: Subdomain routing not working

Cause: Browser doesn't resolve .localhost subdomains

Solution:

  1. Use Chrome or Edge (best support)
  2. Or edit hosts file (see Subdomain Testing section)
  3. Or test direct routes: /finance, /admin

Summary

Testing checklist:

  • ✅ Local development environment set up
  • ✅ Subdomain routing tested on all subdomains
  • ✅ Plaid sandbox connection flow verified
  • ✅ Transaction syncing works with test data
  • ✅ Email templates tested and delivered correctly
  • ✅ All end-to-end user flows completed successfully
  • ✅ Admin workflows function correctly
  • ✅ Database RLS policies enforced
  • ✅ Performance benchmarks met

OneLibro is ready for production deployment!

For deployment instructions, see the Deployment Guide.