Skip to content

Commit b169f21

Browse files
committed
test(plugin-email): pin the queue-delivery fake engine's delete() to ObjectQL's own dispatch (#4550)
`check:engine-double-contract` flagged the fake engine in email-plugin.queue-delivery.test.ts: its `delete()` hand-mirrored the engine's guard (`if (opts?.where?.id == null) throw`) instead of routing through `assertEngineDeleteDispatch`. That mirror is looser than the engine on exactly the case a copy always drops — `where: { id: { $in: [...] } }` reads as an id and is a multi-row predicate, which the real engine rejects without `multi`. Routes through the producer's own predicate, same shape as the other 13 pinned doubles, and adds the `@objectstack/objectql` devDependency the import needs (the precedent in plugin-approvals / plugin-sharing). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017MCKJaEomEqg4tvz4SzdNd
1 parent e09ee47 commit b169f21

3 files changed

Lines changed: 19 additions & 4 deletions

File tree

‎packages/plugins/plugin-email/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"nodemailer": "^9.0.3"
2626
},
2727
"devDependencies": {
28+
"@objectstack/objectql": "workspace:*",
2829
"@objectstack/service-queue": "workspace:*",
2930
"@objectstack/service-settings": "workspace:*",
3031
"@types/node": "^26.1.2",

‎packages/plugins/plugin-email/src/email-plugin.queue-delivery.test.ts‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import { describe, it, expect, vi, beforeEach } from 'vitest';
1414
import { createMemoryQueue } from '@objectstack/core';
15+
import { assertEngineDeleteDispatch } from '@objectstack/objectql';
1516
import { DbQueueAdapter } from '@objectstack/service-queue';
1617
import { EmailServicePlugin, resolveDurableQueue } from './email-plugin.js';
1718
import { EmailService, EMAIL_SEND_QUEUE } from './email-service.js';
@@ -82,10 +83,20 @@ function fakeEngine() {
8283
return r;
8384
},
8485
async delete(table: string, opts: any) {
85-
const id = opts?.where?.id;
86-
if (id == null) throw new Error('Delete requires an ID or options.multi=true');
87-
tables.set(table, rowsOf(table).filter((r) => r.id !== id));
88-
return { id };
86+
// [#4550] Pinned to ObjectQL.delete's OWN dispatch predicate. A double
87+
// looser than the engine it stands in for is how #4434 shipped a REST
88+
// route that answered 500 to every caller with its suite green — and
89+
// the half a hand-written mirror drops is exactly the scalar test
90+
// (`where: { id: { $in: [...] } }` looks like an id and is not one).
91+
const dispatch = assertEngineDeleteDispatch(opts);
92+
if (dispatch.kind === 'multi') {
93+
const survivors = rowsOf(table).filter((r) => !matches(r, opts?.where ?? {}));
94+
const deleted = rowsOf(table).length - survivors.length;
95+
tables.set(table, survivors);
96+
return { deleted };
97+
}
98+
tables.set(table, rowsOf(table).filter((r) => r.id !== dispatch.id));
99+
return { id: dispatch.id };
89100
},
90101
};
91102
}

‎pnpm-lock.yaml‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)