feat: T4 wave 3 - notifications per user, suggestions API, varroa bands, split/queen events
- Schema III: per-user notifications (broadcast fan-out migration, category, link, requiredPermissions filter), notification preferences and settings endpoints, calendar withdrawal switch, occupiedFrames - Suggestions API: list/summary with permission visibility and paging, accept/dismiss/reopen incl. bulk, SuggestionView on all responses - Varroa traffic light by season and method, young colonies, treatment effect window, sample-based percentage; shared withdrawal-window helper on Berlin calendar days; GET /varroa-measurements/thresholds - ColonySplit and QueenAssigned domain events with calendar and webhook handlers - Outbox claims compare against now() rounded to timestamp(3); fixes the flaky e2e claims of rows written with SQL now() Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
d99a697739
commit
f468dc5f75
87 files changed
+6022
-510
No files matched your search
@@ -45,7 +45,7 @@ describe('AutomationService', () => {
|
||||
$connect: jest.Mock;
|
||||
};
|
||||
let auditLog: { record: jest.Mock };
|
||||
let notifications: { record: jest.Mock };
|
||||
let notifications: { recordForPermission: jest.Mock };
|
||||
let tasks: { createInTx: jest.Mock };
|
||||
|
||||
const queenlessRule: Rule = {
|
||||
@@ -102,7 +102,9 @@ describe('AutomationService', () => {
|
||||
$connect: jest.fn(),
|
||||
};
|
||||
auditLog = { record: jest.fn().mockResolvedValue(undefined) };
|
||||
notifications = { record: jest.fn().mockResolvedValue({ id: 'n-1' }) };
|
||||
notifications = {
|
||||
recordForPermission: jest.fn().mockResolvedValue([{ id: 'n-1' }]),
|
||||
};
|
||||
tasks = { createInTx: jest.fn().mockResolvedValue({ id: 'task-1' }) };
|
||||
service = new AutomationService(
|
||||
prisma as unknown as PrismaService,
|
||||
@@ -258,7 +260,9 @@ describe('AutomationService', () => {
|
||||
staged.push(client === tx ? 'task' : 'task outside tx');
|
||||
return Promise.resolve({ id: 'task-1' });
|
||||
});
|
||||
notifications.record.mockRejectedValue(new Error('notifications down'));
|
||||
notifications.recordForPermission.mockRejectedValue(
|
||||
new Error('notifications down'),
|
||||
);
|
||||
auditLog.record.mockImplementation((_entry: unknown, client: unknown) => {
|
||||
staged.push(client === tx ? 'audit' : 'audit outside tx');
|
||||
return Promise.resolve();
|
||||
@@ -303,9 +307,11 @@ describe('AutomationService', () => {
|
||||
},
|
||||
null,
|
||||
);
|
||||
expect(notifications.record).toHaveBeenCalledWith(
|
||||
expect(notifications.recordForPermission).toHaveBeenCalledWith(
|
||||
'tasks:read',
|
||||
{
|
||||
priority: 'WARNUNG',
|
||||
category: 'SUGGESTION',
|
||||
message: 'Kontrolle empfohlen',
|
||||
relatedType: 'COLONY',
|
||||
relatedId: 'colony-1',
|
||||
@@ -332,7 +338,9 @@ describe('AutomationService', () => {
|
||||
});
|
||||
|
||||
it('lets a failed notification write propagate, so the delivery is rolled back and retried', async () => {
|
||||
notifications.record.mockRejectedValue(new Error('notifications down'));
|
||||
notifications.recordForPermission.mockRejectedValue(
|
||||
new Error('notifications down'),
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.evaluateInspectionIn(asTx(), queenlessInspection as never),
|
||||
@@ -355,7 +363,7 @@ describe('AutomationService', () => {
|
||||
select: { id: true },
|
||||
});
|
||||
expect(tasks.createInTx).not.toHaveBeenCalled();
|
||||
expect(notifications.record).not.toHaveBeenCalled();
|
||||
expect(notifications.recordForPermission).not.toHaveBeenCalled();
|
||||
expect(auditLog.record).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -403,7 +411,7 @@ describe('AutomationService', () => {
|
||||
await expect(
|
||||
service.evaluateInspectionIn(asTx(), queenlessInspection as never),
|
||||
).rejects.toThrow('tasks down');
|
||||
expect(notifications.record).not.toHaveBeenCalled();
|
||||
expect(notifications.recordForPermission).not.toHaveBeenCalled();
|
||||
expect(auditLog.record).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -426,7 +434,7 @@ describe('AutomationService', () => {
|
||||
await service.evaluateInspectionIn(asTx(), queenlessInspection as never);
|
||||
|
||||
expect(tasks.createInTx).toHaveBeenCalledTimes(1);
|
||||
expect(notifications.record).toHaveBeenCalledTimes(1);
|
||||
expect(notifications.recordForPermission).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not trigger when the queen was seen', async () => {
|
||||
|
||||
Reference in new issue
Block a user