@go-corp/test-framework
v1.0.5
Published
Test framework utilities for Cloudflare Workers and Hono applications
Maintainers
Readme
@go-corp/test-framework
🧪 The Ultimate Hybrid Test Framework for Modern Web Applications
A comprehensive, multi-runtime test framework designed for Cloudflare Workers, Node.js, and Bun applications with Hono, featuring smart data factories, automatic database management, and interactive test running.
🎯 What We've Built
🏗️ Hybrid Test Framework (@go-corp/test-framework)
• Multi-Runtime Support: Works seamlessly with Cloudflare Workers, Node.js, and Bun
• Environment-Agnostic: Adapters for D1, SQLite, in-memory, and Drizzle databases
• NPM Ready: Complete package structure for publishing and reuse across projects
✨ Key Features
- Interactive Test Runner 🎮 - CLI with selectable test categories and runtime detection
- Smart Data Factory 🏭 - Seeded random generation for consistent, realistic test data
- Test Store Management 🗄️ - Automatic cleanup and state isolation
- HTTP Test Client 🌐 - Full-featured API testing with session management
- Vitest Integration ⚡ - Built-in helpers for automatic test isolation
- Database Adapters 💾 - Support for multiple database providers with automatic detection
- Environment Detection 🔍 - Automatic runtime and capability detection
💡 Hybrid Approach Benefits
• Fast Unit Tests: Memory database for rapid development
• Integration Tests: SQLite for realistic database interactions
• E2E Tests: Full Cloudflare Workers environment for production-like testing
• Flexible Runtime Detection: Automatically adapts to your environment
Installation
bun add --dev @go-corp/test-frameworkQuick Start
Basic Setup
// test/setup.ts
import { setupCloudflareWorkerTests } from '@go-corp/test-framework'
// Sets up console mocking and required environment variables
setupCloudflareWorkerTests()Simple Request Testing
import { requestWithCookies, postJSON } from '@go-corp/test-framework'
import app from '../src/app'
test('user authentication flow', async () => {
// All requests automatically persist cookies
const { json: user } = await postJSON(app, '/api/auth/sign-in', {
body: { email: '[email protected]', password: 'password' }
})
// Session cookie is automatically included in subsequent requests
const { json: profile } = await requestJSON(app, '/api/profile', {
method: 'GET'
}, { expected: 200 })
expect(profile.email).toBe('[email protected]')
})Modern API Usage
// Modern setup with enhanced features
import {
setupTestFramework,
TestDataFactory,
createHttpTestClient,
dbTest,
factoryTest
} from '@go-corp/test-framework'
import app from '../src/app'
// Configure test framework
setupTestFramework({
app,
database: 'sqlite',
isolation: 'test',
autoCleanup: true
})
// Database test with automatic cleanup
dbTest('user creation with database', async (db) => {
const factory = new TestDataFactory()
const userData = factory.user()
// Insert user into database
await db.insert(users).values(userData)
// Test API endpoint
const client = createHttpTestClient(app)
const { response, json } = await client.getJSON(`/api/users/${userData.id}`)
expect(response.status).toBe(200)
expect(json.email).toBe(userData.email)
})
// Factory test with seeded data
factoryTest('consistent test data', async (factory) => {
const user1 = factory.user()
const user2 = factory.user()
// Same seed produces consistent data across test runs
expect(user1.id).toBeDefined()
expect(user2.id).toBeDefined()
expect(user1.id).not.toBe(user2.id)
}, 12345) // Custom seedCore Testing Utilities
Request Helpers
requestWithCookies(app, path, init?, jarKey?)
Makes a request with automatic cookie persistence across test requests.
import { requestWithCookies, resetCookies } from '@go-corp/test-framework'
// Start with clean session
resetCookies()
// Login request sets session cookie
const loginRes = await requestWithCookies(app, '/api/auth/login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ username: 'test', password: 'pass' })
})
// Subsequent requests automatically include session cookie
const profileRes = await requestWithCookies(app, '/api/profile')requestJSON(app, path, init, options?)
Request helper that automatically parses JSON and validates status codes.
// Expect 200 status (default)
const { res, json } = await requestJSON(app, '/api/users', {
method: 'GET'
})
// Expect specific status
const { json: error } = await requestJSON(app, '/api/invalid', {
method: 'GET'
}, { expected: 404 })
// Accept multiple status codes
const { json } = await requestJSON(app, '/api/endpoint', {
method: 'POST',
body: JSON.stringify(data)
}, { expected: [200, 201] })postJSON(app, path, options?)
Convenience wrapper for POST requests with JSON body.
// Simple POST
const { json } = await postJSON(app, '/api/users', {
body: { name: 'John', email: '[email protected]' }
})
// With status validation
const { json } = await postJSON(app, '/api/users', {
body: { name: 'John' },
expected: 201
})Session Management
Multiple Test Sessions
Use different jarKey values to isolate test sessions:
import { requestWithCookies, resetCookies } from '@go-corp/test-framework'
test('concurrent user sessions', async () => {
// User A login
await postJSON(app, '/api/auth/login', {
body: { username: 'userA' },
jarKey: 'sessionA'
})
// User B login
await postJSON(app, '/api/auth/login', {
body: { username: 'userB' },
jarKey: 'sessionB'
})
// Each user sees their own profile
const { json: profileA } = await requestJSON(app, '/api/profile', {}, {
jarKey: 'sessionA'
})
const { json: profileB } = await requestJSON(app, '/api/profile', {}, {
jarKey: 'sessionB'
})
expect(profileA.username).toBe('userA')
expect(profileB.username).toBe('userB')
})Test Context
Create isolated test contexts with automatic cleanup:
import { createTestContext } from '@go-corp/test-framework'
test('isolated test context', async () => {
const ctx = createTestContext()
// All requests use isolated cookie jar
await ctx.postJSON(app, '/api/auth/login', {
body: { username: 'test' }
})
const { json } = await ctx.requestJSON(app, '/api/profile', {})
expect(json.username).toBe('test')
// Clean up (optional, happens automatically)
ctx.reset()
})Email Testing
Test email functionality with built-in assertion helpers.
Basic Email Testing
import {
clearOutbox,
assertEmailSent,
extractVerificationLink
} from '@go-corp/test-framework'
test('password reset flow', async () => {
await clearOutbox(app)
// Trigger password reset
await postJSON(app, '/api/auth/reset-password', {
body: { email: '[email protected]' }
})
// Assert email was sent
const email = await assertEmailSent(app, '[email protected]')
expect(email.subject).toContain('Password Reset')
// Extract and use verification link
const resetLink = extractVerificationLink(email)
expect(resetLink).toBeTruthy()
// Test the verification link
const { json } = await requestJSON(app, resetLink!)
expect(json.message).toBe('Reset link verified')
})Advanced Email Testing
import {
getOutbox,
getLastEmail,
waitForEmail,
extractOTPCode
} from '@go-corp/test-framework'
test('email verification with OTP', async () => {
await clearOutbox(app)
// Trigger account creation
await postJSON(app, '/api/auth/register', {
body: { email: '[email protected]' }
})
// Wait for email (useful for async operations)
const email = await waitForEmail(app, '[email protected]', 3000)
// Extract OTP code
const otpCode = extractOTPCode(email)
expect(otpCode).toBeTruthy()
// Verify with OTP
const { json } = await postJSON(app, '/api/auth/verify-otp', {
body: { email: '[email protected]', code: otpCode }
})
expect(json.verified).toBe(true)
})Test Environment Setup
Basic Setup
import { setupCloudflareWorkerTests } from '@go-corp/test-framework'
// Automatic setup with sensible defaults
setupCloudflareWorkerTests()Custom Setup
import { setupTestEnvironment } from '@go-corp/test-framework'
setupTestEnvironment({
mockConsole: true,
consoleMethods: ['log', 'info', 'debug'],
env: {
API_KEY: 'test-key',
DATABASE_URL: 'test-db'
},
cleanup: [
() => {
// Custom cleanup logic
}
]
})Environment Variables
import { ensureTestEnv, withTestEnv } from '@go-corp/test-framework'
// Ensure variables exist with defaults
ensureTestEnv({
RESEND_API_KEY: 'test-key',
NODE_ENV: 'test'
})
// Run test with specific environment
const testWithEnv = withTestEnv({
FEATURE_FLAG: 'enabled'
}, async () => {
// Test logic here
const { json } = await requestJSON(app, '/api/feature')
expect(json.enabled).toBe(true)
})
await testWithEnv()Time Mocking
import { createTimeMock } from '@go-corp/test-framework'
test('time-sensitive operations', async () => {
const mockTime = createTimeMock(new Date('2024-01-01'))
mockTime.start()
// Create token that expires in 1 hour
const { json: token } = await postJSON(app, '/api/auth/token')
// Advance time by 2 hours
mockTime.advance(2 * 60 * 60 * 1000)
// Token should now be expired
const { json } = await requestJSON(app, '/api/auth/verify', {
headers: { authorization: \`Bearer \${token.access_token}\` }
}, { expected: 401 })
expect(json.error).toBe('Token expired')
mockTime.restore()
})Utility Functions
Unique Data Generation
import { uniqueEmail, uniqueUsername } from '@go-corp/test-framework'
test('user creation', async () => {
const email = uniqueEmail() // [email protected]
const username = uniqueUsername() // user1
await postJSON(app, '/api/users', {
body: { email, username }
})
})Wait Utility
import { wait } from '@go-corp/test-framework'
test('async operations', async () => {
// Trigger async operation
await postJSON(app, '/api/process')
// Wait for processing
await wait(1000)
// Check result
const { json } = await requestJSON(app, '/api/status')
expect(json.status).toBe('completed')
})API Reference
Modern API (Recommended)
Database Adapters
createDatabaseAdapter(type, config?)- Create database adapter for testsMemoryDatabaseAdapter,SqliteDatabaseAdapter,D1DatabaseAdapter- Database implementationsDrizzleSqliteAdapter,DrizzleD1Adapter- Drizzle ORM integrations
Test Data Factory
TestDataFactory- Seeded data generation for consistent testsfactory.user(),factory.organization(),factory.post()- Built-in generatorscreateSeededFactory(seed)- Factory with custom seed
Enhanced HTTP Client
HttpTestClient- Full-featured test client with retries and cookiescreateHttpTestClient(app, options)- Create HTTP client instance
Vitest Integration
setupTestFramework(config)- Configure test environmenttestSuite(name, fn, config)- Enhanced test suite with setuptestWithContext(name, fn)- Test with isolated contextdbTest(name, fn)- Database test with cleanuphttpTest(name, fn)- HTTP test with session isolationfactoryTest(name, fn, seed?)- Test with seeded data factory
Legacy API (Compatibility)
Core Functions
requestWithCookies(app, path, init?, jarKey?)- Make request with cookie persistencerequestJSON(app, path, init, options?)- Request with JSON parsing and status validationpostJSON(app, path, options?)- POST request convenience wrapperresetCookies(jarKey?)- Clear cookies for jar key or all jarscreateTestContext(jarKey?)- Create isolated test context
Email Functions
getOutbox(app, jarKey?)- Get all emails from test outboxclearOutbox(app, jarKey?)- Clear test email outboxgetLastEmail(app, recipient, jarKey?)- Get most recent email to recipientwaitForEmail(app, recipient, timeout?, jarKey?)- Wait for email to be sentassertEmailSent(app, recipient, jarKey?)- Assert email was sentextractVerificationLink(email)- Extract verification URL from emailextractOTPCode(email)- Extract OTP code from email
Setup Functions
setupCloudflareWorkerTests()- Default setup for Cloudflare WorkerssetupTestEnvironment(options?)- Custom test environment setupensureTestEnv(vars)- Ensure environment variables existwithTestEnv(env, fn)- Run function with specific environment
Utility Functions
uniqueEmail(prefix?)- Generate unique email addressuniqueUsername(prefix?)- Generate unique usernamewait(ms)- Wait for specified millisecondscreateTimeMock(date?)- Create time mock for testing
TypeScript Support
The package includes comprehensive TypeScript definitions for all APIs:
// Core types
import type {
Runtime,
DatabaseProvider,
TestEnvironmentConfig,
HonoApp
} from '@go-corp/test-framework'
// HTTP testing types
import type {
TestRequestOptions,
TestResponse,
HttpClientOptions
} from '@go-corp/test-framework'
// Email testing types
import type {
TestEmail,
TestSetupOptions
} from '@go-corp/test-framework'
// Factory types
import type {
FactoryConfig,
FactoryFunction,
TestDataGenerators
} from '@go-corp/test-framework'
// Store and lifecycle types
import type {
TestStore,
IsolationLevel,
VitestConfig,
TestSuiteConfig,
TestRunnerConfig
} from '@go-corp/test-framework'
// Database adapter type
import type {
DatabaseAdapter
} from '@go-corp/test-framework'🛠️ Development
# Install dependencies
bun install
# Build for development
bun run dev
# Type check
bun run typecheck
# Lint code
bun run lint
bun run lint:fix
# Test the CLI
bun run test-runner --help🚀 Release Management
This project uses @go-corp/workflow for automated release management:
# Interactive release (quality gates, git, npm)
bun run release
# Dry run (show what would happen)
bun run release --dry-run
# Skip specific deployments
bun run release --skip-npm --skip-cloudflare
# Force specific version bump
bun run release --type minor
# Check workflow status
bun run workflow:statusRelease Pipeline:
- ✅ Quality Gates: Auto-fix linting, type checking, tests
- 🏷️ Version Management: Smart semantic versioning with changelog
- 📦 Build & Publish: Automated npm publishing with confirmation
- 🔗 GitHub Integration: Automated releases and tagging
License
MIT
