194 lines
5.2 KiB
TypeScript
194 lines
5.2 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import {
|
|
completePendingBirdTransfersForOwner,
|
|
createBird,
|
|
createPendingBirdTransfer,
|
|
getBirdById,
|
|
listWeightsForBird,
|
|
transferBirdToWorkspace,
|
|
} from './birdRepository.js';
|
|
import { mockDb } from '../test/mockDb.js';
|
|
|
|
test('getBirdById returns null when the bird does not exist in the workspace', async () => {
|
|
const { calls } = mockDb({ rowCount: 0, rows: [] });
|
|
|
|
const bird = await getBirdById('bird-1', 10);
|
|
|
|
assert.equal(bird, null);
|
|
assert.equal(calls.length, 1);
|
|
assert.deepEqual(calls[0].params, ['bird-1', 10]);
|
|
});
|
|
|
|
test('createBird returns the inserted bird row', async () => {
|
|
mockDb({
|
|
rowCount: 1,
|
|
rows: [
|
|
{
|
|
id: 'bird-1',
|
|
workspace_id: 10,
|
|
name: 'Kiwi',
|
|
tag_id: 'A-1',
|
|
species: 'Cockatiel',
|
|
gender: 'female',
|
|
date_of_birth: null,
|
|
gotcha_day: null,
|
|
chart_color: '#cb3a35',
|
|
photo_data_url: null,
|
|
notify_on_dob: false,
|
|
notify_on_gotcha_day: false,
|
|
created_at: '2026-04-14T00:00:00.000Z',
|
|
latest_weight_grams: null,
|
|
latest_recorded_on: null,
|
|
},
|
|
],
|
|
});
|
|
|
|
const bird = await createBird({
|
|
workspaceId: 10,
|
|
name: 'Kiwi',
|
|
tagId: 'A-1',
|
|
species: 'Cockatiel',
|
|
gender: 'female',
|
|
dateOfBirth: null,
|
|
gotchaDay: null,
|
|
chartColor: '#cb3a35',
|
|
photoDataUrl: null,
|
|
notifyOnDob: false,
|
|
notifyOnGotchaDay: false,
|
|
});
|
|
|
|
assert.equal(bird?.name, 'Kiwi');
|
|
assert.equal(bird?.workspace_id, 10);
|
|
assert.equal(bird?.gender, 'female');
|
|
});
|
|
|
|
test('listWeightsForBird scopes by bird, workspace, and day window', async () => {
|
|
const { calls } = mockDb({
|
|
rowCount: 0,
|
|
rows: [],
|
|
});
|
|
|
|
const weights = await listWeightsForBird('bird-1', 10, 30);
|
|
|
|
assert.deepEqual(weights, []);
|
|
assert.equal(calls.length, 1);
|
|
assert.deepEqual(calls[0].params, ['bird-1', 30, 10]);
|
|
assert.match(calls[0].text, /FROM weight_records/);
|
|
});
|
|
|
|
test('transferBirdToWorkspace moves the bird to the target workspace', async () => {
|
|
const { calls } = mockDb({
|
|
rowCount: 1,
|
|
rows: [
|
|
{
|
|
id: 'bird-1',
|
|
workspace_id: 22,
|
|
name: 'Kiwi',
|
|
tag_id: 'A-1',
|
|
species: 'Cockatiel',
|
|
gender: 'female',
|
|
date_of_birth: null,
|
|
gotcha_day: null,
|
|
chart_color: '#cb3a35',
|
|
photo_data_url: null,
|
|
notify_on_dob: false,
|
|
notify_on_gotcha_day: false,
|
|
created_at: '2026-04-14T00:00:00.000Z',
|
|
latest_weight_grams: '92',
|
|
latest_recorded_on: '2026-04-14',
|
|
},
|
|
],
|
|
});
|
|
|
|
const bird = await transferBirdToWorkspace('bird-1', 10, 22);
|
|
|
|
assert.equal(bird?.workspace_id, 22);
|
|
assert.deepEqual(calls[0].params, ['bird-1', 10, 22]);
|
|
assert.match(calls[0].text, /UPDATE birds/);
|
|
});
|
|
|
|
test('createPendingBirdTransfer stores an open transfer for auto-completion', async () => {
|
|
const { calls } = mockDb({
|
|
rowCount: 1,
|
|
rows: [
|
|
{
|
|
id: 'transfer-1',
|
|
bird_id: 'bird-1',
|
|
source_workspace_id: 10,
|
|
destination_owner_email: 'receiver@example.com',
|
|
requested_by_user_id: 'user-1',
|
|
completed_at: null,
|
|
completed_workspace_id: null,
|
|
last_error: null,
|
|
created_at: '2026-04-15T00:00:00.000Z',
|
|
},
|
|
],
|
|
});
|
|
|
|
const transfer = await createPendingBirdTransfer({
|
|
birdId: 'bird-1',
|
|
sourceWorkspaceId: 10,
|
|
destinationOwnerEmail: 'receiver@example.com',
|
|
requestedByUserId: 'user-1',
|
|
});
|
|
|
|
assert.equal(transfer?.id, 'transfer-1');
|
|
assert.deepEqual(calls[0].params, ['bird-1', 10, 'receiver@example.com', 'user-1']);
|
|
assert.match(calls[0].text, /INSERT INTO pending_bird_transfers/);
|
|
assert.match(calls[0].text, /ON CONFLICT/);
|
|
});
|
|
|
|
test('completePendingBirdTransfersForOwner moves pending birds and marks completion', async () => {
|
|
const { calls } = mockDb(
|
|
{
|
|
rowCount: 1,
|
|
rows: [
|
|
{
|
|
id: 'transfer-1',
|
|
bird_id: 'bird-1',
|
|
source_workspace_id: 10,
|
|
destination_owner_email: 'receiver@example.com',
|
|
requested_by_user_id: 'user-1',
|
|
completed_at: null,
|
|
completed_workspace_id: null,
|
|
last_error: null,
|
|
created_at: '2026-04-15T00:00:00.000Z',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
rowCount: 1,
|
|
rows: [
|
|
{
|
|
id: 'bird-1',
|
|
workspace_id: 22,
|
|
name: 'Kiwi',
|
|
tag_id: 'A-1',
|
|
species: 'Cockatiel',
|
|
gender: 'female',
|
|
date_of_birth: null,
|
|
gotcha_day: null,
|
|
chart_color: '#cb3a35',
|
|
photo_data_url: null,
|
|
notify_on_dob: false,
|
|
notify_on_gotcha_day: false,
|
|
created_at: '2026-04-14T00:00:00.000Z',
|
|
latest_weight_grams: '92',
|
|
latest_recorded_on: '2026-04-14',
|
|
},
|
|
],
|
|
},
|
|
{ rowCount: 1, rows: [] },
|
|
);
|
|
|
|
const result = await completePendingBirdTransfersForOwner('receiver@example.com', 22);
|
|
|
|
assert.deepEqual(result, { completed: 1, failed: 0 });
|
|
assert.deepEqual(calls[0].params, ['receiver@example.com']);
|
|
assert.deepEqual(calls[1].params, ['bird-1', 10, 22]);
|
|
assert.deepEqual(calls[2].params, ['transfer-1', 22]);
|
|
assert.match(calls[2].text, /completed_at = CURRENT_TIMESTAMP/);
|
|
});
|