Skip to content

Commit 0bf6835

Browse files
committed
fix(api): require auth for job creation
1 parent 62ba8e6 commit 0bf6835

3 files changed

Lines changed: 74 additions & 2 deletions

File tree

apps/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"scripts": {
66
"dev": "node src/server.js",
77
"start": "node src/server.js",
8-
"test": "node --test src/tests"
8+
"test": "node --test \"src/tests/*.test.js\""
99
},
1010
"dependencies": {
1111
"cors": "^2.8.5",

apps/api/src/routes/jobRoutes.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Router } from "express";
22
import { getJobs, postJob } from "../controllers/jobController.js";
3+
import { authMiddleware } from "../middleware/auth.js";
34

45
export const jobRoutes = Router();
56

67
jobRoutes.get("/", getJobs);
7-
jobRoutes.post("/", postJob);
8+
jobRoutes.post("/", authMiddleware, postJob);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
import { createApp } from "../app.js";
4+
import { signAccessToken } from "../utils/jwt.js";
5+
6+
const validJobPayload = {
7+
title: "Senior Node.js Developer",
8+
description: "Build and maintain Express APIs for a production app.",
9+
budgetMin: 50,
10+
budgetMax: 100,
11+
categoryId: "web-development",
12+
skills: ["node.js", "express"]
13+
};
14+
15+
async function withServer(callback) {
16+
const app = createApp();
17+
const server = app.listen(0);
18+
19+
await new Promise((resolve, reject) => {
20+
server.once("listening", resolve);
21+
server.once("error", reject);
22+
});
23+
24+
try {
25+
const { port } = server.address();
26+
await callback(`http://127.0.0.1:${port}`);
27+
} finally {
28+
await new Promise((resolve, reject) => {
29+
server.close((error) => (error ? reject(error) : resolve()));
30+
});
31+
}
32+
}
33+
34+
test("POST /api/jobs rejects unauthenticated requests", async () => {
35+
await withServer(async (baseUrl) => {
36+
const response = await fetch(`${baseUrl}/api/jobs`, {
37+
method: "POST",
38+
headers: { "Content-Type": "application/json" },
39+
body: JSON.stringify(validJobPayload)
40+
});
41+
const payload = await response.json();
42+
43+
assert.equal(response.status, 401);
44+
assert.equal(payload.success, false);
45+
assert.equal(payload.message, "Unauthorized");
46+
});
47+
});
48+
49+
test("POST /api/jobs accepts authenticated requests", async () => {
50+
await withServer(async (baseUrl) => {
51+
const token = signAccessToken({
52+
id: "user_123",
53+
email: "client@example.com",
54+
role: "client"
55+
});
56+
57+
const response = await fetch(`${baseUrl}/api/jobs`, {
58+
method: "POST",
59+
headers: {
60+
"Content-Type": "application/json",
61+
Authorization: `Bearer ${token}`
62+
},
63+
body: JSON.stringify(validJobPayload)
64+
});
65+
const payload = await response.json();
66+
67+
assert.equal(response.status, 201);
68+
assert.equal(payload.success, true);
69+
assert.equal(payload.data.title, validJobPayload.title);
70+
});
71+
});

0 commit comments

Comments
 (0)