Skip to content

Commit a7e4c31

Browse files
authored
chore: add POST /api/stripe-webhooks tests (#581)
1 parent b4ba280 commit a7e4c31

File tree

3 files changed

+160
-3
lines changed

3 files changed

+160
-3
lines changed

deno.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"check:license": "deno run --allow-read --allow-write tasks/check_license.ts",
1313
"check:types": "deno check **/*.ts && deno check **/*.tsx",
1414
"ok": "deno fmt --check && deno lint && deno task check:license --check && deno task check:types && deno task test",
15-
"cov:gen": "deno coverage ./cov/ --lcov --exclude='test.ts' > cov.lcov",
15+
"cov:gen": "deno coverage ./cov/ --lcov --exclude='test.ts' --output=cov.lcov",
1616
"cov:view": "genhtml -o html_cov cov.lcov && open html_cov/index.html",
1717
"update": "deno run -A -r https://fresh.deno.dev/update .",
1818
"build": "deno run -A --unstable dev.ts build",

e2e_test.ts

+158-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import {
77
createItem,
88
createUser,
99
createVote,
10+
getUser,
1011
type Item,
1112
listItemsByUser,
13+
User,
1214
Vote,
1315
} from "@/utils/db.ts";
1416
import { genNewItem, genNewUser, genNewVote } from "@/utils/db_test.ts";
@@ -23,7 +25,12 @@ import {
2325
assertObjectMatch,
2426
assertStringIncludes,
2527
} from "std/assert/mod.ts";
26-
import { assertSpyCall, resolvesNext, stub } from "std/testing/mock.ts";
28+
import {
29+
assertSpyCall,
30+
resolvesNext,
31+
returnsNext,
32+
stub,
33+
} from "std/testing/mock.ts";
2734
import Stripe from "stripe";
2835
import options from "./fresh.config.ts";
2936

@@ -463,6 +470,27 @@ Deno.test("[e2e] POST /api/items/[id]/vote", async (test) => {
463470
});
464471
});
465472

473+
function createStripeEvent(
474+
type: Stripe.Event.Type,
475+
customer: string,
476+
): Promise<Stripe.Event> {
477+
return Promise.resolve({
478+
id: "123",
479+
object: "event",
480+
api_version: null,
481+
created: 0,
482+
livemode: false,
483+
pending_webhooks: 0,
484+
type,
485+
request: null,
486+
data: {
487+
object: {
488+
customer,
489+
},
490+
},
491+
});
492+
}
493+
466494
Deno.test("[e2e] POST /api/stripe-webhooks", async (test) => {
467495
const url = "http://localhost/api/stripe-webhooks";
468496

@@ -517,6 +545,135 @@ Deno.test("[e2e] POST /api/stripe-webhooks", async (test) => {
517545
);
518546
assertEquals(resp.status, Status.BadRequest);
519547
});
548+
549+
await test.step("returns HTTP 404 Not Found response if the user is not found for subscription creation", async () => {
550+
const constructEventAsyncStub = stub(
551+
stripe.webhooks,
552+
"constructEventAsync",
553+
returnsNext([
554+
createStripeEvent("customer.subscription.created", "x"),
555+
]),
556+
);
557+
558+
const resp = await handler(
559+
new Request(url, {
560+
method: "POST",
561+
headers: { "Stripe-Signature": crypto.randomUUID() },
562+
}),
563+
);
564+
565+
assertFalse(resp.ok);
566+
assertEquals(await resp.text(), "User not found");
567+
assertEquals(resp.status, Status.NotFound);
568+
569+
constructEventAsyncStub.restore();
570+
});
571+
572+
await test.step("returns HTTP 201 Created response if the subscription is created", async () => {
573+
const user = genNewUser();
574+
await createUser(user);
575+
576+
const constructEventAsyncStub = stub(
577+
stripe.webhooks,
578+
"constructEventAsync",
579+
returnsNext([
580+
createStripeEvent(
581+
"customer.subscription.created",
582+
user.stripeCustomerId!,
583+
),
584+
]),
585+
);
586+
587+
const resp = await handler(
588+
new Request(url, {
589+
method: "POST",
590+
headers: { "Stripe-Signature": crypto.randomUUID() },
591+
}),
592+
);
593+
594+
assertEquals(await getUser(user.login), { ...user, isSubscribed: true });
595+
assert(resp.ok);
596+
assertEquals(resp.body, null);
597+
assertEquals(resp.status, Status.Created);
598+
599+
constructEventAsyncStub.restore();
600+
});
601+
602+
await test.step("returns HTTP 404 Not Found response if the user is not found for subscription deletion", async () => {
603+
const constructEventAsyncStub = stub(
604+
stripe.webhooks,
605+
"constructEventAsync",
606+
returnsNext([
607+
createStripeEvent("customer.subscription.deleted", "x"),
608+
]),
609+
);
610+
611+
const resp = await handler(
612+
new Request(url, {
613+
method: "POST",
614+
headers: { "Stripe-Signature": crypto.randomUUID() },
615+
}),
616+
);
617+
618+
assertFalse(resp.ok);
619+
assertEquals(await resp.text(), "User not found");
620+
assertEquals(resp.status, Status.NotFound);
621+
622+
constructEventAsyncStub.restore();
623+
});
624+
625+
await test.step("returns HTTP 202 Accepted response if the subscription is deleted", async () => {
626+
const user: User = { ...genNewUser(), isSubscribed: true };
627+
await createUser(user);
628+
629+
const constructEventAsyncStub = stub(
630+
stripe.webhooks,
631+
"constructEventAsync",
632+
returnsNext([
633+
createStripeEvent(
634+
"customer.subscription.deleted",
635+
user.stripeCustomerId!,
636+
),
637+
]),
638+
);
639+
640+
const resp = await handler(
641+
new Request(url, {
642+
method: "POST",
643+
headers: { "Stripe-Signature": crypto.randomUUID() },
644+
}),
645+
);
646+
647+
assertEquals(await getUser(user.login), { ...user, isSubscribed: false });
648+
assert(resp.ok);
649+
assertEquals(resp.body, null);
650+
assertEquals(resp.status, Status.Accepted);
651+
652+
constructEventAsyncStub.restore();
653+
});
654+
655+
await test.step("returns HTTP 400 Bad Request response if the event type is not supported", async () => {
656+
const constructEventAsyncStub = stub(
657+
stripe.webhooks,
658+
"constructEventAsync",
659+
returnsNext([
660+
createStripeEvent("account.application.authorized", "x"),
661+
]),
662+
);
663+
664+
const resp = await handler(
665+
new Request(url, {
666+
method: "POST",
667+
headers: { "Stripe-Signature": crypto.randomUUID() },
668+
}),
669+
);
670+
671+
assertFalse(resp.ok);
672+
assertEquals(await resp.text(), "Event type not supported");
673+
assertEquals(resp.status, Status.BadRequest);
674+
675+
constructEventAsyncStub.restore();
676+
});
520677
});
521678

522679
Deno.test("[e2e] GET /account", async (test) => {

routes/api/stripe-webhooks.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const handler: Handlers = {
3333
);
3434
}
3535

36-
let event!: Stripe.Event;
36+
let event: Stripe.Event;
3737
try {
3838
event = await stripe.webhooks.constructEventAsync(
3939
body,

0 commit comments

Comments
 (0)