164 lines
5.1 KiB
TypeScript
164 lines
5.1 KiB
TypeScript
import {
|
|
BASE_URL,
|
|
createUser,
|
|
db,
|
|
insertDump,
|
|
insertPlaylist,
|
|
test,
|
|
} from "../helpers.ts";
|
|
import { assertEquals, assertFalse, assertStringIncludes } from "@std/assert";
|
|
import { Application } from "@oak/oak";
|
|
import { ogMiddleware } from "../../middleware/og.ts";
|
|
|
|
async function handle(
|
|
app: Application,
|
|
path: string,
|
|
headers: HeadersInit = {},
|
|
) {
|
|
const res = await app.handle(new Request(`${BASE_URL}${path}`, { headers }));
|
|
if (!res) throw new Error("no response");
|
|
return { res, text: await res.text() };
|
|
}
|
|
|
|
function ogApp() {
|
|
const app = new Application();
|
|
app.use(ogMiddleware);
|
|
app.use((ctx) => {
|
|
ctx.response.body = "fallthrough";
|
|
});
|
|
return app;
|
|
}
|
|
|
|
test("ogMiddleware: dump pages get escaped OG tags", async () => {
|
|
const user = await createUser();
|
|
const dump = insertDump({
|
|
userId: user.id,
|
|
title: `Tom & "Jerry" <script>`,
|
|
comment: "A comment",
|
|
richContent: {
|
|
type: "generic",
|
|
url: "x",
|
|
thumbnailUrl: "https://img.test/a.jpg",
|
|
},
|
|
});
|
|
const { res, text } = await handle(ogApp(), dump.path);
|
|
assertEquals(res.headers.get("content-type"), "text/html; charset=utf-8");
|
|
assertStringIncludes(
|
|
text,
|
|
`<meta property="og:title" content="Tom & "Jerry" <script>" />`,
|
|
);
|
|
assertStringIncludes(
|
|
text,
|
|
`<title>Tom & "Jerry" <script></title>`,
|
|
);
|
|
assertStringIncludes(
|
|
text,
|
|
`<meta property="og:description" content="A comment" />`,
|
|
);
|
|
assertStringIncludes(
|
|
text,
|
|
`<meta property="og:image" content="https://img.test/a.jpg" />`,
|
|
);
|
|
assertStringIncludes(text, `content="summary_large_image"`);
|
|
assertStringIncludes(
|
|
text,
|
|
`<meta property="og:url" content="${BASE_URL}${dump.path}" />`,
|
|
);
|
|
// The index's own title is replaced, not duplicated.
|
|
assertEquals(text.match(/<title>/g)?.length, 1);
|
|
assertFalse(text.includes("__SITE_NAME__"));
|
|
});
|
|
|
|
test("ogMiddleware: no artwork falls back to the site icon as a small card", async () => {
|
|
const user = await createUser();
|
|
const dump = insertDump({ userId: user.id, title: "Plain" });
|
|
const { text } = await handle(ogApp(), dump.path);
|
|
assertStringIncludes(text, `content="${BASE_URL}/apple-touch-icon.png"`);
|
|
assertStringIncludes(text, `<meta name="twitter:card" content="summary" />`);
|
|
const file = insertDump({
|
|
userId: user.id,
|
|
fileName: "a.png",
|
|
fileMime: "image/png",
|
|
});
|
|
assertStringIncludes(
|
|
(await handle(ogApp(), file.path)).text,
|
|
`content="${BASE_URL}/api/dumps/${file.id}/file"`,
|
|
);
|
|
});
|
|
|
|
test("ogMiddleware: users and public playlists; private things fall through", async () => {
|
|
const user = await createUser();
|
|
const app = ogApp();
|
|
assertStringIncludes(
|
|
(await handle(app, `/@${user.username}/upvoted`)).text,
|
|
`<meta property="og:title" content="${user.username}" />`,
|
|
);
|
|
assertEquals(
|
|
(await handle(app, `/@${user.username}/nope`)).text,
|
|
"fallthrough",
|
|
);
|
|
const pub = insertPlaylist({ userId: user.id, title: "Open list" });
|
|
assertStringIncludes(
|
|
(await handle(app, pub.path)).text,
|
|
"Open list",
|
|
);
|
|
const priv = insertPlaylist({
|
|
userId: user.id,
|
|
title: "Closed",
|
|
isPublic: false,
|
|
});
|
|
assertEquals(
|
|
(await handle(app, priv.path)).text,
|
|
"fallthrough",
|
|
);
|
|
const secret = insertDump({
|
|
userId: user.id,
|
|
title: "Secret",
|
|
isPrivate: true,
|
|
});
|
|
assertEquals((await handle(app, secret.path)).text, "fallthrough");
|
|
assertEquals(
|
|
(await handle(app, `/@${user.username}/dumps/nope`)).text,
|
|
"fallthrough",
|
|
);
|
|
// v3 paths aren't redirected.
|
|
assertEquals((await handle(app, `/dumps/${secret.id}`)).text, "fallthrough");
|
|
assertEquals(
|
|
(await handle(app, `/api/dumps/${secret.id}`)).text,
|
|
"fallthrough",
|
|
);
|
|
assertEquals((await handle(app, `/favicon.ico`)).text, "fallthrough");
|
|
});
|
|
|
|
test("ogMiddleware: a former slug, a former name or another case gets a 301", async () => {
|
|
const user = await createUser();
|
|
const dump = insertDump({ userId: user.id, title: "Now" });
|
|
db.prepare(
|
|
`INSERT INTO dump_slug_aliases (user_id, slug, dump_id, created_at) VALUES (?, 'before', ?, ?);`,
|
|
).run(user.id, dump.id, new Date().toISOString());
|
|
db.prepare(
|
|
`INSERT INTO user_aliases (username, user_id, created_at) VALUES ('old_name', ?, ?);`,
|
|
).run(user.id, new Date().toISOString());
|
|
for (
|
|
const [from, to] of [
|
|
[`/@${user.username}/dumps/before?x=1`, `${dump.path}?x=1`],
|
|
[`/@OLD_NAME/dumps/now`, dump.path],
|
|
[`/@old_name/follow/playlists`, `/@${user.username}/follow/playlists`],
|
|
[`/@${user.username.toUpperCase()}`, `/@${user.username}`],
|
|
]
|
|
) {
|
|
const { res } = await handle(ogApp(), from);
|
|
assertEquals(res.status, 301, from);
|
|
assertEquals(res.headers.get("location"), `${BASE_URL}${to}`, from);
|
|
}
|
|
// A private dump's former slug tells nothing.
|
|
const secret = insertDump({ userId: user.id, title: "S", isPrivate: true });
|
|
db.prepare(
|
|
`INSERT INTO dump_slug_aliases (user_id, slug, dump_id, created_at) VALUES (?, 'hush', ?, ?);`,
|
|
).run(user.id, secret.id, new Date().toISOString());
|
|
assertEquals(
|
|
(await handle(ogApp(), `/@${user.username}/dumps/hush`)).text,
|
|
"fallthrough",
|
|
);
|
|
});
|