style: format server and shared with prettier

A mechanical prettier --write over server/src, server/tests, server/scripts
and shared/src with the workspaces' .prettierrc; nothing but layout and
import order changes, and every file keeps the same set of imports. shared
is formatted throughout and its format baseline is empty. The server
baseline keeps the 35 files that Prettier would grow past their lint:size
limit (they need a split first, which is not a formatting change), and the
generated entity files stay as their generator writes them.
This commit is contained in:
Maurice
2026-10-10 21:23:37 +02:00
parent f4e518b2df
commit 724c88c201
1978 changed files with 78448 additions and 39347 deletions
+309 -121
View File
@@ -1,11 +1,12 @@
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
import { Platform } from '@mikro-orm/core';
import { expressionBuilder, type ExpressionBuilder } from 'kysely';
import { createSnapshotTestDb } from '../../../helpers/db-mock';
import { resetTestDb } from '../../../helpers/test-db';
import { createTestOrm, type TestOrm } from '../../../helpers/test-orm';
import { createUser } from '../../../helpers/factories';
import { Users } from '../../../../src/db/entities/Users.entity';
import {
castIntegerKysely,
concatKysely,
currentTimestampKysely,
nowPlusSecondsKysely,
startsWithIsoDateKysely,
substringKysely,
unixEpochToIsoKysely,
} from '../../../../src/db/dialect/kysely-functions';
import {
absDifference,
caseWhenEquals,
@@ -40,16 +41,16 @@ import {
substring,
trim,
} from '../../../../src/db/dialect/sql-functions';
import {
castIntegerKysely,
concatKysely,
currentTimestampKysely,
nowPlusSecondsKysely,
startsWithIsoDateKysely,
substringKysely,
unixEpochToIsoKysely,
} from '../../../../src/db/dialect/kysely-functions';
import { Users } from '../../../../src/db/entities/Users.entity';
import { createSnapshotTestDb } from '../../../helpers/db-mock';
import { createUser } from '../../../helpers/factories';
import { createJourney, createJourneyEntry } from '../../../helpers/factories';
import { resetTestDb } from '../../../helpers/test-db';
import { createTestOrm, type TestOrm } from '../../../helpers/test-orm';
import { Platform } from '@mikro-orm/core';
import { expressionBuilder, type ExpressionBuilder } from 'kysely';
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
/** The `users` columns the Kysely-expression tests below read/write, narrowed the same way every other Kysely-typed repository method in this program declares its own `TDB`. */
interface UsersKyselyDB {
@@ -64,15 +65,24 @@ interface UsersKyselyDB {
const testDb = createSnapshotTestDb();
let t: TestOrm;
beforeAll(async () => { t = await createTestOrm(testDb); });
beforeEach(() => { resetTestDb(testDb); t.clear(); });
afterAll(async () => { await t.close(); testDb.close(); });
beforeAll(async () => {
t = await createTestOrm(testDb);
});
beforeEach(() => {
resetTestDb(testDb);
t.clear();
});
afterAll(async () => {
await t.close();
testDb.close();
});
describe('sql-functions (sqlite)', () => {
it('SQLF-001: dateOf yields the calendar date of a stored timestamp', async () => {
const { user } = createUser(testDb);
testDb.prepare("UPDATE users SET created_at = '2026-09-21 13:05:09' WHERE id = ?").run(user.id);
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([dateOf(t.em.getPlatform(), 'u.created_at').as('d')])
.where({ id: user.id })
.execute('get', false);
@@ -82,7 +92,8 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-002: dateAdd shifts by whole days', async () => {
const { user } = createUser(testDb);
testDb.prepare("UPDATE users SET created_at = '2026-09-21 13:05:09' WHERE id = ?").run(user.id);
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([dateAdd(t.em.getPlatform(), 'u.created_at', 10).as('d')])
.where({ id: user.id })
.execute('get', false);
@@ -91,7 +102,8 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-003: currentTimestamp is the DB clock in the wire format', async () => {
createUser(testDb);
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([currentTimestamp(t.em.getPlatform()).as('now')])
.limit(1)
.execute('get', false);
@@ -112,7 +124,8 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-006: dateAdd shifts backwards for a negative day count', async () => {
const { user } = createUser(testDb);
testDb.prepare("UPDATE users SET created_at = '2026-09-21 13:05:09' WHERE id = ?").run(user.id);
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([dateAdd(t.em.getPlatform(), 'u.created_at', -10).as('d')])
.where({ id: user.id })
.execute('get', false);
@@ -126,7 +139,9 @@ describe('sql-functions (sqlite)', () => {
expect(() => dateAdd(foreign, 'u.created_at', 1)).toThrow(/no implementation for platform FakePlatform/);
expect(() => currentTimestamp(foreign)).toThrow(/no implementation for platform FakePlatform/);
expect(() => columnRef(foreign, 'u.max_uses')).toThrow(/no implementation for platform FakePlatform/);
expect(() => columnIncrementedBy(foreign, 'u.used_count', 1)).toThrow(/no implementation for platform FakePlatform/);
expect(() => columnIncrementedBy(foreign, 'u.used_count', 1)).toThrow(
/no implementation for platform FakePlatform/,
);
expect(() => lower(foreign, 'u.email')).toThrow(/no implementation for platform FakePlatform/);
expect(() => lowerParam(foreign, 'x')).toThrow(/no implementation for platform FakePlatform/);
});
@@ -164,14 +179,16 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-009: columnRef compares one column against another (not a bound value)', async () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET login_count = 5, password_version = 3 WHERE id = ?').run(user.id);
const below = await t.em.createQueryBuilder(Users, 'u')
const below = await t.em
.createQueryBuilder(Users, 'u')
.select('*')
.where({ id: user.id, password_version: { $lt: columnRef(t.em.getPlatform(), 'login_count') } })
.execute('get', false);
expect((below as { id: number } | undefined)?.id).toBe(user.id);
testDb.prepare('UPDATE users SET login_count = 1 WHERE id = ?').run(user.id);
const notBelow = await t.em.createQueryBuilder(Users, 'u')
const notBelow = await t.em
.createQueryBuilder(Users, 'u')
.select('*')
.where({ id: user.id, password_version: { $lt: columnRef(t.em.getPlatform(), 'login_count') } })
.execute('get', false);
@@ -181,19 +198,27 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-010: columnIncrementedBy writes <col> + n / <col> - n, never a bound-parameter add', async () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET login_count = 10 WHERE id = ?').run(user.id);
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ login_count: columnIncrementedBy(t.em.getPlatform(), 'login_count', 3) })
.where({ id: user.id })
.execute('run');
t.clear();
expect((testDb.prepare('SELECT login_count FROM users WHERE id = ?').get(user.id) as { login_count: number }).login_count).toBe(13);
expect(
(testDb.prepare('SELECT login_count FROM users WHERE id = ?').get(user.id) as { login_count: number })
.login_count,
).toBe(13);
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ login_count: columnIncrementedBy(t.em.getPlatform(), 'login_count', -5) })
.where({ id: user.id })
.execute('run');
t.clear();
expect((testDb.prepare('SELECT login_count FROM users WHERE id = ?').get(user.id) as { login_count: number }).login_count).toBe(8);
expect(
(testDb.prepare('SELECT login_count FROM users WHERE id = ?').get(user.id) as { login_count: number })
.login_count,
).toBe(8);
});
it('SQLF-011: columnIncrementedBy rejects a non-integer amount', () => {
@@ -221,7 +246,8 @@ describe('sql-functions (sqlite)', () => {
// back through the raw connection, stores that exact same shape — not
// just the fragment text in isolation.
const { user } = createUser(testDb);
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ created_at: currentTimestamp(t.em.getPlatform()) })
.where({ id: user.id })
.execute('run');
@@ -300,7 +326,8 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-018: trim strips leading/trailing whitespace', async () => {
const { user } = createUser(testDb, { username: ' Padded Name ' });
const platform = t.em.getPlatform();
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([trim(platform, 'u.username').as('n')])
.where({ id: user.id })
.execute('get', false);
@@ -313,13 +340,15 @@ describe('sql-functions (sqlite)', () => {
const { user: withoutName } = createUser(testDb, { username: 'no-display-name' });
const platform = t.em.getPlatform();
const a = await t.em.createQueryBuilder(Users, 'u')
const a = await t.em
.createQueryBuilder(Users, 'u')
.select([coalesce(platform, 'u.display_name', 'u.username').as('name')])
.where({ id: withName.id })
.execute('get', false);
expect((a as { name: string }).name).toBe('Display Name');
const b = await t.em.createQueryBuilder(Users, 'u')
const b = await t.em
.createQueryBuilder(Users, 'u')
.select([coalesce(platform, 'u.display_name', 'u.username').as('name')])
.where({ id: withoutName.id })
.execute('get', false);
@@ -329,19 +358,27 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-020: coalesceParam(ref, value) writes COALESCE(col, ?) with the fallback bound, not another column', async () => {
const { user } = createUser(testDb);
const platform = t.em.getPlatform();
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ display_name: coalesceParam(platform, 'display_name', 'Fallback Name') })
.where({ id: user.id })
.execute('run');
t.clear();
expect((testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string }).display_name).toBe('Fallback Name');
expect(
(testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string })
.display_name,
).toBe('Fallback Name');
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ display_name: coalesceParam(platform, 'display_name', 'Never Used') })
.where({ id: user.id })
.execute('run');
t.clear();
expect((testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string }).display_name).toBe('Fallback Name');
expect(
(testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string })
.display_name,
).toBe('Fallback Name');
});
// Plan 3h Task 5 (doc-sync, DS23/DS24) — `coalesceOverride`, the
@@ -354,24 +391,32 @@ describe('sql-functions (sqlite)', () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('Old Name', user.id);
const platform = t.em.getPlatform();
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ display_name: coalesceOverride(platform, 'New Name', 'display_name') })
.where({ id: user.id })
.execute('run');
t.clear();
expect((testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string }).display_name).toBe('New Name');
expect(
(testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string })
.display_name,
).toBe('New Name');
});
it('SQLF-091: coalesceOverride(null, ref) leaves the existing column untouched — a null new value never clobbers what is stored', async () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('Keep Me', user.id);
const platform = t.em.getPlatform();
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({ display_name: coalesceOverride(platform, null, 'display_name') })
.where({ id: user.id })
.execute('run');
t.clear();
expect((testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string }).display_name).toBe('Keep Me');
expect(
(testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(user.id) as { display_name: string })
.display_name,
).toBe('Keep Me');
});
it('SQLF-092: an unknown platform fails closed for coalesceOverride', () => {
@@ -386,31 +431,55 @@ describe('sql-functions (sqlite)', () => {
// repository writes, in ONE UPDATE that also overwrites the key column —
// proving the WHEN reads the stored URL, not the one being written.
describe('coalesceOverrideWhileSame (IM4 self-signed switch)', () => {
async function save(userId: number, url: string, value: number | null): Promise<{ immich_url: string; immich_allow_insecure_tls: number }> {
async function save(
userId: number,
url: string,
value: number | null,
): Promise<{ immich_url: string; immich_allow_insecure_tls: number }> {
const platform = t.em.getPlatform();
await t.em.createQueryBuilder(Users, 'u')
await t.em
.createQueryBuilder(Users, 'u')
.update({
immich_url: url,
immich_allow_insecure_tls: coalesceOverrideWhileSame(platform, value, 'immich_allow_insecure_tls', 'immich_url', url),
immich_allow_insecure_tls: coalesceOverrideWhileSame(
platform,
value,
'immich_allow_insecure_tls',
'immich_url',
url,
),
})
.where({ id: userId })
.execute('run');
t.clear();
return testDb.prepare('SELECT immich_url, immich_allow_insecure_tls FROM users WHERE id = ?').get(userId) as { immich_url: string; immich_allow_insecure_tls: number };
return testDb.prepare('SELECT immich_url, immich_allow_insecure_tls FROM users WHERE id = ?').get(userId) as {
immich_url: string;
immich_allow_insecure_tls: number;
};
}
it('SQLF-093: a null value keeps the stored column while the key column still holds the value being written', async () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = 1 WHERE id = ?').run('https://nas.local', user.id);
expect(await save(user.id, 'https://nas.local', null)).toEqual({ immich_url: 'https://nas.local', immich_allow_insecure_tls: 1 });
testDb
.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = 1 WHERE id = ?')
.run('https://nas.local', user.id);
expect(await save(user.id, 'https://nas.local', null)).toEqual({
immich_url: 'https://nas.local',
immich_allow_insecure_tls: 1,
});
// Symmetric: a stored 0 is kept too, not merely "anything truthy survives".
testDb.prepare('UPDATE users SET immich_allow_insecure_tls = 0 WHERE id = ?').run(user.id);
expect(await save(user.id, 'https://nas.local', null)).toEqual({ immich_url: 'https://nas.local', immich_allow_insecure_tls: 0 });
expect(await save(user.id, 'https://nas.local', null)).toEqual({
immich_url: 'https://nas.local',
immich_allow_insecure_tls: 0,
});
});
it('SQLF-094: a non-null value overwrites the stored column in both directions, same key or not', async () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = 1 WHERE id = ?').run('https://nas.local', user.id);
testDb
.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = 1 WHERE id = ?')
.run('https://nas.local', user.id);
expect((await save(user.id, 'https://nas.local', 0)).immich_allow_insecure_tls).toBe(0);
expect((await save(user.id, 'https://nas.local', 1)).immich_allow_insecure_tls).toBe(1);
expect((await save(user.id, 'https://other.example.com', 1)).immich_allow_insecure_tls).toBe(1);
@@ -419,11 +488,19 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-095: a null value falls back to 0, not to the stored column, once the key column changes — including from NULL (a first connection)', async () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = 1 WHERE id = ?').run('https://nas.local', user.id);
expect(await save(user.id, 'https://photos.example.com', null)).toEqual({ immich_url: 'https://photos.example.com', immich_allow_insecure_tls: 0 });
testDb
.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = 1 WHERE id = ?')
.run('https://nas.local', user.id);
expect(await save(user.id, 'https://photos.example.com', null)).toEqual({
immich_url: 'https://photos.example.com',
immich_allow_insecure_tls: 0,
});
// `IS` (not `=`) so a stored NULL url compares as "different", never as unknown.
testDb.prepare('UPDATE users SET immich_url = NULL, immich_allow_insecure_tls = 1 WHERE id = ?').run(user.id);
expect(await save(user.id, 'https://nas.local', null)).toEqual({ immich_url: 'https://nas.local', immich_allow_insecure_tls: 0 });
expect(await save(user.id, 'https://nas.local', null)).toEqual({
immich_url: 'https://nas.local',
immich_allow_insecure_tls: 0,
});
});
it('SQLF-096: matches the legacy CASE text row-for-row across every (stored url, new url, value) combination', async () => {
@@ -435,10 +512,16 @@ describe('sql-functions (sqlite)', () => {
for (const storedFlag of [0, 1]) {
for (const newUrl of ['https://nas.local', 'https://photos.example.com']) {
for (const value of [null, 0, 1]) {
testDb.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = ? WHERE id = ?').run(storedUrl, storedFlag, user.id);
testDb
.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = ? WHERE id = ?')
.run(storedUrl, storedFlag, user.id);
legacy.run(newUrl, newUrl, value, value, user.id);
const expected = testDb.prepare('SELECT immich_url, immich_allow_insecure_tls FROM users WHERE id = ?').get(user.id);
testDb.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = ? WHERE id = ?').run(storedUrl, storedFlag, user.id);
const expected = testDb
.prepare('SELECT immich_url, immich_allow_insecure_tls FROM users WHERE id = ?')
.get(user.id);
testDb
.prepare('UPDATE users SET immich_url = ?, immich_allow_insecure_tls = ? WHERE id = ?')
.run(storedUrl, storedFlag, user.id);
expect(await save(user.id, newUrl, value)).toEqual(expected);
}
}
@@ -449,7 +532,9 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-097: an unknown platform fails closed for coalesceOverrideWhileSame', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
expect(() => coalesceOverrideWhileSame(foreign, null, 'u.a', 'u.b', 'x')).toThrow(/no implementation for platform FakePlatform/);
expect(() => coalesceOverrideWhileSame(foreign, null, 'u.a', 'u.b', 'x')).toThrow(
/no implementation for platform FakePlatform/,
);
});
});
@@ -489,14 +574,21 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare("UPDATE users SET created_at = '2026-06-15 00:00:00' WHERE id = ?").run(b.id);
const platform = t.em.getPlatform();
const row = await t.em.createQueryBuilder(Users, 'u')
.select([countAll(platform, 'total'), minOf(platform, 'u.created_at', 'oldest'), maxOf(platform, 'u.created_at', 'newest')])
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([
countAll(platform, 'total'),
minOf(platform, 'u.created_at', 'oldest'),
maxOf(platform, 'u.created_at', 'newest'),
])
.where({ id: { $in: [a.id, b.id] } })
.execute('get', false);
expect(row).toEqual({ total: 2, oldest: '2026-01-01 00:00:00', newest: '2026-06-15 00:00:00' });
const expected = testDb
.prepare('SELECT COUNT(*) as total, MIN(created_at) as oldest, MAX(created_at) as newest FROM users WHERE id IN (?, ?)')
.prepare(
'SELECT COUNT(*) as total, MIN(created_at) as oldest, MAX(created_at) as newest FROM users WHERE id IN (?, ?)',
)
.get(a.id, b.id);
expect(row).toEqual(expected);
});
@@ -518,14 +610,16 @@ describe('sql-functions (sqlite)', () => {
createUser(testDb, { role: 'user' });
const platform = t.em.getPlatform();
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select(['u.role', countAll(platform, 'count')])
.groupBy('u.role')
.execute('all', false);
const expected = testDb.prepare('SELECT role, COUNT(*) as count FROM users GROUP BY role').all();
expect([...(rows as { role: string; count: number }[])].sort((a, b) => a.role.localeCompare(b.role)))
.toEqual([...(expected as { role: string; count: number }[])].sort((a, b) => a.role.localeCompare(b.role)));
expect([...(rows as { role: string; count: number }[])].sort((a, b) => a.role.localeCompare(b.role))).toEqual(
[...(expected as { role: string; count: number }[])].sort((a, b) => a.role.localeCompare(b.role)),
);
});
it('SQLF-025: countAll/minOf/maxOf reject an alias that is not a plain identifier', () => {
@@ -549,13 +643,16 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-027: lowerTrim renders LOWER(TRIM(<col>)) as one fragment, matching a hand-written statement', async () => {
const { user } = createUser(testDb, { username: ' Padded Name ' });
const platform = t.em.getPlatform();
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([lowerTrim(platform, 'u.username').as('n')])
.where({ id: user.id })
.execute('get', false);
expect((row as { n: string }).n).toBe('padded name');
const expected = testDb.prepare('SELECT LOWER(TRIM(username)) as n FROM users WHERE id = ?').get(user.id) as { n: string };
const expected = testDb.prepare('SELECT LOWER(TRIM(username)) as n FROM users WHERE id = ?').get(user.id) as {
n: string;
};
expect((row as { n: string }).n).toBe(expected.n);
});
@@ -580,17 +677,21 @@ describe('sql-functions (sqlite)', () => {
// Plan 3c Task 6 — `TripMembersRepository.listWithUserAndInviter`'s `role`
// column (TM2): `CASE WHEN u.id = ? THEN 'owner' ELSE 'member' END`.
it("SQLF-030: caseWhenEquals renders CASE WHEN <col> = ? THEN ? ELSE ? END, matching a hand-written statement", async () => {
it('SQLF-030: caseWhenEquals renders CASE WHEN <col> = ? THEN ? ELSE ? END, matching a hand-written statement', async () => {
const { user: a } = createUser(testDb);
const { user: b } = createUser(testDb);
const platform = t.em.getPlatform();
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select(['u.id', caseWhenEquals(platform, 'u.id', a.id, 'owner', 'member').as('role')])
.where({ id: { $in: [a.id, b.id] } })
.orderBy({ id: 'asc' })
.execute('all', false);
expect(rows).toEqual([{ id: a.id, role: 'owner' }, { id: b.id, role: 'member' }]);
expect(rows).toEqual([
{ id: a.id, role: 'owner' },
{ id: b.id, role: 'member' },
]);
const expected = testDb
.prepare('SELECT id, CASE WHEN id = ? THEN ? ELSE ? END as role FROM users WHERE id IN (?, ?) ORDER BY id ASC')
@@ -601,7 +702,9 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-031: an unknown platform fails closed for caseWhenEquals', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
expect(() => caseWhenEquals(foreign, 'u.id', 1, 'owner', 'member')).toThrow(/no implementation for platform FakePlatform/);
expect(() => caseWhenEquals(foreign, 'u.id', 1, 'owner', 'member')).toThrow(
/no implementation for platform FakePlatform/,
);
});
// Tours (#2586): the assignment projection's `tour_route_geometry` column.
@@ -612,12 +715,16 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare('UPDATE users SET display_name = NULL WHERE id = ?').run(b.id);
const platform = t.em.getPlatform();
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select(['u.id', caseWhenNotNull(platform, 'u.display_name', 'u.username').as('picked')])
.where({ id: { $in: [a.id, b.id] } })
.orderBy({ id: 'asc' })
.execute('all', false);
expect(rows).toEqual([{ id: a.id, picked: a.username }, { id: b.id, picked: null }]);
expect(rows).toEqual([
{ id: a.id, picked: a.username },
{ id: b.id, picked: null },
]);
});
it('SQLF-099: an unknown platform fails closed for caseWhenNotNull', () => {
@@ -640,13 +747,16 @@ describe('sql-functions (sqlite)', () => {
createUser(testDb, { role: 'user' });
const platform = t.em.getPlatform();
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select(['u.role', countAll(platform, 'count')])
.groupBy('u.role')
.orderBy({ [countAllRef(platform)]: 'desc' })
.execute('all', false);
const expected = testDb.prepare('SELECT role, COUNT(*) as count FROM users GROUP BY role ORDER BY COUNT(*) DESC').all();
const expected = testDb
.prepare('SELECT role, COUNT(*) as count FROM users GROUP BY role ORDER BY COUNT(*) DESC')
.all();
expect(rows).toEqual(expected);
});
@@ -677,14 +787,17 @@ describe('sql-functions (sqlite)', () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run(value, user.id);
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([startsWithIsoDate(platform, 'u.display_name').as('matched')])
.where({ id: user.id })
.execute('get', false);
expect(Boolean((row as { matched: number | null }).matched)).toBe(expected);
const raw = testDb
.prepare(`SELECT (display_name GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*') as matched FROM users WHERE id = ?`)
.prepare(
`SELECT (display_name GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*') as matched FROM users WHERE id = ?`,
)
.get(user.id) as { matched: number | null };
expect(Boolean(raw.matched)).toBe(expected);
}
@@ -693,7 +806,9 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-035: an unknown platform fails closed for startsWithIsoDate', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
expect(() => startsWithIsoDate(foreign, 'u.reservation_time')).toThrow(/no implementation for platform FakePlatform/);
expect(() => startsWithIsoDate(foreign, 'u.reservation_time')).toThrow(
/no implementation for platform FakePlatform/,
);
});
it('SQLF-036: substring(ref, start) — the two-arg form — reads from a 1-based position to the end, matching substr(col, N)', async () => {
@@ -701,13 +816,16 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('2026-09-21T13:05:09Z', user.id);
const platform = t.em.getPlatform();
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([substring(platform, 'u.display_name', 12).as('n')])
.where({ id: user.id })
.execute('get', false);
expect((row as { n: string }).n).toBe('13:05:09Z');
const expected = testDb.prepare('SELECT substr(display_name, 12) as n FROM users WHERE id = ?').get(user.id) as { n: string };
const expected = testDb.prepare('SELECT substr(display_name, 12) as n FROM users WHERE id = ?').get(user.id) as {
n: string;
};
expect((row as { n: string }).n).toBe(expected.n);
});
@@ -716,13 +834,16 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('2026-09-21T13:05:09Z', user.id);
const platform = t.em.getPlatform();
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([substring(platform, 'u.display_name', 1, 10).as('n')])
.where({ id: user.id })
.execute('get', false);
expect((row as { n: string }).n).toBe('2026-09-21');
const expected = testDb.prepare('SELECT substr(display_name, 1, 10) as n FROM users WHERE id = ?').get(user.id) as { n: string };
const expected = testDb.prepare('SELECT substr(display_name, 1, 10) as n FROM users WHERE id = ?').get(user.id) as {
n: string;
};
expect((row as { n: string }).n).toBe(expected.n);
});
@@ -740,18 +861,21 @@ describe('sql-functions (sqlite)', () => {
expect(() => substring(foreign, 'u.x', 1)).toThrow(/no implementation for platform FakePlatform/);
});
it('SQLF-040: concat renders <col> || <bound-literal> || <col>, matching the legacy d.date || \'T\' || a.check_in shape', async () => {
it("SQLF-040: concat renders <col> || <bound-literal> || <col>, matching the legacy d.date || 'T' || a.check_in shape", async () => {
const { user } = createUser(testDb, { username: 'concat-me' });
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('13:05', user.id);
const platform = t.em.getPlatform();
const row = await t.em.createQueryBuilder(Users, 'u')
const row = await t.em
.createQueryBuilder(Users, 'u')
.select([concat(platform, { column: 'u.username' }, { value: 'T' }, { column: 'u.display_name' }).as('n')])
.where({ id: user.id })
.execute('get', false);
expect((row as { n: string }).n).toBe('concat-meT13:05');
const expected = testDb.prepare("SELECT username || ? || display_name as n FROM users WHERE id = ?").get('T', user.id) as { n: string };
const expected = testDb
.prepare('SELECT username || ? || display_name as n FROM users WHERE id = ?')
.get('T', user.id) as { n: string };
expect((row as { n: string }).n).toBe(expected.n);
});
@@ -764,7 +888,9 @@ describe('sql-functions (sqlite)', () => {
it('SQLF-042: an unknown platform fails closed for concat', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
expect(() => concat(foreign, { column: 'u.a' }, { value: 'x' })).toThrow(/no implementation for platform FakePlatform/);
expect(() => concat(foreign, { column: 'u.a' }, { value: 'x' })).toThrow(
/no implementation for platform FakePlatform/,
);
});
// §18.1: reservations.accommodation_id is TEXT holding integer ids with no
@@ -780,7 +906,8 @@ describe('sql-functions (sqlite)', () => {
const { user: other } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('15', other.id);
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select('u.id')
.where({ id: { $in: [exact.id, dotZero.id, other.id] }, [castInteger(platform, 'u.display_name')]: 14 })
.orderBy({ id: 'asc' })
@@ -802,9 +929,13 @@ describe('sql-functions (sqlite)', () => {
const { user: mismatched } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run(String(exact.id), mismatched.id);
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select('u.id')
.where({ id: { $in: [exact.id, dotZero.id, mismatched.id] }, [castInteger(platform, 'u.display_name')]: columnRef(platform, 'u.id') })
.where({
id: { $in: [exact.id, dotZero.id, mismatched.id] },
[castInteger(platform, 'u.display_name')]: columnRef(platform, 'u.id'),
})
.orderBy({ id: 'asc' })
.execute('all', false);
expect((rows as { id: number }[]).map((r) => r.id)).toEqual([exact.id, dotZero.id]);
@@ -831,7 +962,8 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare("UPDATE users SET created_at = '2026-06-20 00:00:00' WHERE id = ?").run(c.id);
const target = '2026-06-18';
const rows = await t.em.createQueryBuilder(Users, 'u')
const rows = await t.em
.createQueryBuilder(Users, 'u')
.select('u.id')
.where({ id: { $in: [a.id, b.id, c.id] } })
.orderBy({ [dayDistance(platform, 'u.created_at', target)]: 'asc' })
@@ -880,7 +1012,8 @@ describe('sql-functions (sqlite)', () => {
const { user } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run(value, user.id);
const row = await t.em.getKysely<UsersKyselyDB>()
const row = await t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select((eb) => [startsWithIsoDateKysely(platform, eb, 'display_name').as('matched')])
.where('id', '=', user.id)
@@ -888,7 +1021,9 @@ describe('sql-functions (sqlite)', () => {
expect(Boolean(row.matched)).toBe(expected);
const raw = testDb
.prepare(`SELECT (display_name GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*') as matched FROM users WHERE id = ?`)
.prepare(
`SELECT (display_name GLOB '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*') as matched FROM users WHERE id = ?`,
)
.get(user.id) as { matched: number | null };
expect(Boolean(raw.matched)).toBe(expected);
}
@@ -898,7 +1033,9 @@ describe('sql-functions (sqlite)', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
const eb = expressionBuilder<UsersKyselyDB, 'users'>();
expect(() => startsWithIsoDateKysely(foreign, eb, 'username')).toThrow(/no implementation for platform FakePlatform/);
expect(() => startsWithIsoDateKysely(foreign, eb, 'username')).toThrow(
/no implementation for platform FakePlatform/,
);
});
it('SQLF-051: substringKysely(ref, start) — the two-arg form — matches substr(col, N)', async () => {
@@ -906,14 +1043,17 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('2026-09-21T13:05:09Z', user.id);
const platform = t.em.getPlatform();
const row = await t.em.getKysely<UsersKyselyDB>()
const row = await t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select((eb) => [substringKysely(platform, eb, 'display_name', 12).as('n')])
.where('id', '=', user.id)
.executeTakeFirstOrThrow();
expect(row.n).toBe('13:05:09Z');
const expected = testDb.prepare('SELECT substr(display_name, 12) as n FROM users WHERE id = ?').get(user.id) as { n: string };
const expected = testDb.prepare('SELECT substr(display_name, 12) as n FROM users WHERE id = ?').get(user.id) as {
n: string;
};
expect(row.n).toBe(expected.n);
});
@@ -922,14 +1062,17 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('2026-09-21T13:05:09Z', user.id);
const platform = t.em.getPlatform();
const row = await t.em.getKysely<UsersKyselyDB>()
const row = await t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select((eb) => [substringKysely(platform, eb, 'display_name', 1, 10).as('n')])
.where('id', '=', user.id)
.executeTakeFirstOrThrow();
expect(row.n).toBe('2026-09-21');
const expected = testDb.prepare('SELECT substr(display_name, 1, 10) as n FROM users WHERE id = ?').get(user.id) as { n: string };
const expected = testDb.prepare('SELECT substr(display_name, 1, 10) as n FROM users WHERE id = ?').get(user.id) as {
n: string;
};
expect(row.n).toBe(expected.n);
});
@@ -947,17 +1090,24 @@ describe('sql-functions (sqlite)', () => {
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('2026-09-21T13:05', user.id);
const platform = t.em.getPlatform();
const row = await t.em.getKysely<UsersKyselyDB>()
const row = await t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select((eb) => [
concatKysely(platform, eb, { column: 'username' }, { value: '@' }, { expression: substringKysely(platform, eb, 'display_name', 12) }).as('n'),
concatKysely(
platform,
eb,
{ column: 'username' },
{ value: '@' },
{ expression: substringKysely(platform, eb, 'display_name', 12) },
).as('n'),
])
.where('id', '=', user.id)
.executeTakeFirstOrThrow();
expect(row.n).toBe('concat-me@13:05');
const expected = testDb
.prepare("SELECT username || ? || substr(display_name, 12) as n FROM users WHERE id = ?")
.prepare('SELECT username || ? || substr(display_name, 12) as n FROM users WHERE id = ?')
.get('@', user.id) as { n: string };
expect(row.n).toBe(expected.n);
});
@@ -973,7 +1123,9 @@ describe('sql-functions (sqlite)', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
const eb = expressionBuilder<UsersKyselyDB, 'users'>();
expect(() => concatKysely(foreign, eb, { column: 'username' }, { value: 'x' })).toThrow(/no implementation for platform FakePlatform/);
expect(() => concatKysely(foreign, eb, { column: 'username' }, { value: 'x' })).toThrow(
/no implementation for platform FakePlatform/,
);
expect(() => substringKysely(foreign, eb, 'username', 1)).toThrow(/no implementation for platform FakePlatform/);
});
@@ -986,7 +1138,8 @@ describe('sql-functions (sqlite)', () => {
const { user: other } = createUser(testDb);
testDb.prepare('UPDATE users SET display_name = ? WHERE id = ?').run('15', other.id);
const rows = await t.em.getKysely<UsersKyselyDB>()
const rows = await t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select('id')
.where('id', 'in', [exact.id, dotZero.id, other.id])
@@ -1008,16 +1161,24 @@ describe('sql-functions (sqlite)', () => {
expect(() => castIntegerKysely(foreign, eb, 'display_name')).toThrow(/no implementation for platform FakePlatform/);
});
it('SQLF-059: DY23\'s exact shape compiles to the expected SQL text (compiled {sql, parameters} capture)', () => {
it("SQLF-059: DY23's exact shape compiles to the expected SQL text (compiled {sql, parameters} capture)", () => {
const platform = t.em.getPlatform();
const compiled = t.em.getKysely<UsersKyselyDB>()
const compiled = t.em
.getKysely<UsersKyselyDB>()
.updateTable('users')
.set((eb) => ({
username: eb
.case()
.when('username', 'is', null)
.then(eb.val('2026-09-21'))
.else(concatKysely(platform, eb, { value: '2026-09-21' }, { expression: substringKysely(platform, eb, 'created_at', 11) }))
.else(
concatKysely(
platform,
eb,
{ value: '2026-09-21' },
{ expression: substringKysely(platform, eb, 'created_at', 11) },
),
)
.end(),
}))
.where('id', '=', 1)
@@ -1041,9 +1202,9 @@ describe('sql-functions (sqlite)', () => {
const typed = await t.em.find(Users, { [collateNoCase(platform, 'username')]: 'mixedcase' });
expect(typed.map((r) => r.id)).toEqual([mixed.id]);
const legacy = testDb
.prepare('SELECT id FROM users WHERE username = ? COLLATE NOCASE')
.all('mixedcase') as { id: number }[];
const legacy = testDb.prepare('SELECT id FROM users WHERE username = ? COLLATE NOCASE').all('mixedcase') as {
id: number;
}[];
expect(typed.map((r) => r.id)).toEqual(legacy.map((r) => r.id));
const none = await t.em.find(Users, { [collateNoCase(platform, 'username')]: 'nobody-has-this-name' });
@@ -1069,9 +1230,13 @@ describe('sql-functions (sqlite)', () => {
// recognises the Unicode case mapping), which is exactly why they agree
// rather than diverge — this is genuinely the "no divergence found"
// branch R9 anticipates, not a skipped check.
const asciiCollate = testDb.prepare('SELECT (? = ? COLLATE NOCASE) as m').get('ASCII@x.com', 'ascii@x.com') as { m: number };
const asciiCollate = testDb.prepare('SELECT (? = ? COLLATE NOCASE) as m').get('ASCII@x.com', 'ascii@x.com') as {
m: number;
};
expect(asciiCollate.m).toBe(1);
const nonAsciiCollate = testDb.prepare('SELECT (? = ? COLLATE NOCASE) as m').get('JOSÉ@x.com', 'josé@x.com') as { m: number };
const nonAsciiCollate = testDb.prepare('SELECT (? = ? COLLATE NOCASE) as m').get('JOSÉ@x.com', 'josé@x.com') as {
m: number;
};
expect(nonAsciiCollate.m).toBe(0);
});
@@ -1110,7 +1275,9 @@ describe('sql-functions (sqlite)', () => {
const { user } = createUser(testDb, { username: ' JOSÉ ' });
const platform = t.em.getPlatform();
const exact = await t.em.findOne(Users, { [lowerTrim(platform, 'username')]: lowerTrimParam(platform, ' josÉ ') });
const exact = await t.em.findOne(Users, {
[lowerTrim(platform, 'username')]: lowerTrimParam(platform, ' josÉ '),
});
expect(exact?.id).toBe(user.id);
// A JS-lowered+trimmed value-side bind (mixing engines, program rule 18)
@@ -1141,7 +1308,8 @@ describe('sql-functions (sqlite)', () => {
const platform = t.em.getPlatform();
const createdAt = 1_700_000_000_000; // fixed epoch-millis value — the exact shape gp.created_at stores
const compiled = t.em.getKysely<UsersKyselyDB>()
const compiled = t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select((eb) => [unixEpochToIsoKysely(platform, eb, eb.val(createdAt)).as('iso')])
.compile();
@@ -1160,7 +1328,9 @@ describe('sql-functions (sqlite)', () => {
class FakePlatform extends Platform {}
const foreign = new FakePlatform();
const eb = expressionBuilder<UsersKyselyDB, 'users'>();
expect(() => unixEpochToIsoKysely(foreign, eb, 'created_at')).toThrow(/no implementation for platform FakePlatform/);
expect(() => unixEpochToIsoKysely(foreign, eb, 'created_at')).toThrow(
/no implementation for platform FakePlatform/,
);
});
// Plan 3h Task 0 — no consumer yet: Task 3 (DSY2), Task 4 (RU4), Task 5
@@ -1231,7 +1401,8 @@ describe('sql-functions (sqlite)', () => {
createUser(testDb); // one row so the anchor SELECT FROM users has something to select
const platform = t.em.getPlatform();
const compiled = t.em.getKysely<UsersKyselyDB>()
const compiled = t.em
.getKysely<UsersKyselyDB>()
.selectFrom('users')
.select((eb) => [nowPlusSecondsKysely(platform, eb, 45).as('d')])
.compile();
@@ -1280,7 +1451,11 @@ describe('sql-functions (sqlite)', () => {
const platform = t.em.getPlatform();
testDb.prepare('DELETE FROM found_again_probe WHERE id = 1').run();
testDb.prepare('INSERT INTO found_again_probe (id, state, file_id) VALUES (1, ?, ?)').run(state, fileId);
testDb.prepare(`UPDATE found_again_probe SET state = ${foundAgainState(platform, 'state', 'file_id').sql} WHERE id = 1`).run();
testDb
.prepare(
`UPDATE found_again_probe SET state = ${foundAgainState(platform, 'state', 'file_id').sql} WHERE id = 1`,
)
.run();
return (testDb.prepare('SELECT state FROM found_again_probe WHERE id = 1').get() as { state: string }).state;
}
@@ -1309,7 +1484,7 @@ describe('sql-functions (sqlite)', () => {
// `last_seen_at = CURRENT_TIMESTAMP` inside the partial-index `ON
// CONFLICT ... DO UPDATE`.
describe('currentTimestampKysely', () => {
it('SQLF-087: compiles to the bare CURRENT_TIMESTAMP keyword with no parameters, matching currentTimestamp\'s own raw text and a real row\'s clock value', async () => {
it("SQLF-087: compiles to the bare CURRENT_TIMESTAMP keyword with no parameters, matching currentTimestamp's own raw text and a real row's clock value", async () => {
const platform = t.em.getPlatform();
const compiled = t.em
.getKysely<UsersKyselyDB>()
@@ -1322,7 +1497,9 @@ describe('sql-functions (sqlite)', () => {
createUser(testDb);
const got = testDb.prepare(compiled.sql).get(...compiled.parameters) as { d: string };
const raw = testDb.prepare(`SELECT ${currentTimestamp(platform).sql} as d`).get() as { d: string };
expect(Math.abs(new Date(`${got.d.replace(' ', 'T')}Z`).getTime() - new Date(`${raw.d.replace(' ', 'T')}Z`).getTime())).toBeLessThan(10_000);
expect(
Math.abs(new Date(`${got.d.replace(' ', 'T')}Z`).getTime() - new Date(`${raw.d.replace(' ', 'T')}Z`).getTime()),
).toBeLessThan(10_000);
});
it('SQLF-088: current_timestamp() called as a function is rejected by SQLite — proving the bare-keyword shape is load-bearing, not a style choice', () => {
@@ -1384,7 +1561,10 @@ function galleryChronologicalOrderExpr(
// property of the QUERY, never of the `DB` interface itself, so a
// correlated-subquery helper with a fixed alias contract is typed this
// way rather than generic over an arbitrary caller `DB`/`TB`.
eb: ExpressionBuilder<GalleryOrderTestDB & { gp: GalleryOrderTestDB['journey_photos']; tp: GalleryOrderTestDB['trek_photos'] }, 'gp' | 'tp'>,
eb: ExpressionBuilder<
GalleryOrderTestDB & { gp: GalleryOrderTestDB['journey_photos']; tp: GalleryOrderTestDB['trek_photos'] },
'gp' | 'tp'
>,
) {
return eb.fn.coalesce(
eb.fn<string | null>('nullif', [eb.ref('tp.taken_at'), eb.val('')]),
@@ -1399,7 +1579,12 @@ function galleryChronologicalOrderExpr(
eb2,
{ column: 'je.entry_date' },
{ value: 'T' },
{ expression: eb2.fn.coalesce(eb2.fn<string | null>('nullif', [eb2.ref('je.entry_time'), eb2.val('')]), eb2.val('00:00')) },
{
expression: eb2.fn.coalesce(
eb2.fn<string | null>('nullif', [eb2.ref('je.entry_time'), eb2.val('')]),
eb2.val('00:00'),
),
},
),
)
.as('min_dt'),
@@ -1420,7 +1605,8 @@ describe('GALLERY_CHRONOLOGICAL_ORDER Kysely rebuild (Plan 3g Task 0, R1 worked
// entry_time left NULL — the entry-linked tier's own COALESCE(NULIF(entry_time,''),'00:00') fallback
const trekPhoto = (takenAt: string | null) => {
const r = testDb.prepare('INSERT INTO trek_photos (provider, asset_id, owner_id, taken_at) VALUES (?, ?, ?, ?)')
const r = testDb
.prepare('INSERT INTO trek_photos (provider, asset_id, owner_id, taken_at) VALUES (?, ?, ?, ?)')
.run('immich', `asset-${Math.random()}`, user.id, takenAt);
return r.lastInsertRowid as number;
};
@@ -1469,7 +1655,8 @@ describe('GALLERY_CHRONOLOGICAL_ORDER Kysely rebuild (Plan 3g Task 0, R1 worked
.execute();
const legacy = testDb
.prepare(`
.prepare(
`
SELECT gp.id
FROM journey_photos gp JOIN trek_photos tp ON tp.id = gp.photo_id
WHERE gp.journey_id = ?
@@ -1483,7 +1670,8 @@ describe('GALLERY_CHRONOLOGICAL_ORDER Kysely rebuild (Plan 3g Task 0, R1 worked
) ASC,
gp.sort_order ASC,
gp.id ASC
`)
`,
)
.all(journey.id) as { id: number }[];
expect(rows.map((r) => r.id)).toEqual(legacy.map((r) => r.id));