feat: add testing infrastructure — Vitest, Playwright, CI, git hooks
Add comprehensive testing covering 420 unit/handler tests across 24 Vitest files, 9 Playwright E2E specs, accessibility scans, and visual regression. Includes GitHub Actions CI, Husky pre-push hook, and TESTING.md docs.
This commit is contained in:
parent
036af95e00
commit
1e30ba23cd
35 changed files with 3637 additions and 5 deletions
168
tests/server/api/dev-endpoints.test.js
Normal file
168
tests/server/api/dev-endpoints.test.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
vi.mock('../../../server/models/member.js', () => ({
|
||||
default: { findOne: vi.fn(), create: vi.fn() }
|
||||
}))
|
||||
|
||||
vi.mock('../../../server/utils/mongoose.js', () => ({
|
||||
connectDB: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('jsonwebtoken', () => ({
|
||||
default: { sign: vi.fn().mockReturnValue('mock-token') }
|
||||
}))
|
||||
|
||||
import Member from '../../../server/models/member.js'
|
||||
import testLoginHandler from '../../../server/api/dev/test-login.get.js'
|
||||
import memberLoginHandler from '../../../server/api/dev/member-login.get.js'
|
||||
import { createMockEvent } from '../helpers/createMockEvent.js'
|
||||
|
||||
const mockMember = {
|
||||
_id: 'member-123',
|
||||
email: 'test-admin@ghostguild.dev',
|
||||
name: 'Test Admin',
|
||||
circle: 'founder',
|
||||
role: 'admin',
|
||||
status: 'active'
|
||||
}
|
||||
|
||||
describe('dev endpoints', () => {
|
||||
let originalNodeEnv
|
||||
|
||||
beforeEach(() => {
|
||||
originalNodeEnv = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'development'
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.env.NODE_ENV = originalNodeEnv
|
||||
})
|
||||
|
||||
// ── Source inspection ──────────────────────────────────────────
|
||||
|
||||
describe('source inspection', () => {
|
||||
const devDir = resolve(import.meta.dirname, '../../../server/api/dev')
|
||||
|
||||
it('test-login.get.js first conditional is NODE_ENV production check', () => {
|
||||
const source = readFileSync(resolve(devDir, 'test-login.get.js'), 'utf-8')
|
||||
const handlerBody = source.slice(source.indexOf('defineEventHandler'))
|
||||
const firstIf = handlerBody.match(/if\s*\([^)]+\)/)?.[0]
|
||||
expect(firstIf).toContain("process.env.NODE_ENV === 'production'")
|
||||
})
|
||||
|
||||
it('member-login.get.js first conditional is NODE_ENV production check', () => {
|
||||
const source = readFileSync(resolve(devDir, 'member-login.get.js'), 'utf-8')
|
||||
const handlerBody = source.slice(source.indexOf('defineEventHandler'))
|
||||
const firstIf = handlerBody.match(/if\s*\([^)]+\)/)?.[0]
|
||||
expect(firstIf).toContain("process.env.NODE_ENV === 'production'")
|
||||
})
|
||||
})
|
||||
|
||||
// ── test-login.get.js ──────────────────────────────────────────
|
||||
|
||||
describe('test-login.get.js', () => {
|
||||
it('returns 404 in production', async () => {
|
||||
process.env.NODE_ENV = 'production'
|
||||
const event = createMockEvent({ method: 'GET', path: '/api/dev/test-login' })
|
||||
|
||||
await expect(testLoginHandler(event)).rejects.toMatchObject({
|
||||
statusCode: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('creates admin user when none exists', async () => {
|
||||
Member.findOne.mockResolvedValue(null)
|
||||
Member.create.mockResolvedValue(mockMember)
|
||||
|
||||
const event = createMockEvent({ method: 'GET', path: '/api/dev/test-login' })
|
||||
await testLoginHandler(event)
|
||||
|
||||
expect(Member.findOne).toHaveBeenCalledWith({ email: 'test-admin@ghostguild.dev' })
|
||||
expect(Member.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
email: 'test-admin@ghostguild.dev',
|
||||
role: 'admin',
|
||||
circle: 'founder'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('uses existing admin when found', async () => {
|
||||
Member.findOne.mockResolvedValue(mockMember)
|
||||
|
||||
const event = createMockEvent({ method: 'GET', path: '/api/dev/test-login' })
|
||||
await testLoginHandler(event)
|
||||
|
||||
expect(Member.findOne).toHaveBeenCalledWith({ email: 'test-admin@ghostguild.dev' })
|
||||
expect(Member.create).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('sets auth cookie', async () => {
|
||||
Member.findOne.mockResolvedValue(mockMember)
|
||||
|
||||
const event = createMockEvent({ method: 'GET', path: '/api/dev/test-login' })
|
||||
await testLoginHandler(event)
|
||||
|
||||
const cookieHeader = event._testSetHeaders['set-cookie']
|
||||
expect(cookieHeader).toBeDefined()
|
||||
expect(cookieHeader).toContain('auth-token=mock-token')
|
||||
})
|
||||
})
|
||||
|
||||
// ── member-login.get.js ────────────────────────────────────────
|
||||
|
||||
describe('member-login.get.js', () => {
|
||||
it('returns 404 in production', async () => {
|
||||
process.env.NODE_ENV = 'production'
|
||||
const event = createMockEvent({ method: 'GET', path: '/api/dev/member-login' })
|
||||
|
||||
await expect(memberLoginHandler(event)).rejects.toMatchObject({
|
||||
statusCode: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('returns 400 when email query param is missing', async () => {
|
||||
const event = createMockEvent({ method: 'GET', path: '/api/dev/member-login' })
|
||||
|
||||
await expect(memberLoginHandler(event)).rejects.toMatchObject({
|
||||
statusCode: 400
|
||||
})
|
||||
})
|
||||
|
||||
it('returns 404 when member not found', async () => {
|
||||
Member.findOne.mockResolvedValue(null)
|
||||
|
||||
const event = createMockEvent({
|
||||
method: 'GET',
|
||||
path: '/api/dev/member-login?email=nobody@example.com'
|
||||
})
|
||||
|
||||
await expect(memberLoginHandler(event)).rejects.toMatchObject({
|
||||
statusCode: 404
|
||||
})
|
||||
})
|
||||
|
||||
it('sets auth cookie for found member', async () => {
|
||||
const foundMember = {
|
||||
_id: 'member-456',
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
status: 'active'
|
||||
}
|
||||
Member.findOne.mockResolvedValue(foundMember)
|
||||
|
||||
const event = createMockEvent({
|
||||
method: 'GET',
|
||||
path: '/api/dev/member-login?email=test@example.com'
|
||||
})
|
||||
await memberLoginHandler(event)
|
||||
|
||||
const cookieHeader = event._testSetHeaders['set-cookie']
|
||||
expect(cookieHeader).toBeDefined()
|
||||
expect(cookieHeader).toContain('auth-token=mock-token')
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue