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
2 changes: 1 addition & 1 deletion api/contributor.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async function handler(req, res) {

if (req.method === 'POST') {
const { status, body } = await handleSaveContributorProfile(
{ userId, ...req.body },
{ ...req.body, userId },
db
);
return res.status(status).json(body);
Expand Down
2 changes: 1 addition & 1 deletion api/saved-calcs.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ async function handler(req, res) {
// expected by the transport-agnostic handler.
const { client_id: clientId, ...rest } = req.body ?? {};
const { status, body } = await handleSaveCalc(
{ userId: req.user.id, clientId, ...rest },
{ ...rest, clientId, userId: req.user.id },
db
);
return res.status(status).json(body);
Expand Down
56 changes: 56 additions & 0 deletions app/src/lib/__tests__/apiOwnerIdFromToken.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';

// Pass-through auth/CORS so the tests exercise only the endpoint bodies.
vi.mock('../../../../api/_lib/auth.js', () => ({
requireAuth: (handler) => (req, res) => {
req.user = { id: 7 };
return handler(req, res);
},
}));
vi.mock('../../../../api/_lib/cors.js', () => ({ handleCors: (handler) => handler }));

const query = vi.fn();
vi.mock('../../../../api/_lib/db.js', () => ({ query: (...args) => query(...args) }));

const { default: savedCalcs } = await import('../../../../api/saved-calcs.js');
const { default: contributor } = await import('../../../../api/contributor.js');

function makeRes() {
const res = { statusCode: 200, body: undefined };
res.status = (code) => { res.statusCode = code; return res; };
res.json = (body) => { res.body = body; return res; };
res.send = (body) => { res.body = body; return res; };
res.setHeader = () => res;
return res;
}

beforeEach(() => {
query.mockReset();
});

describe('owner id comes from the verified token, never the body', () => {
it('saves a calculation under the signed-in user even if the body names another', async () => {
query.mockResolvedValue({ rows: [{ id: 1 }] });
const req = { method: 'POST', body: { userId: 999, name: 'x', species: 'Pink Salmon' }, query: {} };

await savedCalcs(req, makeRes());

const insert = query.mock.calls.find(([sql]) => /INSERT INTO calculations/i.test(sql));
expect(insert[1][0]).toBe(7); // user_id
expect(insert[1]).not.toContain(999);
});

it('saves a contributor profile under the signed-in user even if the body names another', async () => {
query.mockResolvedValue({ rows: [{ id: 1 }] });
const req = { method: 'POST', body: { userId: 999, display_name: 'Deckhand' }, query: {} };

await contributor(req, makeRes());

for (const [, params] of query.mock.calls) {
expect(params).not.toContain(999);
}
const update = query.mock.calls.find(([sql]) => /UPDATE contributors/i.test(sql));
expect(update).toBeDefined();
expect(update[1][4]).toBe(7); // WHERE user_id = $5
});
});
4 changes: 2 additions & 2 deletions server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ async function handleSaveCalcRequest(req, res) {
const dbAdapter = makeSqliteAdapter(db);
const { client_id: clientId, ...rest } = req.body ?? {};
const { status, body } = await handleSaveCalc(
{ userId: req.user.id, clientId, ...rest },
{ ...rest, clientId, userId: req.user.id },
dbAdapter
);
return res.status(status).json(body);
Expand Down Expand Up @@ -571,7 +571,7 @@ app.post('/api/contributor', authenticate, async (req, res) => {
const { handleSaveContributorProfile } = await handlersModulePromise;
const dbAdapter = makeSqliteAdapter(db);
const { status, body } = await handleSaveContributorProfile(
{ userId: req.user.id, ...req.body },
{ ...req.body, userId: req.user.id },
dbAdapter
);
return res.status(status).json(body);
Expand Down
Loading