Skip to content

Commit

Permalink
chore: add e2e tests for POST /api/items, `DELETE /api/items/:id/vo…
Browse files Browse the repository at this point in the history
…te` and `POST /api/items/:id/vote` (denoland#565)

Adds coverage on behalf of denoland#524
  • Loading branch information
revgum authored Sep 11, 2023
1 parent 3a193e1 commit 3cd8357
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 17 deletions.
187 changes: 177 additions & 10 deletions e2e_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ import {
type Item,
kv,
listCommentsByItem,
listItemsByUser,
type Notification,
Vote,
} from "@/utils/db.ts";
import {
genNewComment,
genNewItem,
genNewNotification,
genNewUser,
genNewVote,
} from "@/utils/db_test.ts";
import { stripe } from "@/utils/stripe.ts";
import {
Expand Down Expand Up @@ -249,6 +252,80 @@ Deno.test("[e2e] GET /api/items", async () => {
]);
});

Deno.test("[e2e] POST /api/items", async (test) => {
const url = "http://localhost/api/items";
const user = genNewUser();
await createUser(user);

await test.step("returns HTTP 401 Unauthorized response if the session user is not signed in", async () => {
const resp = await handler(new Request(url, { method: "POST" }));
assertFalse(resp.ok);
assertEquals(await resp.text(), "User must be signed in");
assertEquals(resp.status, Status.Unauthorized);
});

await test.step("returns HTTP 400 Bad Request response if item is missing title", async () => {
const body = new FormData();
const resp = await handler(
new Request(url, {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
body,
}),
);

assertEquals(await resp.text(), "Title is missing");
assertEquals(resp.status, Status.BadRequest);
});

await test.step("returns HTTP 400 Bad Request response if item has an invalid or missing url", async () => {
const body = new FormData();
body.set("title", "Title text");
const resp1 = await handler(
new Request(url, {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
body,
}),
);

assertEquals(await resp1.text(), "URL is invalid or missing");
assertEquals(resp1.status, Status.BadRequest);

body.set("url", "invalid-url");
const resp2 = await handler(
new Request(url, {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
body,
}),
);

assertEquals(await resp2.text(), "URL is invalid or missing");
assertEquals(resp2.status, Status.BadRequest);
});

await test.step("creates an item and redirects to the item page", async () => {
const item = { title: "Title text", url: "http://bobross.com" };
const body = new FormData();
body.set("title", item.title);
body.set("url", item.url);
const resp = await handler(
new Request(url, {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
body,
}),
);
const items = await collectValues(listItemsByUser(user.login));

assertEquals(resp.status, Status.SeeOther);
assertEquals(resp.headers.get("location"), `/items/${items[0].id}`);
// Deep partial match since the item ID is a ULID generated at runtime
assertObjectMatch(items[0], item);
});
});

