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'); }); }); });