Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion src/deliver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function recordingRouter() {
return {
grants,
mail,
router: {
router: <HookMailRouter>{
routeMail: (
_address: string,
rawMessage: string,
Expand Down Expand Up @@ -203,6 +203,41 @@ describe("createRunTriggerDeliverer", () => {
expect(recorded.grants).toHaveLength(1);
});

test("an async router answering false for grants is unroutable", async () => {
const sender = await durableSystemSender();
const recorded = recordingRouter();
recorded.router.sendRunGrants = async () => false;
const error = await deliverer(recorded.router, sender.sender)
.to(ADDRESS, "tick", "tnt_1", undefined)
.then(
() => {
throw new Error("the dead run delivered");
},
(e: unknown) => e,
);

expect(isRunTriggerUnroutable(error)).toBe(true);
expect((error as { code: string }).code).toBe(RUN_GRANTS_NOT_ROUTABLE);
expect(recorded.mail).toEqual([]);
});

test("an async router answering false for mail is unroutable", async () => {
const sender = await durableSystemSender();
const recorded = recordingRouter();
recorded.router.routeMail = async () => false;
const error = await deliverer(recorded.router, sender.sender)
.to(ADDRESS, "tick", "tnt_1", undefined)
.then(
() => {
throw new Error("the dead run delivered");
},
(e: unknown) => e,
);

expect(isRunTriggerUnroutable(error)).toBe(true);
expect((error as { code: string }).code).toBe(RUN_MAIL_NOT_ROUTABLE);
});

test("rejected grants deliver nothing", async () => {
const sender = await durableSystemSender();
const recorded = recordingRouter();
Expand Down
31 changes: 16 additions & 15 deletions src/deliver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ export type HookMailRouter = {
rawMessage: string,
authenticatedSender: string,
messageId?: string,
) => boolean;
) => boolean | Promise<boolean>;
sendRunGrants: (
address: string,
runId: string,
stepGrants: RunGrantsFrame["stepGrants"],
senderIdentities: RunGrantsFrame["senderIdentities"],
) => boolean;
) => boolean | Promise<boolean>;
};

export type RunTriggerMaterialize = ReturnType<
Expand Down Expand Up @@ -130,11 +130,13 @@ export function createRunTriggerDeliverer(
// Co-deliver the system sender's durable key on the grants barrier so
// the recipient can verify the trigger mail against the key the hub
// vouches for, exactly as a person-originated trigger does.
if (
!opts.router.sendRunGrants(address, runId, grants.stepGrants, [
{ address: sender.address, publicKey: sender.publicKey },
])
) {
const grantsRouted = await opts.router.sendRunGrants(
address,
runId,
grants.stepGrants,
[{ address: sender.address, publicKey: sender.publicKey }],
);
if (!grantsRouted) {
throw new RunTriggerUnroutableError(
RUN_GRANTS_NOT_ROUTABLE,
address,
Expand All @@ -150,14 +152,13 @@ export function createRunTriggerDeliverer(
subject,
sender,
});
if (
!opts.router.routeMail(
address,
raw.base64,
sender.address,
raw.messageId,
)
) {
const mailRouted = await opts.router.routeMail(
address,
raw.base64,
sender.address,
raw.messageId,
);
if (!mailRouted) {
throw new RunTriggerUnroutableError(
RUN_MAIL_NOT_ROUTABLE,
address,
Expand Down
Loading