Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ jobs:
run: npm ci

- name: Build application
env:
DATABASE_URL: postgresql://ci:ci@localhost:5432/asca_ci
NEXT_PUBLIC_SUPABASE_URL: https://example.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-anon-key
SUPABASE_SERVICE_ROLE_KEY: ci-service-role-key
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: pk_test_ZHVtbXkuY2xlcmsuYWNjb3VudHMuZGV2JA
CLERK_SECRET_KEY: sk_test_ci
NEXT_PUBLIC_APP_URL: http://localhost:3000
run: npm run build

- name: Upload build artifacts
Expand Down
20 changes: 16 additions & 4 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,22 @@ jobs:
- name: Setup environment variables
run: |
# Next.js with NODE_ENV=test loads .env.test (NOT .env.local).
# See: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
cp .env.example .env.test
cp .env.example .env.local # safety net for any non-Next consumer
# Add any additional environment variables needed for testing
# Use structurally valid CI placeholders so Clerk validates during dev-server startup.
cat > .env.test <<'EOF'
DATABASE_URL="postgresql://ci:ci@localhost:5432/asca_ci"
NEXT_PUBLIC_SUPABASE_URL="https://example.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="ci-anon-key"
SUPABASE_SERVICE_ROLE_KEY="ci-service-role-key"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_ZHVtbXkuY2xlcmsuYWNjb3VudHMuZGV2JA"
CLERK_SECRET_KEY="sk_test_ci"
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/sign-up"
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/"
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_E2E_DISABLE_CLERK="true"
EOF
cp .env.test .env.local # safety net for any non-Next consumer

- name: Run Playwright tests
run: npm run test:e2e:ci
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ next-env.d.ts
coverage/
.nyc_output/
.eslintcache
playwright-report/
test-results/
.playwright-browsers/
.vercel
ops/venv/

Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ pnpm-lock.yaml
# Test outputs
coverage/
junit.xml
playwright-report/
test-results/

# Marketing/agent context (markdown tables would break under prettier)
.agents/
26 changes: 25 additions & 1 deletion app/api/members/__tests__/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,18 @@ jest.mock('@/lib/security/rate-limit', () => ({
},
}))

jest.mock('@clerk/nextjs/server', () => ({
auth: jest.fn().mockResolvedValue({ userId: 'test-user-id' }),
}))

import { describe, test, expect, beforeEach } from '@jest/globals'
import { NextRequest } from 'next/server'
import { auth } from '@clerk/nextjs/server'
import { withPerformanceLog } from '@/lib/db'
import { GET, POST } from '../route'

const mockWithPerformanceLog = withPerformanceLog as jest.MockedFunction<typeof withPerformanceLog>
const mockAuth = auth as unknown as jest.MockedFunction<() => Promise<{ userId: string | null }>>

describe('GET /api/members', () => {
beforeEach(() => {
Expand Down Expand Up @@ -159,7 +165,7 @@ describe('GET /api/members', () => {
await GET(request)

// Assert
expect(mockWithPerformanceLog).toHaveBeenCalledWith('members.list', expect.any(Function))
expect(mockWithPerformanceLog).toHaveBeenCalledWith('members.listPublic', expect.any(Function))
})
})

Expand All @@ -173,6 +179,24 @@ describe('POST /api/members', () => {

beforeEach(() => {
jest.clearAllMocks()
mockAuth.mockResolvedValue({ userId: 'test-user-id' })
})
Comment on lines 180 to +183

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

테스트에서 인증 우회 플래그 환경변수를 명시적으로 고정하세요.

NEXT_PUBLIC_E2E_DISABLE_CLERK 값이 외부 환경에서 주입되면 이 스위트의 POST 기대값(201/409)이 비결정적으로 깨질 수 있습니다. beforeEach에서 명시적으로 해제/설정해 격리하는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/members/__tests__/route.test.ts` around lines 180 - 183, In the
beforeEach block that sets up mockAuth, explicitly set
process.env.NEXT_PUBLIC_E2E_DISABLE_CLERK to a deterministic value (e.g.,
'false') so tests don't pick up external environment overrides, and add an
afterEach to restore the original env value; update the existing beforeEach (and
add afterEach) to save the current process.env.NEXT_PUBLIC_E2E_DISABLE_CLERK,
set it to the fixed value, then restore it after each test to keep tests
isolated.


test('should return 401 when authentication is missing', async () => {
mockAuth.mockResolvedValueOnce({ userId: null })

const request = new NextRequest('http://localhost:3000/api/members', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(validBody),
})

const response = await POST(request)
const data = await response.json()

expect(response.status).toBe(401)
expect(data.success).toBe(false)
expect(data.error.code).toBe('UNAUTHORIZED')
})

test('should create member and return 201 with valid data', async () => {
Expand Down
Loading
Loading