- Registration form: added 5 new role options to dropdown - Sidebar: new roles get proper nav access via ALL_ROLES/LEADER_ROLES - Dashboard: isLeader check expanded to include new leadership roles - Shared/Pages: role badge colors added for all new roles - Invite modal: expanded role dropdown
121 lines
4.0 KiB
JavaScript
121 lines
4.0 KiB
JavaScript
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import request from 'supertest';
|
|
import { app } from '../index.js';
|
|
import pool from '../db.js';
|
|
|
|
// Mock DB
|
|
const mockQuery = vi.fn();
|
|
const mockRelease = vi.fn();
|
|
const mockCommit = vi.fn();
|
|
const mockRollback = vi.fn();
|
|
const mockBeginTransaction = vi.fn();
|
|
|
|
vi.mock('../db.js', () => ({
|
|
default: {
|
|
query: vi.fn(),
|
|
getConnection: vi.fn()
|
|
},
|
|
initDB: vi.fn()
|
|
}));
|
|
|
|
describe('Task Routes', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockQuery.mockReset(); // Important to clear implementations
|
|
pool.query = mockQuery;
|
|
pool.getConnection.mockResolvedValue({
|
|
query: mockQuery,
|
|
release: mockRelease,
|
|
beginTransaction: mockBeginTransaction,
|
|
commit: mockCommit,
|
|
rollback: mockRollback
|
|
});
|
|
});
|
|
|
|
describe('GET /api/tasks', () => {
|
|
it('returns list of tasks', async () => {
|
|
// Mock fetching task IDs
|
|
mockQuery.mockResolvedValueOnce([[{ id: 't1' }]]);
|
|
|
|
// Mock getFullTask queries for 't1'
|
|
// 1. Task details
|
|
mockQuery.mockResolvedValueOnce([[{ id: 't1', title: 'Task 1', status: 'todo' }]]); // Task
|
|
// 2. Subtasks
|
|
mockQuery.mockResolvedValueOnce([[]]); // Subtasks
|
|
// 3. Comments
|
|
mockQuery.mockResolvedValueOnce([[]]); // Comments
|
|
// 4. Activities
|
|
mockQuery.mockResolvedValueOnce([[]]); // Activities
|
|
// 5. Tags
|
|
mockQuery.mockResolvedValueOnce([[]]); // Tags
|
|
// 6. Dependencies
|
|
mockQuery.mockResolvedValueOnce([[]]); // Dependencies
|
|
|
|
const res = await request(app).get('/api/tasks');
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body).toHaveLength(1);
|
|
expect(res.body[0].title).toBe('Task 1');
|
|
});
|
|
});
|
|
|
|
describe('POST /api/tasks', () => {
|
|
it('creates a new task', async () => {
|
|
// Mock INSERTs (1: Task, 2: Activities) -> Return {}
|
|
mockQuery.mockResolvedValueOnce({}); // Insert task
|
|
mockQuery.mockResolvedValueOnce({}); // Insert activity
|
|
|
|
// getFullTask queries (3-8)
|
|
mockQuery.mockResolvedValueOnce([[{ id: 'new-id', title: 'New Task', status: 'todo' }]]); // Task
|
|
mockQuery.mockResolvedValueOnce([[]]); // Subtasks
|
|
mockQuery.mockResolvedValueOnce([[]]); // Comments
|
|
mockQuery.mockResolvedValueOnce([[]]); // Activities
|
|
mockQuery.mockResolvedValueOnce([[]]); // Tags
|
|
mockQuery.mockResolvedValueOnce([[]]); // Deps
|
|
|
|
const newTask = {
|
|
// For getFullTask called at end
|
|
title: 'New Task',
|
|
description: 'Desc',
|
|
status: 'todo'
|
|
};
|
|
|
|
const res = await request(app)
|
|
.post('/api/tasks')
|
|
.send(newTask);
|
|
|
|
expect(res.status).toBe(201);
|
|
expect(mockBeginTransaction).toHaveBeenCalled();
|
|
expect(mockCommit).toHaveBeenCalled();
|
|
expect(mockRelease).toHaveBeenCalled();
|
|
});
|
|
|
|
it('rolls back on error', async () => {
|
|
mockQuery.mockRejectedValue(new Error('DB Error'));
|
|
|
|
const res = await request(app)
|
|
.post('/api/tasks')
|
|
.send({ title: 'Task' });
|
|
|
|
expect(res.status).toBe(500);
|
|
expect(mockRollback).toHaveBeenCalled();
|
|
expect(mockRelease).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('DELETE /api/tasks/:id', () => {
|
|
it('deletes a task', async () => {
|
|
mockQuery.mockResolvedValue({});
|
|
|
|
const res = await request(app).delete('/api/tasks/t1');
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(mockQuery).toHaveBeenCalledWith(
|
|
expect.stringContaining('DELETE FROM tasks'),
|
|
['t1']
|
|
);
|
|
});
|
|
});
|
|
});
|