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
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,14 @@ internal/frontend/frontend_generated.go
internal/migrations/migrations_generated.go
taskcafe
conf/taskcafe.toml

# Test coverage
coverage/
*.lcov

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
124 changes: 124 additions & 0 deletions __tests__/auth.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
const request = require('supertest');

const app = require('../src/app');

const User = require('../src/models/User');

const jwt = require('jsonwebtoken');

// Mock the User model

jest.mock('../src/models/User');

describe('Authentication API', () => {

beforeEach(() => {

jest.clearAllMocks();

});

describe('POST /login', () => {

it('should return 400 if email is invalid', async () => {

const response = await request(app)

.post('/api/auth/login')

.send({ email: 'invalid-email', password: 'password123' });

expect(response.status).toBe(400);

expect(response.body.errors).toContain('Please provide a valid email address');

});

it('should return 401 if credentials are invalid', async () => {

User.findOne.mockResolvedValue(null);

const response = await request(app)

.post('/api/auth/login')

.send({ email: 'test@example.com', password: 'wrongpassword' });

expect(response.status).toBe(401);

expect(response.body.error).toBe('Invalid email or password');

});

it('should return JWT token on successful login', async () => {

const mockUser = {

_id: 'user123',

email: 'test@example.com',

validatePassword: jest.fn().mockResolvedValue(true)

};

User.findOne.mockResolvedValue(mockUser);

const response = await request(app)

.post('/api/auth/login')

.send({ email: 'test@example.com', password: 'password123' });

expect(response.status).toBe(200);

expect(response.body).toHaveProperty('token');

expect(response.body).toHaveProperty('expiresIn', '1h');

});

});

describe('POST /refresh-token', () => {

it('should return 400 if token is not provided', async () => {

const response = await request(app)

.post('/api/auth/refresh-token')

.send({});

expect(response.status).toBe(400);

expect(response.body.error).toBe('Token is required');

});

it('should return new token when valid token is provided', async () => {

const validToken = jwt.sign(

{ userId: 'user123', email: 'test@example.com' },

'your_default_jwt_secret_here',

{ expiresIn: '1h' }

);

const response = await request(app)

.post('/api/auth/refresh-token')

.send({ token: validToken });

expect(response.status).toBe(200);

expect(response.body).toHaveProperty('token');

expect(response.body).toHaveProperty('expiresIn', '1h');
});
});
});
152 changes: 152 additions & 0 deletions __tests__/categories.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
const request = require('supertest');
const express = require('express');

// Mock the Category model
const mockSave = jest.fn();
const mockFindOne = jest.fn();
const mockFind = jest.fn();
const mockFindById = jest.fn();
const mockFindByIdAndUpdate = jest.fn();
const mockFindByIdAndDelete = jest.fn();

const MockCategory = jest.fn().mockImplementation((data) => ({
...data,
save: mockSave
}));

MockCategory.findOne = mockFindOne;
MockCategory.find = mockFind;
MockCategory.findById = mockFindById;
MockCategory.findByIdAndUpdate = mockFindByIdAndUpdate;
MockCategory.findByIdAndDelete = mockFindByIdAndDelete;

// Mock the Category module
jest.mock('../src/models/Category', () => MockCategory);

// Create Express app for testing (bypassing the file system issue)
function createTestApp() {
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Import and use routes inline to avoid file system issues
const {
createCategory,
getCategories,
getCategory,
updateCategory,
deleteCategory
} = require('../src/controllers/categoryController');

app.post('/api/categories', createCategory);
app.get('/api/categories', getCategories);
app.get('/api/categories/:id', getCategory);
app.put('/api/categories/:id', updateCategory);
app.delete('/api/categories/:id', deleteCategory);

return app;
}

describe('Categories API', () => {
let app;

beforeEach(() => {
jest.clearAllMocks();
app = createTestApp();
});

describe('POST /categories', () => {
it('should create a new category successfully', async () => {
const savedCategory = {
_id: 'category123',
name: 'Food',
limit: 500,
description: 'Groceries and dining'
};

mockFindOne.mockResolvedValue(null);
mockSave.mockResolvedValue(savedCategory);

const response = await request(app)
.post('/api/categories')
.send({
name: 'Food',
limit: 500,
description: 'Groceries and dining'
});

expect(response.status).toBe(201);
expect(response.body.message).toBe('Category created successfully');
expect(response.body.category).toBeDefined();
});

it('should return 409 if category already exists', async () => {
const existingCategory = { name: 'Food' };
mockFindOne.mockResolvedValue(existingCategory);

const response = await request(app)
.post('/api/categories')
.send({
name: 'Food',
limit: 500
});

expect(response.status).toBe(409);
expect(response.body.error).toBe('Category with this name already exists');
});
});

describe('GET /categories', () => {
it('should return all categories', async () => {
const mockCategories = [
{ _id: '1', name: 'Food', limit: 500 },
{ _id: '2', name: 'Transport', limit: 300 }
];

mockFind.mockReturnValue({
sort: jest.fn().mockResolvedValue(mockCategories)
});

const response = await request(app).get('/api/categories');

expect(response.status).toBe(200);
expect(response.body.count).toBe(2);
expect(response.body.categories).toHaveLength(2);
});
});

describe('PUT /categories/:id', () => {
it('should update a category successfully', async () => {
const updatedCategory = {
_id: 'category123',
name: 'Updated Food',
limit: 600
};

mockFindByIdAndUpdate.mockResolvedValue(updatedCategory);

const response = await request(app)
.put('/api/categories/category123')
.send({
name: 'Updated Food',
limit: 600
});

expect(response.status).toBe(200);
expect(response.body.message).toBe('Category updated successfully');
});
});

describe('DELETE /categories/:id', () => {
it('should delete a category successfully', async () => {
const deletedCategory = { _id: 'category123', name: 'Food' };
mockFindByIdAndDelete.mockResolvedValue(deletedCategory);

const response = await request(app)
.delete('/api/categories/category123');

expect(response.status).toBe(200);
expect(response.body.message).toBe('Category deleted successfully');
});
});
});
35 changes: 35 additions & 0 deletions __tests__/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Test setup file
// This file runs before each test suite

// Mock mongoose to prevent database connections during testing
// jest.mock('mongoose', () => ({
// Schema: jest.fn().mockImplementation(() => ({
// pre: jest.fn(),
// statics: {},
// methods: {}
// })),
// model: jest.fn(),
// connect: jest.fn(),
// connection: {
// close: jest.fn()
// }
// }));

// Mock console methods to reduce noise during testing
global.console = {
...console,
// Uncomment to ignore specific console methods during tests
// log: jest.fn(),
// debug: jest.fn(),
// info: jest.fn(),
// warn: jest.fn(),
// error: jest.fn(),
};

// Set test timeout
jest.setTimeout(10000);

// Global test helpers can be added here
global.testHelpers = {
// Add any global test utilities here
};
Loading