- Migrations (additive): veterinary medicines with lots and movement ledger, treatment medicine/lot/dose links, label particulars, authority notices, apiary restrictions, compliance settings and acknowledgements; notification category COMPLIANCE, added to non-empty Telegram lists. - Permissions veterinary-medicines:* and compliance:* with role grants. - LOCKABLE_TABLES: Apiary locks before Colony; T6 tables appended. - Backup exports and restores the new tables in FK order; unsealed files skip them and report dropped medicine links; an open TSK draft in the target wins. - describeNullContracts shared helper; Telegram backlog e2e no longer depends on the time of day. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import {
|
|
ScratchDatabase,
|
|
createScratchDatabase,
|
|
} from './utils/migration-harness';
|
|
|
|
const BEFORE_COMPLIANCE_CATEGORY =
|
|
'20261016100300_t6_notification_category_compliance';
|
|
const COMPLIANCE_DEFAULT = '20261016100400_t6_notification_compliance_default';
|
|
|
|
const TIMEOUT_MS = 240_000;
|
|
|
|
async function seedUsers(db: ScratchDatabase): Promise<void> {
|
|
await db.query(
|
|
`INSERT INTO "Role" ("id", "key", "name", "updatedAt")
|
|
VALUES ('role-1', 'MIG_TEST', 'Migration test', now())`,
|
|
);
|
|
for (const id of ['user-a', 'user-b', 'user-c', 'user-d']) {
|
|
await db.query(
|
|
`INSERT INTO "User" ("id", "email", "passwordHash", "name", "roleId", "updatedAt")
|
|
VALUES ($1, $2, 'x', $1, 'role-1', now())`,
|
|
[id, `${id}@example.com`],
|
|
);
|
|
}
|
|
}
|
|
|
|
describe('T6 migrations (e2e)', () => {
|
|
let db: ScratchDatabase | undefined;
|
|
|
|
afterEach(async () => {
|
|
await db?.drop();
|
|
db = undefined;
|
|
});
|
|
|
|
it(
|
|
'adds COMPLIANCE to the Telegram categories of every existing preference and keeps the rest, an empty list (opt-out) stays empty',
|
|
async () => {
|
|
db = await createScratchDatabase(BEFORE_COMPLIANCE_CATEGORY);
|
|
await seedUsers(db);
|
|
await db.query(
|
|
`INSERT INTO "NotificationPreference" ("userId", "telegramCategories", "updatedAt") VALUES
|
|
('user-a', DEFAULT, now()),
|
|
('user-b', '{SYSTEM}', now()),
|
|
('user-c', '{}', now()),
|
|
('user-d', '{DIGEST,TASK_DUE,SEASON}', now())`,
|
|
);
|
|
|
|
await db.migrateThrough(COMPLIANCE_DEFAULT);
|
|
|
|
expect(
|
|
await db.query(
|
|
`SELECT "userId", "telegramCategories"::text AS "categories"
|
|
FROM "NotificationPreference" ORDER BY "userId"`,
|
|
),
|
|
).toEqual([
|
|
{ userId: 'user-a', categories: '{TASK_DUE,DIGEST,COMPLIANCE}' },
|
|
{ userId: 'user-b', categories: '{SYSTEM,COMPLIANCE}' },
|
|
{ userId: 'user-c', categories: '{}' },
|
|
{ userId: 'user-d', categories: '{DIGEST,TASK_DUE,SEASON,COMPLIANCE}' },
|
|
]);
|
|
},
|
|
TIMEOUT_MS,
|
|
);
|
|
|
|
it(
|
|
'gives a preference created afterwards the new default and lists COMPLIANCE once',
|
|
async () => {
|
|
db = await createScratchDatabase(BEFORE_COMPLIANCE_CATEGORY);
|
|
await seedUsers(db);
|
|
|
|
await db.migrateThrough(COMPLIANCE_DEFAULT);
|
|
await db.query(
|
|
`INSERT INTO "NotificationPreference" ("userId", "updatedAt") VALUES ('user-a', now())`,
|
|
);
|
|
|
|
expect(
|
|
await db.query(
|
|
`SELECT "telegramCategories"::text AS "categories" FROM "NotificationPreference"`,
|
|
),
|
|
).toEqual([{ categories: '{TASK_DUE,DIGEST,COMPLIANCE}' }]);
|
|
expect(
|
|
await db.query(
|
|
`SELECT enum_range(NULL::"NotificationCategory")::text AS "values"`,
|
|
),
|
|
).toEqual([
|
|
{
|
|
values:
|
|
'{TASK_DUE,DIGEST,SUGGESTION,WITHDRAWAL,SEASON,SYSTEM,COMPLIANCE}',
|
|
},
|
|
]);
|
|
},
|
|
TIMEOUT_MS,
|
|
);
|
|
});
|