From b9eaedc4680d79d6439ca7c32281393cdb2e3509 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Mon, 20 Apr 2026 11:05:03 +0100 Subject: [PATCH] fix(today-settings): add missing CSRF token to today settings PUT request (#1044) TodaySettingsDropdown was the only settings component that omitted the x-csrf-token header, causing PUT /api/profile/today-settings to return 500 for all session-authenticated users. Adds integration tests for the today-settings endpoint. --- .../profile-today-settings.test.js | 58 +++++++++++++++++++ .../components/Task/TodaySettingsDropdown.tsx | 2 + 2 files changed, 60 insertions(+) create mode 100644 backend/tests/integration/profile-today-settings.test.js diff --git a/backend/tests/integration/profile-today-settings.test.js b/backend/tests/integration/profile-today-settings.test.js new file mode 100644 index 0000000..1a2509e --- /dev/null +++ b/backend/tests/integration/profile-today-settings.test.js @@ -0,0 +1,58 @@ +'use strict'; + +const request = require('supertest'); +const app = require('../../app'); +const { createTestUser } = require('../helpers/testUtils'); + +describe('PUT /api/profile/today-settings', () => { + let agent; + + beforeEach(async () => { + await createTestUser({ email: 'test@example.com' }); + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); + }); + + it('should save showSuggestions setting', async () => { + const response = await agent + .put('/api/v1/profile/today-settings') + .send({ showSuggestions: true }); + + expect(response.status).toBe(200); + expect(response.body.today_settings.showSuggestions).toBe(true); + }); + + it('should persist settings across requests', async () => { + await agent + .put('/api/v1/profile/today-settings') + .send({ showSuggestions: true }); + + const profile = await agent.get('/api/v1/profile'); + expect(profile.status).toBe(200); + expect(profile.body.today_settings.showSuggestions).toBe(true); + }); + + it('should preserve existing settings when updating a single field', async () => { + await agent + .put('/api/v1/profile/today-settings') + .send({ showMetrics: true }); + await agent + .put('/api/v1/profile/today-settings') + .send({ showSuggestions: true }); + + const profile = await agent.get('/api/v1/profile'); + expect(profile.body.today_settings.showMetrics).toBe(true); + expect(profile.body.today_settings.showSuggestions).toBe(true); + }); + + it('should require authentication', async () => { + const response = await request(app) + .put('/api/v1/profile/today-settings') + .send({ showSuggestions: true }); + + expect(response.status).toBe(401); + }); +}); diff --git a/frontend/components/Task/TodaySettingsDropdown.tsx b/frontend/components/Task/TodaySettingsDropdown.tsx index ef98c39..8ef3f59 100644 --- a/frontend/components/Task/TodaySettingsDropdown.tsx +++ b/frontend/components/Task/TodaySettingsDropdown.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect, useRef } from 'react'; +import { getCsrfToken } from '../../utils/csrfService'; import { useTranslation } from 'react-i18next'; import { ChartBarIcon, @@ -86,6 +87,7 @@ const TodaySettingsDropdown: React.FC = ({ credentials: 'include', headers: { 'Content-Type': 'application/json', + 'x-csrf-token': await getCsrfToken(), }, body: JSON.stringify(settingsToSave), });