feat: add CI/CD pipeline with OIDC, ECR, Docker Hub, lint and tests
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
const js = require("@eslint/js");
|
||||
|
||||
module.exports = [
|
||||
js.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 2021,
|
||||
globals: {
|
||||
require: "readonly",
|
||||
module: "readonly",
|
||||
exports: "readonly",
|
||||
process: "readonly",
|
||||
__dirname: "readonly",
|
||||
console: "readonly",
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"no-console": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.test.js"],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
describe: "readonly",
|
||||
it: "readonly",
|
||||
expect: "readonly",
|
||||
beforeEach: "readonly",
|
||||
afterEach: "readonly",
|
||||
beforeAll: "readonly",
|
||||
afterAll: "readonly",
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
Generated
+5465
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,18 @@
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "node --watch server.js"
|
||||
"dev": "node --watch server.js",
|
||||
"test": "jest --forceExit",
|
||||
"lint": "eslint . --ext .js --max-warnings=0"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^17.4.0",
|
||||
"express": "^4.21.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"eslint": "^10.2.1",
|
||||
"jest": "^30.3.0",
|
||||
"supertest": "^7.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-11
@@ -11,19 +11,23 @@ let tasks = [];
|
||||
let nextId = 1;
|
||||
|
||||
// Routes
|
||||
app.get("/", (req, res) => {
|
||||
app.get("/api", (req, res) => {
|
||||
res.json({ message: "Task API is running", version: "1.0.0" });
|
||||
});
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.status(200).json({ status: "healthy", uptime: process.uptime() });
|
||||
app.get("/api/health", (req, res) => {
|
||||
res.status(200).json({
|
||||
status: "ok",
|
||||
uptime: process.uptime(),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/tasks", (req, res) => {
|
||||
app.get("/api/tasks", (req, res) => {
|
||||
res.json(tasks);
|
||||
});
|
||||
|
||||
app.post("/tasks", (req, res) => {
|
||||
app.post("/api/tasks", (req, res) => {
|
||||
const { title } = req.body;
|
||||
if (!title) {
|
||||
return res.status(400).json({ error: "Title is required" });
|
||||
@@ -33,14 +37,14 @@ app.post("/tasks", (req, res) => {
|
||||
res.status(201).json(task);
|
||||
});
|
||||
|
||||
app.patch("/tasks/:id", (req, res) => {
|
||||
app.patch("/api/tasks/:id", (req, res) => {
|
||||
const task = tasks.find((t) => t.id === parseInt(req.params.id));
|
||||
if (!task) return res.status(404).json({ error: "Task not found" });
|
||||
task.completed = !task.completed;
|
||||
res.json(task);
|
||||
});
|
||||
|
||||
app.delete("/tasks/:id", (req, res) => {
|
||||
app.delete("/api/tasks/:id", (req, res) => {
|
||||
const index = tasks.findIndex((t) => t.id === parseInt(req.params.id));
|
||||
if (index === -1) return res.status(404).json({ error: "Task not found" });
|
||||
tasks.splice(index, 1);
|
||||
@@ -53,11 +57,15 @@ app.use((req, res) => {
|
||||
});
|
||||
|
||||
// Global error handler
|
||||
app.use((err, req, res, next) => {
|
||||
app.use((err, req, res, _next) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
if (require.main === module) {
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
@@ -0,0 +1,34 @@
|
||||
const request = require('supertest');
|
||||
const app = require('./server');
|
||||
|
||||
describe('Health Check', () => {
|
||||
it('GET /api/health returns 200 with status ok', async () => {
|
||||
const res = await request(app).get('/api/health');
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body.status).toBe('ok');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tasks API', () => {
|
||||
it('GET /api/tasks returns empty array initially', async () => {
|
||||
const res = await request(app).get('/api/tasks');
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
});
|
||||
|
||||
it('POST /api/tasks creates a new task', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/tasks')
|
||||
.send({ title: 'Test task' });
|
||||
expect(res.statusCode).toBe(201);
|
||||
expect(res.body.title).toBe('Test task');
|
||||
expect(res.body.completed).toBe(false);
|
||||
});
|
||||
|
||||
it('POST /api/tasks returns 400 when title is missing', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/tasks')
|
||||
.send({});
|
||||
expect(res.statusCode).toBe(400);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user