|
1 | | -import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test'; |
2 | | -import { describe, it, expect } from 'vitest'; |
3 | | -import crypto from 'crypto'; |
4 | | -import worker from '../src/_index'; |
5 | | -import { TEST_SECRET_KEY } from './constants'; |
| 1 | +import { env, createExecutionContext, waitOnExecutionContext, SELF } from "cloudflare:test"; |
| 2 | +import { describe, it, expect } from "vitest"; |
| 3 | +import crypto from "crypto"; |
| 4 | +import worker from "../src/_index"; |
| 5 | +import { TEST_SECRET_KEY } from "./constants"; |
6 | 6 |
|
7 | 7 | const IncomingRequest = Request<unknown, IncomingRequestCfProperties>; |
8 | 8 |
|
9 | | -const timestamp = '1710000000'; |
10 | | -const payload = { id: 'evt_123', status: 'paid' }; |
| 9 | +const timestamp = "1710000000"; |
| 10 | +const payload = { id: "evt_123", status: "paid" }; |
11 | 11 | const rawBody = JSON.stringify(payload); |
12 | 12 |
|
13 | 13 | function signatureFor(secret: string, body = rawBody) { |
14 | | - const digest = crypto.createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex'); |
| 14 | + const digest = crypto.createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex"); |
15 | 15 | return `sha256=${digest}`; |
16 | 16 | } |
17 | 17 |
|
18 | 18 | function signedRequest(body = rawBody, signature = signatureFor(TEST_SECRET_KEY, body)) { |
19 | | - return new IncomingRequest('http://example.com/webhook', { |
20 | | - method: 'POST', |
| 19 | + return new IncomingRequest("http://example.com/webhook", { |
| 20 | + method: "POST", |
21 | 21 | headers: { |
22 | | - 'content-type': 'application/json', |
23 | | - 'x-zsend-event': 'payment.succeeded', |
24 | | - 'x-zsend-signature': signature, |
25 | | - 'x-zsend-timestamp': timestamp, |
| 22 | + "content-type": "application/json", |
| 23 | + "x-zsend-event": "payment.succeeded", |
| 24 | + "x-zsend-signature": signature, |
| 25 | + "x-zsend-timestamp": timestamp, |
26 | 26 | }, |
27 | 27 | body, |
28 | 28 | }); |
29 | 29 | } |
30 | 30 |
|
31 | | -describe('ZSend webhook worker', () => { |
32 | | - it('rejects non-POST requests', async () => { |
33 | | - const request = new IncomingRequest('http://example.com/webhook'); |
| 31 | +describe("ZSend webhook worker", () => { |
| 32 | + it("rejects non-POST requests", async () => { |
| 33 | + const request = new IncomingRequest("http://example.com/webhook"); |
34 | 34 | const ctx = createExecutionContext(); |
35 | 35 | const response = await worker.fetch(request, env, ctx); |
36 | 36 | await waitOnExecutionContext(ctx); |
37 | 37 |
|
38 | 38 | expect(response.status).toBe(405); |
39 | | - expect(await response.text()).toBe('Method Not Allowed'); |
| 39 | + expect(await response.text()).toBe("Method Not Allowed"); |
40 | 40 | }); |
41 | 41 |
|
42 | | - it('rejects requests when SECRET_KEY is not configured', async () => { |
| 42 | + it("rejects requests when SECRET_KEY is not configured", async () => { |
43 | 43 | const request = signedRequest(); |
44 | 44 | const ctx = createExecutionContext(); |
45 | | - const response = await worker.fetch(request, { ...env, SECRET_KEY: '' }, ctx); |
| 45 | + const response = await worker.fetch(request, { ...env, SECRET_KEY: "" }, ctx); |
46 | 46 | await waitOnExecutionContext(ctx); |
47 | 47 |
|
48 | 48 | expect(response.status).toBe(500); |
49 | | - expect(await response.text()).toBe('Server configuration error: SECRET_KEY is not set'); |
| 49 | + expect(await response.text()).toBe("Server configuration error: SECRET_KEY is not set"); |
50 | 50 | }); |
51 | 51 |
|
52 | | - it('rejects requests missing signature headers', async () => { |
53 | | - const request = new IncomingRequest('http://example.com/webhook', { |
54 | | - method: 'POST', |
55 | | - headers: { 'content-type': 'application/json' }, |
| 52 | + it("rejects requests missing signature headers", async () => { |
| 53 | + const request = new IncomingRequest("http://example.com/webhook", { |
| 54 | + method: "POST", |
| 55 | + headers: { "content-type": "application/json" }, |
56 | 56 | body: rawBody, |
57 | 57 | }); |
58 | 58 | const ctx = createExecutionContext(); |
59 | 59 | const response = await worker.fetch(request, env, ctx); |
60 | 60 | await waitOnExecutionContext(ctx); |
61 | 61 |
|
62 | 62 | expect(response.status).toBe(400); |
63 | | - expect(await response.text()).toBe('Missing signature or timestamp'); |
| 63 | + expect(await response.text()).toBe("Missing signature or timestamp"); |
64 | 64 | }); |
65 | 65 |
|
66 | | - it('rejects invalid JSON bodies', async () => { |
67 | | - const request = signedRequest('{bad json}', signatureFor(TEST_SECRET_KEY, '{bad json}')); |
| 66 | + it("rejects invalid JSON bodies", async () => { |
| 67 | + const request = signedRequest("{bad json}", signatureFor(TEST_SECRET_KEY, "{bad json}")); |
68 | 68 | const ctx = createExecutionContext(); |
69 | 69 | const response = await worker.fetch(request, env, ctx); |
70 | 70 | await waitOnExecutionContext(ctx); |
71 | 71 |
|
72 | 72 | expect(response.status).toBe(400); |
73 | | - expect(await response.text()).toBe('Invalid JSON'); |
| 73 | + expect(await response.text()).toBe("Invalid JSON"); |
74 | 74 | }); |
75 | 75 |
|
76 | | - it('rejects requests with invalid signatures', async () => { |
77 | | - const request = signedRequest(rawBody, 'invalid-signature'); |
| 76 | + it("rejects requests with invalid signatures", async () => { |
| 77 | + const request = signedRequest(rawBody, "invalid-signature"); |
78 | 78 | const ctx = createExecutionContext(); |
79 | 79 | const response = await worker.fetch(request, env, ctx); |
80 | 80 | await waitOnExecutionContext(ctx); |
81 | 81 |
|
82 | 82 | expect(response.status).toBe(401); |
83 | | - expect(await response.text()).toBe('Invalid signature'); |
| 83 | + expect(await response.text()).toBe("Invalid signature"); |
84 | 84 | }); |
85 | 85 |
|
86 | | - it('accepts valid signed webhook requests in unit style', async () => { |
| 86 | + it("accepts valid signed webhook requests in unit style", async () => { |
87 | 87 | const request = signedRequest(); |
88 | 88 | const ctx = createExecutionContext(); |
89 | 89 | const response = await worker.fetch(request, env, ctx); |
90 | 90 | await waitOnExecutionContext(ctx); |
91 | 91 |
|
92 | 92 | expect(response.status).toBe(200); |
93 | | - expect(await response.text()).toBe('OK!'); |
| 93 | + expect(await response.text()).toBe("OK!"); |
94 | 94 | }); |
95 | 95 |
|
96 | | - it('accepts valid signed webhook requests in integration style', async () => { |
97 | | - const response = await SELF.fetch('https://example.com/webhook', { |
98 | | - method: 'POST', |
| 96 | + it("accepts valid signed webhook requests in integration style", async () => { |
| 97 | + const response = await SELF.fetch("https://example.com/webhook", { |
| 98 | + method: "POST", |
99 | 99 | headers: signedRequest().headers, |
100 | 100 | body: rawBody, |
101 | 101 | }); |
102 | 102 |
|
103 | 103 | expect(response.status).toBe(200); |
104 | | - expect(await response.text()).toBe('OK!'); |
| 104 | + expect(await response.text()).toBe("OK!"); |
105 | 105 | }); |
106 | 106 | }); |
0 commit comments