-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from DevanshMistry890/godwin/nodejs-rest-api
Updates
- Loading branch information
Showing
13 changed files
with
6,552 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
const request = require('supertest'); | ||
const app = require('../index'); // Make sure to export app from index.js | ||
|
||
const sampleTask = { | ||
description: 'Test task', | ||
assignedTo: 'John Doe', | ||
dueDate: '2024-12-31', | ||
priority: 'high', | ||
status: 'in progress', | ||
}; | ||
|
||
describe('Task API Tests', () => { | ||
let server; | ||
let createdTask; | ||
|
||
beforeAll((done) => { | ||
server = app.listen(3001, () => { | ||
console.log('Test server started on port 3001'); | ||
done(); | ||
}); | ||
}); | ||
|
||
afterAll((done) => { | ||
server.close(done); | ||
}); | ||
|
||
// Test POST /api/tasks | ||
test('POST /api/tasks should create a new task', async () => { | ||
const response = await request(app) | ||
.post('/api/tasks') | ||
.send(sampleTask) | ||
.expect('Content-Type', /json/) | ||
.expect(201); | ||
|
||
expect(response.body).toMatchObject(sampleTask); | ||
expect(response.body.id).toBeDefined(); | ||
expect(response.body.createdAt).toBeDefined(); | ||
|
||
createdTask = response.body; // Store for other tests | ||
}); | ||
|
||
// Test GET /api/tasks | ||
test('GET /api/tasks should return all tasks', async () => { | ||
const response = await request(app) | ||
.get('/api/tasks') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
|
||
expect(Array.isArray(response.body)).toBeTruthy(); | ||
expect(response.body.length).toBe(1); | ||
}); | ||
|
||
// Test GET /api/tasks/:id | ||
test('GET /api/tasks/:id should return a specific task', async () => { | ||
const response = await request(app) | ||
.get(`/api/tasks/${createdTask.id}`) | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
|
||
expect(response.body).toMatchObject(sampleTask); | ||
}); | ||
|
||
// Test PUT /api/tasks/:id | ||
test('PUT /api/tasks/:id should update a task', async () => { | ||
const updatedTask = { | ||
...sampleTask, | ||
description: 'Updated test task', | ||
status: 'in progress', | ||
}; | ||
|
||
const response = await request(app) | ||
.put(`/api/tasks/${createdTask.id}`) | ||
.send(updatedTask) | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
|
||
expect(response.body).toMatchObject(updatedTask); | ||
expect(response.body.updatedAt).toBeDefined(); | ||
}); | ||
|
||
// Test DELETE /api/tasks/:id | ||
test('DELETE /api/tasks/:id should delete a task', async () => { | ||
await request(app).delete(`/api/tasks/${createdTask.id}`).expect(204); | ||
|
||
// Verify task is deleted | ||
await request(app).get(`/api/tasks/${createdTask.id}`).expect(404); | ||
}); | ||
|
||
// Test GET /api/tasks/search | ||
test('GET /api/tasks/search should return filtered tasks', async () => { | ||
await request(app).post('/api/tasks').send(sampleTask).expect(201); | ||
|
||
// Test search by description | ||
const response = await request(app) | ||
.get('/api/tasks/search?query=Test') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
|
||
expect(Array.isArray(response.body)).toBeTruthy(); | ||
expect(response.body.length).toBeGreaterThan(0); | ||
expect(response.body[0].description).toContain('Test'); | ||
|
||
// Test empty query returns all tasks | ||
const allTasksResponse = await request(app) | ||
.get('/api/tasks/search') | ||
.expect('Content-Type', /json/) | ||
.expect(200); | ||
|
||
expect(Array.isArray(allTasksResponse.body)).toBeTruthy(); | ||
}); | ||
|
||
// Test error cases for GET /api/tasks/:id | ||
test('GET /api/tasks/:id should return 404 for non-existent task', async () => { | ||
await request(app).get('/api/tasks/999999').expect(404); | ||
}); | ||
|
||
// Test error cases for PUT /api/tasks/:id | ||
test('PUT /api/tasks/:id should return 404 for non-existent task', async () => { | ||
await request(app).put('/api/tasks/999999').send(sampleTask).expect(404); | ||
}); | ||
|
||
// Test error cases for DELETE /api/tasks/:id | ||
test('DELETE /api/tasks/:id should return 404 for non-existent task', async () => { | ||
await request(app).delete('/api/tasks/999999').expect(404); | ||
}); | ||
|
||
// Test validation for POST /api/tasks | ||
test('POST /api/tasks should validate required fields', async () => { | ||
const invalidTask = { | ||
description: 'Missing required fields', | ||
}; | ||
|
||
await request(app).post('/api/tasks').send(invalidTask).expect(400); | ||
}); | ||
}); | ||
|
||
// Test validation for POST /api/tasks | ||
describe('Task Validation Tests', () => { | ||
const invalidPriorityTask = { | ||
...sampleTask, | ||
priority: 'invalid_priority', | ||
}; | ||
|
||
const invalidStatusTask = { | ||
...sampleTask, | ||
status: 'invalid_status', | ||
}; | ||
|
||
const invalidDateTask = { | ||
...sampleTask, | ||
dueDate: 'invalid-date', | ||
}; | ||
|
||
test('should reject task with invalid priority', async () => { | ||
await request(app).post('/api/tasks').send(invalidPriorityTask).expect(400); | ||
}); | ||
|
||
test('should reject task with invalid status', async () => { | ||
await request(app).post('/api/tasks').send(invalidStatusTask).expect(400); | ||
}); | ||
|
||
test('should reject task with invalid date format', async () => { | ||
await request(app).post('/api/tasks').send(invalidDateTask).expect(400); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
const fs = require('fs').promises; | ||
const path = require('path'); | ||
|
||
const TEST_TASKS_FILE = path.join(__dirname, '../../data/test_tasks.json'); | ||
|
||
// Important: Add a dummy test to satisfy Jest | ||
test('dummy test', () => { | ||
expect(true).toBe(true); | ||
}); | ||
|
||
beforeEach(async () => { | ||
// Initialize with empty array instead of resetting | ||
if (!(await fileExists(TEST_TASKS_FILE))) { | ||
await fs.writeFile(TEST_TASKS_FILE, '[]'); | ||
} | ||
}); | ||
|
||
afterAll(async () => { | ||
try { | ||
await fs.unlink(TEST_TASKS_FILE); | ||
} catch (error) { | ||
// Ignore if file doesn't exist | ||
} | ||
}); | ||
|
||
async function fileExists(filePath) { | ||
try { | ||
await fs.access(filePath); | ||
return true; | ||
} catch { | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
const Task = require('../models/Task'); | ||
|
||
class TaskController { | ||
static async getAllTasks(req, res) { | ||
try { | ||
const tasks = await Task.findAll(); | ||
res.json(tasks); | ||
} catch (error) { | ||
console.error('Error getting all tasks:', error); | ||
res.status(500).json({ error: 'Failed to read tasks' }); | ||
} | ||
} | ||
|
||
static async getTaskById(req, res) { | ||
try { | ||
const task = await Task.findById(req.params.id); | ||
if (!task) { | ||
return res.status(404).json({ error: 'Task not found' }); | ||
} | ||
res.json(task); | ||
} catch (error) { | ||
console.error('Error getting task by id:', error); | ||
res.status(500).json({ error: 'Failed to read task' }); | ||
} | ||
} | ||
|
||
static async createTask(req, res) { | ||
try { | ||
const newTask = await Task.create(req.body); | ||
res.status(201).json(newTask); | ||
} catch (error) { | ||
console.error('Error creating task:', error); | ||
res.status(500).json({ error: 'Failed to create task' }); | ||
} | ||
} | ||
|
||
static async updateTask(req, res) { | ||
try { | ||
const updatedTask = await Task.update(req.params.id, req.body); | ||
if (!updatedTask) { | ||
return res.status(404).json({ error: 'Task not found' }); | ||
} | ||
res.json(updatedTask); | ||
} catch (error) { | ||
console.error('Error updating task:', error); | ||
res.status(500).json({ error: 'Failed to update task' }); | ||
} | ||
} | ||
|
||
static async deleteTask(req, res) { | ||
try { | ||
const deleted = await Task.delete(req.params.id); | ||
if (!deleted) { | ||
return res.status(404).json({ error: 'Task not found' }); | ||
} | ||
res.status(204).send(); | ||
} catch (error) { | ||
console.error('Error deleting task:', error); | ||
res.status(500).json({ error: 'Failed to delete task' }); | ||
} | ||
} | ||
|
||
static async searchTasks(req, res) { | ||
try { | ||
const { query } = req.query; | ||
if (!query) { | ||
return res.json(await Task.findAll()); | ||
} | ||
|
||
const tasks = await Task.findAll(); | ||
|
||
const filteredTasks = tasks.filter( | ||
(task) => | ||
task.description.toLowerCase().includes(query.toLowerCase()) || | ||
task.assignedTo.toLowerCase().includes(query.toLowerCase()) | ||
); | ||
|
||
res.json(filteredTasks); | ||
} catch (error) { | ||
console.error('Error searching tasks:', error); | ||
res.status(500).json({ error: 'Failed to search tasks' }); | ||
} | ||
} | ||
} | ||
|
||
module.exports = TaskController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[ | ||
{ | ||
"id": "1732645059698", | ||
"description": "Test the API endpoints", | ||
"assignedTo": "Jane Doe", | ||
"dueDate": "2024-10-31", | ||
"priority": "low", | ||
"status": "completed", | ||
"createdAt": "2024-11-26T18:17:39.698Z" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
const express = require('express'); | ||
const bodyParser = require('body-parser'); | ||
const cors = require('cors'); | ||
const taskRoutes = require('./routes/taskRoutes'); | ||
|
||
const app = express(); | ||
const PORT = process.env.NODE_ENV === 'test' ? 3001 : 3000; | ||
|
||
// Middleware | ||
app.use(cors()); | ||
app.use(bodyParser.json()); | ||
|
||
// Routes | ||
app.use('/api/tasks', taskRoutes); | ||
|
||
// Error handling middleware | ||
app.use((err, req, res, next) => { | ||
console.error(err.stack); | ||
res.status(500).json({ error: 'Something went wrong!' }); | ||
}); | ||
|
||
// Only start the server if not in test environment | ||
if (process.env.NODE_ENV !== 'test') { | ||
app.listen(PORT, () => { | ||
console.log(`Server is running on port ${PORT}`); | ||
}); | ||
} | ||
|
||
// Needed for testing | ||
module.exports = app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const validateTask = (req, res, next) => { | ||
const { description, assignedTo, dueDate, priority, status } = req.body; | ||
|
||
if (!description || !assignedTo || !dueDate || !priority || !status) { | ||
return res.status(400).json({ error: 'All fields are required' }); | ||
} | ||
|
||
const validPriorities = ['low', 'medium', 'high']; | ||
const validStatuses = ['not started', 'in progress', 'completed']; | ||
|
||
if (!validPriorities.includes(priority.toLowerCase())) { | ||
return res.status(400).json({ error: 'Invalid priority value' }); | ||
} | ||
|
||
if (!validStatuses.includes(status.toLowerCase())) { | ||
return res.status(400).json({ error: 'Invalid status value' }); | ||
} | ||
|
||
const dateRegex = /^\d{4}-\d{2}-\d{2}$/; | ||
if (!dateRegex.test(dueDate)) { | ||
return res.status(400).json({ error: 'Invalid date format. Use YYYY-MM-DD' }); | ||
} | ||
|
||
next(); | ||
}; | ||
|
||
module.exports = { validateTask }; |
Oops, something went wrong.