435 lines
16 KiB
JavaScript
435 lines
16 KiB
JavaScript
const request = require('supertest');
|
|
const app = require('../../app');
|
|
const { User } = require('../../models');
|
|
const { createTestUser } = require('../helpers/testUtils');
|
|
|
|
describe('Users Routes', () => {
|
|
let user, agent;
|
|
|
|
beforeEach(async () => {
|
|
user = await createTestUser({
|
|
email: `test_${Date.now()}@example.com`,
|
|
});
|
|
|
|
// Create authenticated agent
|
|
agent = request.agent(app);
|
|
await agent.post('/api/login').send({
|
|
email: user.email,
|
|
password: 'password123',
|
|
});
|
|
});
|
|
|
|
describe('GET /api/profile', () => {
|
|
it('should get user profile', async () => {
|
|
const response = await agent.get('/api/profile');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.uid).toBe(user.uid);
|
|
expect(response.body).not.toHaveProperty('id');
|
|
expect(response.body.email).toBe(user.email);
|
|
expect(response.body).toHaveProperty('appearance');
|
|
expect(response.body).toHaveProperty('language');
|
|
expect(response.body).toHaveProperty('timezone');
|
|
expect(response.body).toHaveProperty('avatar_image');
|
|
expect(response.body).toHaveProperty('telegram_bot_token');
|
|
expect(response.body).toHaveProperty('telegram_chat_id');
|
|
expect(response.body).toHaveProperty('task_summary_enabled');
|
|
expect(response.body).toHaveProperty('task_summary_frequency');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).get('/api/profile');
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should return 401 when session user no longer exists', async () => {
|
|
await User.destroy({ where: { id: user.id } });
|
|
|
|
const response = await agent.get('/api/profile');
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('User not found');
|
|
});
|
|
});
|
|
|
|
describe('PATCH /api/profile', () => {
|
|
it('should update user profile', async () => {
|
|
const updateData = {
|
|
appearance: 'dark',
|
|
language: 'es',
|
|
timezone: 'UTC',
|
|
avatar_image: 'new-avatar.png',
|
|
telegram_bot_token: 'new-token',
|
|
};
|
|
|
|
const response = await agent.patch('/api/profile').send(updateData);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.uid).toBe(user.uid);
|
|
expect(response.body).not.toHaveProperty('id');
|
|
expect(response.body.appearance).toBe(updateData.appearance);
|
|
expect(response.body.language).toBe(updateData.language);
|
|
expect(response.body.timezone).toBe(updateData.timezone);
|
|
expect(response.body.avatar_image).toBe(updateData.avatar_image);
|
|
expect(response.body.telegram_bot_token).toBe(
|
|
updateData.telegram_bot_token
|
|
);
|
|
});
|
|
|
|
it('should allow partial updates', async () => {
|
|
const updateData = {
|
|
appearance: 'dark',
|
|
};
|
|
|
|
const response = await agent.patch('/api/profile').send(updateData);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.appearance).toBe(updateData.appearance);
|
|
expect(response.body.language).toBe(user.language);
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const updateData = {
|
|
appearance: 'dark',
|
|
};
|
|
|
|
const response = await request(app)
|
|
.patch('/api/profile')
|
|
.send(updateData);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should return 401 when session user no longer exists', async () => {
|
|
await User.destroy({ where: { id: user.id } });
|
|
|
|
const response = await agent
|
|
.patch('/api/profile')
|
|
.send({ appearance: 'dark' });
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('User not found');
|
|
});
|
|
});
|
|
|
|
describe('Task Intelligence Settings', () => {
|
|
describe('GET /api/profile - task_intelligence_enabled field', () => {
|
|
it('should return task_intelligence_enabled in profile', async () => {
|
|
const response = await agent.get('/api/profile');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toHaveProperty(
|
|
'task_intelligence_enabled'
|
|
);
|
|
expect(typeof response.body.task_intelligence_enabled).toBe(
|
|
'boolean'
|
|
);
|
|
});
|
|
|
|
it('should default to true for new users', async () => {
|
|
const response = await agent.get('/api/profile');
|
|
|
|
expect(response.status).toBe(200);
|
|
// New users should have task intelligence enabled by default
|
|
expect(response.body.task_intelligence_enabled).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('PATCH /api/profile - disable task intelligence', () => {
|
|
it('should disable task intelligence (AI popups)', async () => {
|
|
const response = await agent
|
|
.patch('/api/profile')
|
|
.send({ task_intelligence_enabled: false });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.task_intelligence_enabled).toBe(false);
|
|
|
|
// Verify it persisted
|
|
const getResponse = await agent.get('/api/profile');
|
|
expect(getResponse.body.task_intelligence_enabled).toBe(false);
|
|
});
|
|
|
|
it('should enable task intelligence (AI popups)', async () => {
|
|
// First disable it
|
|
await user.update({ task_intelligence_enabled: false });
|
|
|
|
// Then enable it via API
|
|
const response = await agent
|
|
.patch('/api/profile')
|
|
.send({ task_intelligence_enabled: true });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.task_intelligence_enabled).toBe(true);
|
|
|
|
// Verify it persisted
|
|
const getResponse = await agent.get('/api/profile');
|
|
expect(getResponse.body.task_intelligence_enabled).toBe(true);
|
|
});
|
|
|
|
it('should allow updating task intelligence with other fields', async () => {
|
|
const response = await agent.patch('/api/profile').send({
|
|
task_intelligence_enabled: false,
|
|
appearance: 'dark',
|
|
language: 'es',
|
|
});
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.task_intelligence_enabled).toBe(false);
|
|
expect(response.body.appearance).toBe('dark');
|
|
expect(response.body.language).toBe('es');
|
|
});
|
|
|
|
it('should handle boolean conversion from string', async () => {
|
|
// Test that "false" string gets converted to boolean false
|
|
const response = await agent
|
|
.patch('/api/profile')
|
|
.send({ task_intelligence_enabled: 'false' });
|
|
|
|
expect(response.status).toBe(200);
|
|
// Should handle string to boolean conversion
|
|
expect([false, 'false']).toContain(
|
|
response.body.task_intelligence_enabled
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Task Intelligence Feature Verification', () => {
|
|
it('should respect disabled setting - no AI suggestions shown', async () => {
|
|
// Disable task intelligence
|
|
await agent
|
|
.patch('/api/profile')
|
|
.send({ task_intelligence_enabled: false });
|
|
|
|
// Verify the setting is stored correctly
|
|
const response = await agent.get('/api/profile');
|
|
expect(response.body.task_intelligence_enabled).toBe(false);
|
|
|
|
// This verifies that the backend correctly stores the setting
|
|
// Frontend tests verify that popups are actually hidden
|
|
});
|
|
|
|
it('should persist across sessions', async () => {
|
|
// Disable task intelligence
|
|
await agent
|
|
.patch('/api/profile')
|
|
.send({ task_intelligence_enabled: false });
|
|
|
|
// Simulate new session by creating new agent
|
|
const newAgent = request.agent(app);
|
|
await newAgent.post('/api/login').send({
|
|
email: user.email,
|
|
password: 'password123',
|
|
});
|
|
|
|
// Verify setting persisted
|
|
const response = await newAgent.get('/api/profile');
|
|
expect(response.body.task_intelligence_enabled).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('POST /api/profile/task-summary/toggle', () => {
|
|
beforeEach(async () => {
|
|
await user.update({ task_summary_enabled: false });
|
|
});
|
|
|
|
it('should toggle task summary on', async () => {
|
|
const response = await agent.post(
|
|
'/api/profile/task-summary/toggle'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.enabled).toBe(true);
|
|
expect(response.body.message).toBe(
|
|
'Task summary notifications have been enabled.'
|
|
);
|
|
});
|
|
|
|
it('should toggle task summary off', async () => {
|
|
await user.update({ task_summary_enabled: true });
|
|
|
|
const response = await agent.post(
|
|
'/api/profile/task-summary/toggle'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.enabled).toBe(false);
|
|
expect(response.body.message).toBe(
|
|
'Task summary notifications have been disabled.'
|
|
);
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).post(
|
|
'/api/profile/task-summary/toggle'
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should return 401 when session user no longer exists', async () => {
|
|
await User.destroy({ where: { id: user.id } });
|
|
|
|
const response = await agent.post(
|
|
'/api/profile/task-summary/toggle'
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('User not found');
|
|
});
|
|
});
|
|
|
|
describe('POST /api/profile/task-summary/frequency', () => {
|
|
it('should update task summary frequency', async () => {
|
|
const response = await agent
|
|
.post('/api/profile/task-summary/frequency')
|
|
.send({ frequency: 'daily' });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.frequency).toBe('daily');
|
|
expect(response.body.message).toBe(
|
|
'Task summary frequency has been set to daily.'
|
|
);
|
|
});
|
|
|
|
it('should require frequency parameter', async () => {
|
|
const response = await agent
|
|
.post('/api/profile/task-summary/frequency')
|
|
.send({});
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Frequency is required.');
|
|
});
|
|
|
|
it('should validate frequency value', async () => {
|
|
const response = await agent
|
|
.post('/api/profile/task-summary/frequency')
|
|
.send({ frequency: 'invalid' });
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Invalid frequency value.');
|
|
});
|
|
|
|
it('should accept valid frequencies', async () => {
|
|
const validFrequencies = [
|
|
'daily',
|
|
'weekdays',
|
|
'weekly',
|
|
'1h',
|
|
'2h',
|
|
'4h',
|
|
'8h',
|
|
'12h',
|
|
];
|
|
|
|
for (const frequency of validFrequencies) {
|
|
const response = await agent
|
|
.post('/api/profile/task-summary/frequency')
|
|
.send({ frequency });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.frequency).toBe(frequency);
|
|
}
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app)
|
|
.post('/api/profile/task-summary/frequency')
|
|
.send({ frequency: 'daily' });
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should return 401 when session user no longer exists', async () => {
|
|
await User.destroy({ where: { id: user.id } });
|
|
|
|
const response = await agent
|
|
.post('/api/profile/task-summary/frequency')
|
|
.send({ frequency: 'daily' });
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('User not found');
|
|
});
|
|
});
|
|
|
|
describe('POST /api/profile/task-summary/send-now', () => {
|
|
it('should require telegram configuration', async () => {
|
|
const response = await agent.post(
|
|
'/api/profile/task-summary/send-now'
|
|
);
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe(
|
|
'Telegram bot is not properly configured.'
|
|
);
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).post(
|
|
'/api/profile/task-summary/send-now'
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should return 401 when session user no longer exists', async () => {
|
|
await User.destroy({ where: { id: user.id } });
|
|
|
|
const response = await agent.post(
|
|
'/api/profile/task-summary/send-now'
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('User not found');
|
|
});
|
|
});
|
|
|
|
describe('GET /api/profile/task-summary/status', () => {
|
|
it('should get task summary status', async () => {
|
|
await user.update({
|
|
task_summary_enabled: true,
|
|
task_summary_frequency: 'daily',
|
|
});
|
|
|
|
const response = await agent.get(
|
|
'/api/profile/task-summary/status'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.enabled).toBe(true);
|
|
expect(response.body.frequency).toBe('daily');
|
|
expect(response.body).toHaveProperty('last_run');
|
|
expect(response.body).toHaveProperty('next_run');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).get(
|
|
'/api/profile/task-summary/status'
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should return 401 when session user no longer exists', async () => {
|
|
await User.destroy({ where: { id: user.id } });
|
|
|
|
const response = await agent.get(
|
|
'/api/profile/task-summary/status'
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('User not found');
|
|
});
|
|
});
|
|
});
|