feat: T6 wave 1 – compliance schema, permissions, lock order, backup
- 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]>
This commit is contained in:
1 parent
7a1480138d
commit
8a31c42578
23 files changed
+2028
-84
No files matched your search
@@ -0,0 +1,93 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user