@blokjs/trigger-webhook 0.6.18 → 0.6.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -4
- package/CHANGELOG.md +0 -22
- package/src/WebhookTrigger.integration.test.ts +0 -162
- package/src/WebhookTrigger.test.ts +0 -252
- package/src/WebhookTrigger.ts +0 -459
- package/src/index.ts +0 -51
- package/src/verifiers.test.ts +0 -316
- package/src/verifiers.ts +0 -357
- package/tsconfig.json +0 -32
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blokjs/trigger-webhook",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.19",
|
|
4
|
+
"files": ["dist"],
|
|
4
5
|
"description": "Webhook trigger for Blok workflows - supports GitHub, Stripe, Shopify, and custom webhooks",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "dist/index.js",
|
|
@@ -14,9 +15,9 @@
|
|
|
14
15
|
"author": "Deskree Technologies Inc.",
|
|
15
16
|
"license": "Apache-2.0",
|
|
16
17
|
"dependencies": {
|
|
17
|
-
"@blokjs/helper": "^0.6.
|
|
18
|
-
"@blokjs/runner": "^0.6.
|
|
19
|
-
"@blokjs/shared": "^0.6.
|
|
18
|
+
"@blokjs/helper": "^0.6.19",
|
|
19
|
+
"@blokjs/runner": "^0.6.19",
|
|
20
|
+
"@blokjs/shared": "^0.6.19",
|
|
20
21
|
"@opentelemetry/api": "^1.9.0",
|
|
21
22
|
"hono": "^4.11.7",
|
|
22
23
|
"uuid": "^11.1.0"
|
package/CHANGELOG.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# @blokjs/trigger-webhook
|
|
2
|
-
|
|
3
|
-
## 0.2.0
|
|
4
|
-
|
|
5
|
-
### Minor Changes
|
|
6
|
-
|
|
7
|
-
- Initial public release of Blok packages.
|
|
8
|
-
|
|
9
|
-
This release includes:
|
|
10
|
-
|
|
11
|
-
- Core packages: @blokjs/shared, @blokjs/helper, @blokjs/runner
|
|
12
|
-
- Node packages: @blokjs/api-call, @blokjs/if-else, @blokjs/react
|
|
13
|
-
- Trigger packages: pubsub, queue, webhook, websocket, worker, cron, grpc
|
|
14
|
-
- CLI tool: blokctl
|
|
15
|
-
- Editor support: @blokjs/lsp-server, @blokjs/syntax
|
|
16
|
-
|
|
17
|
-
### Patch Changes
|
|
18
|
-
|
|
19
|
-
- Updated dependencies
|
|
20
|
-
- @blokjs/shared@0.2.0
|
|
21
|
-
- @blokjs/helper@0.2.0
|
|
22
|
-
- @blokjs/runner@0.2.0
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v0.7 PR 4 — full end-to-end webhook trigger integration test.
|
|
3
|
-
*
|
|
4
|
-
* Spins up a real Hono app + `@hono/node-server` with a real
|
|
5
|
-
* WebhookTrigger configured for a GitHub-style provider. Sends a
|
|
6
|
-
* signed POST via native `fetch` and asserts the workflow ran. A
|
|
7
|
-
* second POST with the same delivery id exercises the replay-cache
|
|
8
|
-
* dedup path and expects `{ status: "duplicate" }`.
|
|
9
|
-
*
|
|
10
|
-
* Complements `WebhookTrigger.test.ts` + `verifiers.test.ts` (unit
|
|
11
|
-
* coverage of the public surface).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { createHmac } from "node:crypto";
|
|
15
|
-
import type { Server } from "node:http";
|
|
16
|
-
import { NodeMap, RunTracker, WorkflowRegistry, defineNode } from "@blokjs/runner";
|
|
17
|
-
import { serve } from "@hono/node-server";
|
|
18
|
-
import { Hono } from "hono";
|
|
19
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
20
|
-
import { z } from "zod";
|
|
21
|
-
|
|
22
|
-
vi.mock("@opentelemetry/api", () => ({
|
|
23
|
-
trace: {
|
|
24
|
-
getTracer: () => ({
|
|
25
|
-
startActiveSpan: (_name: string, fn: (span: unknown) => unknown) =>
|
|
26
|
-
fn({ setAttribute: vi.fn(), setStatus: vi.fn(), recordException: vi.fn(), end: vi.fn() }),
|
|
27
|
-
}),
|
|
28
|
-
},
|
|
29
|
-
metrics: {
|
|
30
|
-
getMeter: () => ({
|
|
31
|
-
createCounter: () => ({ add: vi.fn() }),
|
|
32
|
-
createHistogram: () => ({ record: vi.fn() }),
|
|
33
|
-
createGauge: () => ({ record: vi.fn() }),
|
|
34
|
-
createObservableGauge: () => ({ addCallback: vi.fn() }),
|
|
35
|
-
}),
|
|
36
|
-
},
|
|
37
|
-
SpanStatusCode: { OK: 0, ERROR: 1 },
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
import WebhookTriggerClass, { _setActiveWebhookTrigger } from "./WebhookTrigger";
|
|
41
|
-
|
|
42
|
-
const TEST_PORT = 4903;
|
|
43
|
-
const SECRET = "shhh-its-a-secret-1234567890";
|
|
44
|
-
|
|
45
|
-
function hmacHex(body: string): string {
|
|
46
|
-
return createHmac("sha256", SECRET).update(body).digest("hex");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const handleNode = defineNode({
|
|
50
|
-
name: "handle-event",
|
|
51
|
-
description: "test fixture — record the event id + type",
|
|
52
|
-
input: z.object({}).passthrough(),
|
|
53
|
-
output: z.object({ handled: z.boolean(), eventId: z.string() }),
|
|
54
|
-
async execute(ctx) {
|
|
55
|
-
const body = (ctx.request?.body as { delivery_id?: string } | undefined) ?? {};
|
|
56
|
-
return { handled: true, eventId: body.delivery_id ?? "" };
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe("WebhookTrigger — v0.7 PR 4 integration (real HTTP)", () => {
|
|
61
|
-
let app: Hono;
|
|
62
|
-
let trigger: InstanceType<typeof WebhookTriggerClass>;
|
|
63
|
-
let httpServer: Server | null = null;
|
|
64
|
-
|
|
65
|
-
beforeEach(() => {
|
|
66
|
-
WorkflowRegistry.resetInstance();
|
|
67
|
-
_setActiveWebhookTrigger(null);
|
|
68
|
-
process.env.GH_SECRET = SECRET;
|
|
69
|
-
app = new Hono();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
afterEach(
|
|
73
|
-
() =>
|
|
74
|
-
new Promise<void>((resolve) => {
|
|
75
|
-
if (trigger) void trigger.stop();
|
|
76
|
-
if (httpServer) {
|
|
77
|
-
httpServer.close(() => {
|
|
78
|
-
httpServer = null;
|
|
79
|
-
WorkflowRegistry.resetInstance();
|
|
80
|
-
_setActiveWebhookTrigger(null);
|
|
81
|
-
process.env.GH_SECRET = undefined;
|
|
82
|
-
resolve();
|
|
83
|
-
});
|
|
84
|
-
} else {
|
|
85
|
-
WorkflowRegistry.resetInstance();
|
|
86
|
-
_setActiveWebhookTrigger(null);
|
|
87
|
-
process.env.GH_SECRET = undefined;
|
|
88
|
-
resolve();
|
|
89
|
-
}
|
|
90
|
-
}),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
it("verifies a signed GitHub-style POST, runs the workflow, and dedups replays", async () => {
|
|
94
|
-
const nodes = new NodeMap();
|
|
95
|
-
nodes.addNode("handle-event", handleNode);
|
|
96
|
-
|
|
97
|
-
WorkflowRegistry.getInstance().register({
|
|
98
|
-
name: "gh-events",
|
|
99
|
-
source: "/test/gh.json",
|
|
100
|
-
workflow: {
|
|
101
|
-
name: "gh-events",
|
|
102
|
-
version: "1.0.0",
|
|
103
|
-
trigger: {
|
|
104
|
-
webhook: {
|
|
105
|
-
provider: "github",
|
|
106
|
-
path: "/webhooks/github",
|
|
107
|
-
secretEnv: "GH_SECRET",
|
|
108
|
-
idempotencyKey: "js/ctx.request.headers['x-github-delivery']",
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
steps: [{ id: "handle", node: "handle-event", type: "module", inputs: {} }],
|
|
112
|
-
nodes: { handle: { inputs: {} } },
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
trigger = new WebhookTriggerClass(app);
|
|
117
|
-
trigger.setNodeMap({ nodes });
|
|
118
|
-
await trigger.listen();
|
|
119
|
-
|
|
120
|
-
await new Promise<void>((resolve) => {
|
|
121
|
-
httpServer = serve({ fetch: app.fetch, port: TEST_PORT }, () => resolve()) as Server;
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const body = JSON.stringify({ ref: "refs/heads/main", delivery_id: "delivery-uuid-9" });
|
|
125
|
-
const sig = `sha256=${hmacHex(body)}`;
|
|
126
|
-
const reqInit = {
|
|
127
|
-
method: "POST",
|
|
128
|
-
headers: {
|
|
129
|
-
"content-type": "application/json",
|
|
130
|
-
"x-hub-signature-256": sig,
|
|
131
|
-
"x-github-event": "push",
|
|
132
|
-
"x-github-delivery": "delivery-uuid-9",
|
|
133
|
-
},
|
|
134
|
-
body,
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// First delivery — should run the workflow.
|
|
138
|
-
const first = await fetch(`http://localhost:${TEST_PORT}/webhooks/github`, reqInit);
|
|
139
|
-
expect(first.status).toBe(200);
|
|
140
|
-
const firstJson = (await first.json()) as { status?: string; eventId?: string };
|
|
141
|
-
expect(firstJson.status).toBe("ok");
|
|
142
|
-
expect(firstJson.eventId).toBe("delivery-uuid-9");
|
|
143
|
-
|
|
144
|
-
// Second delivery with the same delivery id — replay cache should
|
|
145
|
-
// short-circuit with `duplicate` and NOT run the workflow.
|
|
146
|
-
const second = await fetch(`http://localhost:${TEST_PORT}/webhooks/github`, reqInit);
|
|
147
|
-
expect(second.status).toBe(200);
|
|
148
|
-
const secondJson = (await second.json()) as { status?: string; eventId?: string };
|
|
149
|
-
expect(secondJson.status).toBe("duplicate");
|
|
150
|
-
expect(secondJson.eventId).toBe("delivery-uuid-9");
|
|
151
|
-
}, 15_000);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Drain the per-process RunTracker after the suite to keep singletons
|
|
155
|
-
// from leaking into other tests in the same project.
|
|
156
|
-
afterEach(() => {
|
|
157
|
-
try {
|
|
158
|
-
RunTracker.resetInstance();
|
|
159
|
-
} catch {
|
|
160
|
-
/* ignore — older test orderings */
|
|
161
|
-
}
|
|
162
|
-
});
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebhookTrigger — v0.7 PR 4 — unit tests for the public surface.
|
|
3
|
-
*
|
|
4
|
-
* Covers construction, the pre-catch-all hook coordination contract
|
|
5
|
-
* with HttpTrigger, route discovery via WorkflowRegistry, idempotent
|
|
6
|
-
* listen(), and the singleton helper accessor. End-to-end coverage
|
|
7
|
-
* (real HTTP POST with a signed Stripe-shaped payload + replay
|
|
8
|
-
* dedup) lives in `WebhookTrigger.integration.test.ts`.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { createHmac } from "node:crypto";
|
|
12
|
-
import { WorkflowRegistry } from "@blokjs/runner";
|
|
13
|
-
import { Hono } from "hono";
|
|
14
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
15
|
-
|
|
16
|
-
vi.mock("@opentelemetry/api", () => ({
|
|
17
|
-
trace: {
|
|
18
|
-
getTracer: () => ({
|
|
19
|
-
startActiveSpan: (_name: string, fn: (span: unknown) => unknown) =>
|
|
20
|
-
fn({ setAttribute: vi.fn(), setStatus: vi.fn(), recordException: vi.fn(), end: vi.fn() }),
|
|
21
|
-
}),
|
|
22
|
-
},
|
|
23
|
-
metrics: {
|
|
24
|
-
getMeter: () => ({
|
|
25
|
-
createCounter: () => ({ add: vi.fn() }),
|
|
26
|
-
createHistogram: () => ({ record: vi.fn() }),
|
|
27
|
-
createGauge: () => ({ record: vi.fn() }),
|
|
28
|
-
createObservableGauge: () => ({ addCallback: vi.fn() }),
|
|
29
|
-
}),
|
|
30
|
-
},
|
|
31
|
-
SpanStatusCode: { OK: 0, ERROR: 1 },
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
import WebhookTrigger, { _getActiveWebhookTrigger, _setActiveWebhookTrigger } from "./WebhookTrigger";
|
|
35
|
-
|
|
36
|
-
const SECRET = "shhh-its-a-secret-1234567890";
|
|
37
|
-
|
|
38
|
-
function hmacHex(data: string, secret = SECRET): string {
|
|
39
|
-
return createHmac("sha256", secret).update(data).digest("hex");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
describe("WebhookTrigger — v0.7 PR 4", () => {
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
WorkflowRegistry.resetInstance();
|
|
45
|
-
_setActiveWebhookTrigger(null);
|
|
46
|
-
process.env.GH_SECRET = SECRET;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
afterEach(() => {
|
|
50
|
-
_setActiveWebhookTrigger(null);
|
|
51
|
-
process.env.GH_SECRET = undefined;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe("constructor()", () => {
|
|
55
|
-
it("registers as the active webhook trigger singleton", () => {
|
|
56
|
-
const app = new Hono();
|
|
57
|
-
const trigger = new WebhookTrigger(app);
|
|
58
|
-
expect(trigger).toBeDefined();
|
|
59
|
-
expect(_getActiveWebhookTrigger()).toBe(trigger);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("accepts an optional httpTrigger for pre-catch-all coordination", () => {
|
|
63
|
-
const app = new Hono();
|
|
64
|
-
const addPreCatchAllHook = vi.fn();
|
|
65
|
-
const httpTrigger = { addPreCatchAllHook };
|
|
66
|
-
const trigger = new WebhookTrigger(app, httpTrigger);
|
|
67
|
-
expect(trigger).toBeDefined();
|
|
68
|
-
expect(addPreCatchAllHook).not.toHaveBeenCalled();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe("listen()", () => {
|
|
73
|
-
it("registers a POST route per webhook workflow in the registry", async () => {
|
|
74
|
-
const app = new Hono();
|
|
75
|
-
WorkflowRegistry.getInstance().register({
|
|
76
|
-
name: "gh-events",
|
|
77
|
-
source: "/test/gh.json",
|
|
78
|
-
workflow: {
|
|
79
|
-
name: "gh-events",
|
|
80
|
-
version: "1.0.0",
|
|
81
|
-
trigger: { webhook: { provider: "github", path: "/webhooks/github", secretEnv: "GH_SECRET" } },
|
|
82
|
-
steps: [],
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const trigger = new WebhookTrigger(app);
|
|
87
|
-
await trigger.listen();
|
|
88
|
-
|
|
89
|
-
// Send a valid signed POST — should at least not 404.
|
|
90
|
-
const body = JSON.stringify({ ref: "refs/heads/main" });
|
|
91
|
-
const res = await app.fetch(
|
|
92
|
-
new Request("http://localhost/webhooks/github", {
|
|
93
|
-
method: "POST",
|
|
94
|
-
headers: {
|
|
95
|
-
"content-type": "application/json",
|
|
96
|
-
"x-hub-signature-256": `sha256=${hmacHex(body)}`,
|
|
97
|
-
"x-github-event": "push",
|
|
98
|
-
"x-github-delivery": "delivery-1",
|
|
99
|
-
},
|
|
100
|
-
body,
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
expect(res.status).not.toBe(404);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("skips workflows without trigger.webhook config", async () => {
|
|
107
|
-
const app = new Hono();
|
|
108
|
-
WorkflowRegistry.getInstance().register({
|
|
109
|
-
name: "http-only",
|
|
110
|
-
source: "/test/http.json",
|
|
111
|
-
workflow: {
|
|
112
|
-
name: "http-only",
|
|
113
|
-
version: "1.0.0",
|
|
114
|
-
trigger: { http: { method: "POST", path: "/api/foo" } },
|
|
115
|
-
steps: [],
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
const trigger = new WebhookTrigger(app);
|
|
119
|
-
await trigger.listen();
|
|
120
|
-
// No webhook route mounted — anything not on app returns 404.
|
|
121
|
-
const res = await app.fetch(new Request("http://localhost/webhooks/anywhere", { method: "POST" }));
|
|
122
|
-
expect(res.status).toBe(404);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("skips workflows with neither `provider` nor `signature`", async () => {
|
|
126
|
-
const app = new Hono();
|
|
127
|
-
WorkflowRegistry.getInstance().register({
|
|
128
|
-
name: "misconfigured",
|
|
129
|
-
source: "/test/bad.json",
|
|
130
|
-
workflow: {
|
|
131
|
-
name: "misconfigured",
|
|
132
|
-
version: "1.0.0",
|
|
133
|
-
trigger: { webhook: { path: "/webhooks/bad" } },
|
|
134
|
-
steps: [],
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
const trigger = new WebhookTrigger(app);
|
|
138
|
-
await trigger.listen();
|
|
139
|
-
expect(trigger.getStats().workflowsRegistered).toBe(0);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("registers a pre-catch-all hook on httpTrigger when provided", async () => {
|
|
143
|
-
const app = new Hono();
|
|
144
|
-
const addPreCatchAllHook = vi.fn();
|
|
145
|
-
const httpTrigger = { addPreCatchAllHook };
|
|
146
|
-
WorkflowRegistry.getInstance().register({
|
|
147
|
-
name: "gh-events",
|
|
148
|
-
source: "/test/gh.json",
|
|
149
|
-
workflow: {
|
|
150
|
-
name: "gh-events",
|
|
151
|
-
version: "1.0.0",
|
|
152
|
-
trigger: { webhook: { provider: "github", path: "/webhooks/github", secretEnv: "GH_SECRET" } },
|
|
153
|
-
steps: [],
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
const trigger = new WebhookTrigger(app, httpTrigger);
|
|
157
|
-
await trigger.listen();
|
|
158
|
-
expect(addPreCatchAllHook).toHaveBeenCalledTimes(1);
|
|
159
|
-
expect(addPreCatchAllHook).toHaveBeenCalledWith(expect.any(Function));
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("is idempotent — second listen() call is a no-op", async () => {
|
|
163
|
-
const app = new Hono();
|
|
164
|
-
const trigger = new WebhookTrigger(app);
|
|
165
|
-
await trigger.listen();
|
|
166
|
-
await expect(trigger.listen()).resolves.toBeTypeOf("number");
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe("request handling", () => {
|
|
171
|
-
it("returns 401 with structured reason when the signature is invalid", async () => {
|
|
172
|
-
const app = new Hono();
|
|
173
|
-
WorkflowRegistry.getInstance().register({
|
|
174
|
-
name: "gh-events",
|
|
175
|
-
source: "/test/gh.json",
|
|
176
|
-
workflow: {
|
|
177
|
-
name: "gh-events",
|
|
178
|
-
version: "1.0.0",
|
|
179
|
-
trigger: { webhook: { provider: "github", path: "/webhooks/github", secretEnv: "GH_SECRET" } },
|
|
180
|
-
steps: [],
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
const trigger = new WebhookTrigger(app);
|
|
184
|
-
await trigger.listen();
|
|
185
|
-
|
|
186
|
-
const res = await app.fetch(
|
|
187
|
-
new Request("http://localhost/webhooks/github", {
|
|
188
|
-
method: "POST",
|
|
189
|
-
headers: {
|
|
190
|
-
"content-type": "application/json",
|
|
191
|
-
"x-hub-signature-256": "sha256=deadbeef",
|
|
192
|
-
"x-github-event": "push",
|
|
193
|
-
},
|
|
194
|
-
body: JSON.stringify({}),
|
|
195
|
-
}),
|
|
196
|
-
);
|
|
197
|
-
expect(res.status).toBe(401);
|
|
198
|
-
const json = (await res.json()) as { reason?: string };
|
|
199
|
-
expect(json.reason).toBe("signature_mismatch");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("returns 200 `ignored` when the event isn't in the allowlist", async () => {
|
|
203
|
-
const app = new Hono();
|
|
204
|
-
WorkflowRegistry.getInstance().register({
|
|
205
|
-
name: "gh-events",
|
|
206
|
-
source: "/test/gh.json",
|
|
207
|
-
workflow: {
|
|
208
|
-
name: "gh-events",
|
|
209
|
-
version: "1.0.0",
|
|
210
|
-
trigger: {
|
|
211
|
-
webhook: {
|
|
212
|
-
provider: "github",
|
|
213
|
-
path: "/webhooks/github",
|
|
214
|
-
secretEnv: "GH_SECRET",
|
|
215
|
-
events: ["push"],
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
steps: [],
|
|
219
|
-
},
|
|
220
|
-
});
|
|
221
|
-
const trigger = new WebhookTrigger(app);
|
|
222
|
-
await trigger.listen();
|
|
223
|
-
|
|
224
|
-
const body = JSON.stringify({});
|
|
225
|
-
const res = await app.fetch(
|
|
226
|
-
new Request("http://localhost/webhooks/github", {
|
|
227
|
-
method: "POST",
|
|
228
|
-
headers: {
|
|
229
|
-
"content-type": "application/json",
|
|
230
|
-
"x-hub-signature-256": `sha256=${hmacHex(body)}`,
|
|
231
|
-
"x-github-event": "pull_request",
|
|
232
|
-
},
|
|
233
|
-
body,
|
|
234
|
-
}),
|
|
235
|
-
);
|
|
236
|
-
expect(res.status).toBe(200);
|
|
237
|
-
const json = (await res.json()) as { status?: string; reason?: string };
|
|
238
|
-
expect(json.status).toBe("ignored");
|
|
239
|
-
expect(json.reason).toBe("event_not_allowed");
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
describe("stop()", () => {
|
|
244
|
-
it("clears the singleton", async () => {
|
|
245
|
-
const app = new Hono();
|
|
246
|
-
const trigger = new WebhookTrigger(app);
|
|
247
|
-
await trigger.listen();
|
|
248
|
-
await trigger.stop();
|
|
249
|
-
expect(_getActiveWebhookTrigger()).toBeNull();
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
});
|