Deno.test("[e2e] GET /api/items/[id]", async () => {
const item = genNewItem();
const req = new Request("http://localhost/api/items/" + item.id);
Expand Down Expand Up @@ -285,16 +362,6 @@ Deno.test("[e2e] GET /api/items/[id]/comments", async () => {
assertEquals(values, [JSON.parse(JSON.stringify(comment))]);
});

Deno.test("[e2e] POST /api/items", async () => {
const resp = await handler(
new Request("http://localhost/api/items", { method: "POST" }),
);

assertFalse(resp.ok);
assertEquals(await resp.text(), "User must be signed in");
assertEquals(resp.status, Status.Unauthorized);
});

Deno.test("[e2e] GET /api/users", async () => {
const user1 = genNewUser();
const user2 = genNewUser();
Expand Down Expand Up @@ -371,6 +438,106 @@ Deno.test("[e2e] GET /api/users/[login]/notifications", async () => {
]);
});

Deno.test("[e2e] DELETE /api/items/[id]/vote", async (test) => {
const item = genNewItem();
const user = genNewUser();
await createItem(item);
await createUser(user);
const vote: Vote = {
...genNewVote(),
itemId: item.id,
userLogin: user.login,
createdAt: new Date(),
};
await createVote(vote);
const url = `http://localhost/api/items/${item.id}/vote`;

await test.step("returns HTTP 401 Unauthorized response if the session user is not signed in", async () => {
const resp = await handler(new Request(url, { method: "DELETE" }));
assertFalse(resp.ok);
assertEquals(await resp.text(), "User must be signed in");
assertEquals(resp.status, Status.Unauthorized);
});

await test.step("returns HTTP 404 Not Found response if the item is not found", async () => {
const resp = await handler(
new Request("http://localhost/api/items/bob-ross/vote", {
method: "DELETE",
headers: { cookie: "site-session=" + user.sessionId },
}),
);
assertFalse(resp.ok);
assertEquals(await resp.text(), "Item not found");
assertEquals(resp.status, Status.NotFound);
});

await test.step("returns HTTP 204 No Content when it deletes a vote", async () => {
const resp = await handler(
new Request(url, {
method: "DELETE",
headers: { cookie: "site-session=" + user.sessionId },
}),
);
assert(resp.ok);
assertEquals(resp.body, null);
assertEquals(resp.status, Status.NoContent);
});
});

Deno.test("[e2e] POST /api/items/[id]/vote", async (test) => {
const item = genNewItem();
const user = genNewUser();
await createItem(item);
await createUser(user);
const url = `http://localhost/api/items/${item.id}/vote`;

await test.step("returns HTTP 401 Unauthorized response if the session user is not signed in", async () => {
const resp = await handler(new Request(url, { method: "POST" }));
assertFalse(resp.ok);
assertEquals(await resp.text(), "User must be signed in");
assertEquals(resp.status, Status.Unauthorized);
});

await test.step("returns HTTP 404 Not Found response if the item is not found", async () => {
const resp = await handler(
new Request("http://localhost/api/items/bob-ross/vote", {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
}),
);
assertFalse(resp.ok);
assertEquals(await resp.text(), "Item not found");
assertEquals(resp.status, Status.NotFound);
});

await test.step("creates a vote but not a new notification if for one's own item", async () => {
const item = { ...genNewItem(), userLogin: user.login };
await createItem(item);
const url = `http://localhost/api/items/${item.id}/vote`;
const resp = await handler(
new Request(url, {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
}),
);

assertEquals(resp.status, Status.Created);
assertEquals(await ifUserHasNotifications(user.login), false);
});

await test.step("creates a vote and notification if for someone elses item", async () => {
const resp = await handler(
new Request(url, {
method: "POST",
headers: { cookie: "site-session=" + user.sessionId },
}),
);
assertEquals(resp.status, Status.Created);
assertEquals(await ifUserHasNotifications(item.userLogin), true);
assertEquals(await ifUserHasNotifications(user.login), false);
});
});

Deno.test("[e2e] POST /api/stripe-webhooks", async (test) => {
const url = "http://localhost/api/stripe-webhooks";

Expand Down
23 changes: 16 additions & 7 deletions utils/db_test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import {
assertArrayIncludes,
assertEquals,
assertRejects,
} from "std/assert/mod.ts";
import { DAY } from "std/datetime/constants.ts";
import { ulid } from "std/ulid/mod.ts";
import {
collectValues,
type Comment,
Expand Down Expand Up @@ -32,14 +39,8 @@ import {
Notification,
updateUser,
type User,
Vote,
} from "./db.ts";
import {
assertArrayIncludes,
assertEquals,
assertRejects,
} from "std/assert/mod.ts";
import { DAY } from "std/datetime/constants.ts";
import { ulid } from "std/ulid/mod.ts";

export function genNewComment(): Comment {
return {
Expand Down Expand Up @@ -79,6 +80,14 @@ export function genNewNotification(): Notification {
};
}

export function genNewVote(): Vote {
return {
itemId: crypto.randomUUID(),
userLogin: crypto.randomUUID(),
createdAt: new Date(),
};
}

Deno.test("[db] items", async () => {
const user = genNewUser();
const item1: Item = {
Expand Down

0 comments on commit 3cd8357

Please sign in to comment.