Files
FlockPal/backend/src/repositories/workspaceRepository.test.ts
T

210 lines
5.9 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
createWorkspace,
deleteWorkspaceIfEmpty,
ensurePersonalWorkspaceForUser,
findAlternateWorkspaceForUser,
getPlatformAdminSummary,
listOwnedWorkspacesByOwnerEmail,
updateWorkspace,
} from './workspaceRepository.js';
import { mockDb } from '../test/mockDb.js';
import type { UserRow } from '../types.js';
const user: UserRow = {
id: 'user-1',
email: 'owner@example.com',
password_hash: null,
name: 'Owner',
created_at: '2026-04-14T00:00:00.000Z',
};
test('ensurePersonalWorkspaceForUser returns an existing workspace without creating one', async () => {
const { calls } = mockDb({
rowCount: 1,
rows: [{ workspace_id: 42 }],
});
const workspaceId = await ensurePersonalWorkspaceForUser(user);
assert.equal(workspaceId, 42);
assert.equal(calls.length, 1);
assert.match(calls[0].text, /FROM workspace_members/);
});
test('createWorkspace inserts owner membership and returns the created workspace', async () => {
const { calls } = mockDb(
{ rowCount: 1, rows: [] },
{ rowCount: 1, rows: [] },
{
rowCount: 1,
rows: [
{
id: 9,
name: 'My Rescue',
workspace_type: 'rescue',
billing_email: 'billing@example.com',
billing_plan: 'rescue_free',
billing_interval: 'monthly',
created_at: '2026-04-14T00:00:00.000Z',
updated_at: '2026-04-14T00:00:00.000Z',
},
],
},
);
const workspace = await createWorkspace({
id: 9,
name: 'My Rescue',
workspaceType: 'rescue',
billingEmail: 'billing@example.com',
billingPlan: 'rescue_free',
billingInterval: 'monthly',
owner: user,
});
assert.equal(workspace?.id, 9);
assert.equal(calls.length, 3);
assert.match(calls[0].text, /INSERT INTO workspaces/);
assert.match(calls[1].text, /INSERT INTO workspace_members/);
assert.match(calls[2].text, /SELECT id, name, workspace_type/);
});
test('updateWorkspace converts an existing household flock to rescue without inserting a new flock', async () => {
const { calls } = mockDb({
rowCount: 1,
rows: [
{
id: 42,
name: 'Converted Rescue',
workspace_type: 'rescue',
billing_email: 'billing@example.com',
billing_plan: 'rescue_free',
billing_interval: 'monthly',
subscription_status: 'active',
rescue_verification_status: 'pending',
created_at: '2026-04-14T00:00:00.000Z',
updated_at: '2026-04-15T00:00:00.000Z',
},
],
});
const workspace = await updateWorkspace({
workspaceId: 42,
name: 'Converted Rescue',
workspaceType: 'rescue',
billingEmail: 'billing@example.com',
billingPlan: 'rescue_free',
billingInterval: 'monthly',
});
assert.equal(workspace?.id, 42);
assert.equal(workspace?.workspace_type, 'rescue');
assert.equal(calls.length, 1);
assert.match(calls[0].text, /UPDATE workspaces/);
assert.doesNotMatch(calls[0].text, /INSERT INTO workspaces/);
assert.deepEqual(calls[0].params, [42, 'Converted Rescue', 'rescue', 'billing@example.com', 'rescue_free', 'monthly']);
});
test('deleteWorkspaceIfEmpty blocks deletion when birds are still assigned', async () => {
const { calls } = mockDb(
{
rowCount: 1,
rows: [{ count: '2' }],
},
);
const result = await deleteWorkspaceIfEmpty(42);
assert.deepEqual(result, { deleted: false, reason: 'birds_present' });
assert.equal(calls.length, 1);
assert.match(calls[0].text, /FROM birds/);
});
test('deleteWorkspaceIfEmpty deletes an empty workspace', async () => {
const { calls } = mockDb(
{
rowCount: 1,
rows: [{ count: '0' }],
},
{
rowCount: 1,
rows: [{ id: 42 }],
},
);
const result = await deleteWorkspaceIfEmpty(42);
assert.deepEqual(result, { deleted: true });
assert.equal(calls.length, 2);
assert.match(calls[1].text, /DELETE FROM workspaces/);
});
test('findAlternateWorkspaceForUser returns another workspace when available', async () => {
const { calls } = mockDb({
rowCount: 1,
rows: [{ workspace_id: 84 }],
});
const workspaceId = await findAlternateWorkspaceForUser('user-1', 42);
assert.equal(workspaceId, 84);
assert.deepEqual(calls[0].params, ['user-1', 42]);
});
test('listOwnedWorkspacesByOwnerEmail resolves accepted owner flocks by email', async () => {
const { calls } = mockDb({
rowCount: 1,
rows: [
{
id: 84,
name: 'Receiving Flock',
workspace_type: 'standard',
billing_email: 'receiver@example.com',
billing_plan: 'household_basic',
subscription_status: 'active',
rescue_verification_status: 'not_required',
created_at: '2026-04-14T00:00:00.000Z',
updated_at: '2026-04-14T00:00:00.000Z',
},
],
});
const workspaces = await listOwnedWorkspacesByOwnerEmail('Receiver@Example.com', 42);
assert.equal(workspaces[0]?.id, 84);
assert.deepEqual(calls[0].params, ['Receiver@Example.com', 42]);
assert.match(calls[0].text, /workspace_members\.role = 'owner'/);
assert.match(calls[0].text, /accepted_at IS NOT NULL/);
assert.match(calls[0].text, /workspaces\.id <> \$2/);
});
test('getPlatformAdminSummary counts memorialized birds separately', async () => {
const { calls } = mockDb({
rowCount: 1,
rows: [
{
total_birds: 8,
memorialized_birds: 3,
total_users: 4,
total_workspaces: 2,
rescue_workspaces: 1,
rescue_birds: 5,
pending_rescues: 1,
daily_users: 2,
},
],
});
const summary = await getPlatformAdminSummary();
assert.equal(summary?.memorialized_birds, 3);
assert.equal(calls.length, 1);
assert.match(calls[0].text, /memorialized_birds/);
assert.match(calls[0].text, /memorialized_at IS NOT NULL/);
assert.match(calls[0].text, /rescue_birds/);
assert.match(calls[0].text, /workspaces\.workspace_type = 'rescue'/);
});