Skip to content

Commit 9c8509e

Browse files
authored
Merge pull request #111 from codegasms/feat/ci-cd-pipeline
Feat/ci cd pipeline
2 parents 3cb5c8a + fd406f4 commit 9c8509e

File tree

3 files changed

+275
-0
lines changed

3 files changed

+275
-0
lines changed

.github/workflows/build.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Build Check
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
bun-build:
11+
name: Next.js Build with Bun
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
18+
- name: Setup Bun
19+
uses: oven-sh/setup-bun@v1
20+
with:
21+
bun-version: latest
22+
23+
- name: Install dependencies
24+
run: bun install
25+
26+
- name: Build Next.js project
27+
run: bun run build
28+
29+
- name: Check build output
30+
run: |
31+
if [ ! -d ".next" ]; then
32+
echo "Build failed: .next directory not found"
33+
exit 1
34+
fi
35+
36+
docker-build:
37+
name: Docker Build Check
38+
runs-on: ubuntu-latest
39+
40+
steps:
41+
- name: Checkout code
42+
uses: actions/checkout@v4
43+
44+
- name: Set up Docker Buildx
45+
uses: docker/setup-buildx-action@v3
46+
47+
- name: Build Docker image
48+
uses: docker/build-push-action@v5
49+
with:
50+
context: .
51+
push: false
52+
tags: nextjs-app:test
53+
cache-from: type=gha
54+
cache-to: type=gha,mode=max
55+
56+
- name: Test Docker image
57+
run: |
58+
docker run --rm nextjs-app:test echo "Docker image built successfully"

.github/workflows/test.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Run unit tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
name: build-app
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
- name: Install bun
17+
uses: oven-sh/setup-bun@v2
18+
- name: Install dependencies # (assuming your project has dependencies)
19+
run: bun install # You can use npm/yarn/pnpm instead if you prefer
20+
- name: Run tests
21+
run: bun test

TESTING.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# Testing in Next.js API Routes
2+
3+
This project uses Jest to test Next.js API routes. Tests are configured to run in a node environment to properly test server-side code.
4+
5+
## Setup
6+
7+
The testing setup includes:
8+
9+
- Jest for the test runner
10+
- Next.js App Router API route testing
11+
- Mocking utilities for API route testing
12+
13+
## Running Tests
14+
15+
Run the tests using any of these commands:
16+
17+
```bash
18+
# Run all tests
19+
npx jest
20+
21+
# Run tests in watch mode for development
22+
npx jest --watch
23+
24+
# Run tests with coverage report
25+
npx jest --coverage
26+
27+
# Run a specific test file
28+
npx jest __tests__/api/health.test.ts
29+
```
30+
31+
## Test File Structure
32+
33+
Test files are located in the `__tests__` directory, mirroring the structure of the `/app` directory for API routes.
34+
35+
Example:
36+
- API route: `/app/api/health/route.ts`
37+
- Test file: `/__tests__/api/health.test.ts`
38+
39+
## Mocking Dependencies
40+
41+
The most common pattern for testing Next.js API routes is to mock the dependencies. Here's how to do it in your tests:
42+
43+
```typescript
44+
// Mock modules BEFORE importing the component
45+
jest.mock('@/db/drizzle', () => ({
46+
db: {
47+
query: {
48+
users: {
49+
findFirst: jest.fn()
50+
}
51+
}
52+
}
53+
}));
54+
55+
// Now import the handler from the route file
56+
import { GET } from '@/app/api/your-route/route';
57+
58+
// Import the mocked modules to control them in tests
59+
const { db } = require('@/db/drizzle');
60+
```
61+
62+
## Using Test Utilities
63+
64+
We've created some test utilities to make testing API routes easier:
65+
66+
```typescript
67+
import { createMockRequest, parseJsonResponse } from '../utils/api-test-utils';
68+
69+
// Create a mock request
70+
const req = createMockRequest('https://localhost:3000/api/health', {
71+
method: 'GET',
72+
params: { id: '123' },
73+
});
74+
75+
// Execute the handler
76+
const response = await GET(req);
77+
78+
// Parse the response
79+
const data = await parseJsonResponse(response);
80+
expect(data.status).toBe('healthy');
81+
```
82+
83+
## Example Tests
84+
85+
### Testing a GET API Route
86+
87+
```typescript
88+
/**
89+
* @jest-environment node
90+
*/
91+
// Mock modules before importing the component
92+
jest.mock('@/db/drizzle', () => ({
93+
db: {
94+
execute: jest.fn().mockResolvedValue({})
95+
}
96+
}));
97+
98+
// Import after mocks are defined
99+
import { NextRequest } from 'next/server';
100+
import { GET } from '@/app/api/health/route';
101+
102+
describe('Health API Endpoint', () => {
103+
it('should return a healthy status', async () => {
104+
// Create a mock request
105+
const req = new NextRequest(new Request('https://localhost:3000/api/health'));
106+
107+
// Execute the handler
108+
const response = await GET(req);
109+
110+
// Check the response
111+
expect(response.status).toBe(200);
112+
const data = await response.json();
113+
expect(data.status).toBe('healthy');
114+
});
115+
});
116+
```
117+
118+
### Testing a POST API Route
119+
120+
```typescript
121+
/**
122+
* @jest-environment node
123+
*/
124+
// Mock modules before importing the component
125+
jest.mock('@/db/drizzle', () => ({
126+
db: {
127+
query: {
128+
users: {
129+
findFirst: jest.fn()
130+
}
131+
}
132+
}
133+
}));
134+
135+
jest.mock('@/lib/password', () => ({
136+
verifyPassword: jest.fn()
137+
}));
138+
139+
// Import after mocks are defined
140+
import { NextRequest } from 'next/server';
141+
import { POST } from '@/app/api/auth/login/route';
142+
143+
// Import mocked modules
144+
const { db } = require('@/db/drizzle');
145+
const { verifyPassword } = require('@/lib/password');
146+
147+
describe('Login API Endpoint', () => {
148+
it('should successfully login a user', async () => {
149+
// Mock a user being found
150+
db.query.users.findFirst.mockResolvedValue({
151+
id: 'user-123',
152+
153+
});
154+
155+
// Mock password verification
156+
verifyPassword.mockResolvedValue(true);
157+
158+
// Create login request
159+
const req = new NextRequest('https://localhost:3000/api/auth/login', {
160+
method: 'POST',
161+
headers: { 'Content-Type': 'application/json' },
162+
body: JSON.stringify({
163+
164+
password: 'password123'
165+
})
166+
});
167+
168+
// Execute the handler
169+
const response = await POST(req);
170+
171+
// Check the response
172+
expect(response.status).toBe(200);
173+
});
174+
});
175+
```
176+
177+
## Best Practices
178+
179+
1. **Use Node Environment**: Always use the node environment for API route tests:
180+
```typescript
181+
/**
182+
* @jest-environment node
183+
*/
184+
```
185+
186+
2. **Mock Dependencies**: Mock all external dependencies like databases, third-party APIs, etc.
187+
188+
3. **Test Error Cases**: Test not just the happy path, but also error cases like invalid requests, not found resources, etc.
189+
190+
4. **Clear Mocks Between Tests**: Use `beforeEach(() => { jest.clearAllMocks(); })` to reset mocks between tests.
191+
192+
5. **Organize Tests**: Follow the structure of your application when organizing test files.
193+
194+
## Environment Variables in Tests
195+
196+
Environment variables needed for tests are defined in the Jest setup file. If you need to add more environment variables for testing, add them to `jest.setup.js`.

0 commit comments

Comments
 (0)