Files
outline/server/routes/api/urls/urls.test.ts
T
Tom Moor 77cee2806c chore: getJWTToken -> getSessionToken (#12371)
* getJWTToken -> getSessionToken

Ensure expiry is included in payload

* Refactor test harness to avoid direct usage of getSessionToken
2026-05-17 16:58:52 -04:00

425 lines
12 KiB
TypeScript

import type { Mock } from "vitest";
import { UnfurlResourceType } from "@shared/types";
import env from "@server/env";
import type { User } from "@server/models";
import {
buildCollection,
buildDocument,
buildShare,
buildUser,
} from "@server/test/factories";
import { getTestServer } from "@server/test/support";
import Iframely from "plugins/iframely/server/iframely";
const resolveCname = vi.hoisted(
() =>
(
input: string,
callback: (err: Error | null, addresses: string[]) => void
) => {
if (input.includes("valid.custom.domain")) {
callback(null, ["secure.outline.dev"]);
return;
}
callback(null, []);
}
);
vi.mock("node:dns", () => ({
default: {
resolveCname,
},
resolveCname,
}));
vi.mock("dns", () => ({
default: {
resolveCname,
},
resolveCname,
}));
vi.spyOn(Iframely, "requestResource").mockImplementation(() =>
Promise.resolve({})
);
const server = getTestServer();
describe("#urls.unfurl", () => {
let user: User;
beforeEach(async () => {
user = await buildUser();
});
it("should fail with status 400 bad request when url is invalid", async () => {
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: "/doc/foo-bar",
},
});
const body = await res.json();
expect(res.status).toEqual(400);
expect(body.message).toEqual("url: Invalid URL");
});
it("should fail with status 400 bad request when mention url is invalid", async () => {
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: "mention://1/foo/1",
},
});
const body = await res.json();
expect(res.status).toEqual(400);
expect(body.message).toEqual("url: Must be a valid url");
});
it("should fail with status 400 bad request when mention url is supplied without documentId", async () => {
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: "mention://2767ba0e-ac5c-4533-b9cf-4f5fc456600e/user/34095ac1-c808-45c0-8c6e-6c554497de64",
},
});
const body = await res.json();
expect(res.status).toEqual(400);
expect(body.message).toEqual("body: documentId required");
});
it("should fail with status 404 not found when mention user does not exist", async () => {
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: "mention://2767ba0e-ac5c-4533-b9cf-4f5fc456600e/user/34095ac1-c808-45c0-8c6e-6c554497de64",
documentId: "2767ba0e-ac5c-4533-b9cf-4f5fc456600e",
},
});
const body = await res.json();
expect(res.status).toEqual(404);
expect(body.message).toEqual("Mentioned user does not exist");
});
it("should fail with status 404 not found when document does not exist", async () => {
const mentionedUser = await buildUser({
teamId: user.teamId,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `mention://2767ba0e-ac5c-4533-b9cf-4f5fc456600e/user/${mentionedUser.id}`,
documentId: "2767ba0e-ac5c-4533-b9cf-4f5fc456600e",
},
});
const body = await res.json();
expect(res.status).toEqual(404);
expect(body.message).toEqual("Document does not exist");
});
it("should fail with status 403 forbidden when user is not authorized to read mentioned user info", async () => {
const mentionedUser = await buildUser();
const document = await buildDocument({
teamId: user.teamId,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `mention://2767ba0e-ac5c-4533-b9cf-4f5fc456600e/user/${mentionedUser.id}`,
documentId: document.id,
},
});
const body = await res.json();
expect(res.status).toEqual(403);
expect(body.message).toEqual("Authorization error");
});
it("should succeed with status 200 ok when valid mention url is supplied", async () => {
const mentionedUser = await buildUser({ teamId: user.teamId });
const document = await buildDocument({
teamId: user.teamId,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `mention://2767ba0e-ac5c-4533-b9cf-4f5fc456600e/user/${mentionedUser.id}`,
documentId: document.id,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.type).toEqual(UnfurlResourceType.Mention);
expect(body.name).toEqual(mentionedUser.name);
});
it("should return 204 when internal document url points to non-existent document", async () => {
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `${env.URL}/doc/non-existent-doc-abc123`,
},
});
expect(res.status).toEqual(204);
});
it("should succeed with status 200 ok when valid document url is supplied", async () => {
const document = await buildDocument({
teamId: user.teamId,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `${env.URL}/${document.url}`,
documentId: document.id,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.type).toEqual(UnfurlResourceType.Document);
expect(body.title).toEqual(document.titleWithDefault);
expect(body.id).toEqual(document.id);
});
it("should succeed with status 200 ok when valid share url is supplied", async () => {
const document = await buildDocument({
teamId: user.teamId,
});
const share = await buildShare({
teamId: user.teamId,
userId: user.id,
documentId: document.id,
published: true,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `${env.URL}/s/${share.id}/doc/${document.urlId}`,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.type).toEqual(UnfurlResourceType.Document);
expect(body.title).toEqual(document.titleWithDefault);
expect(body.id).toEqual(document.id);
});
it("should succeed with status 200 ok when share url with urlId is supplied", async () => {
const document = await buildDocument({
teamId: user.teamId,
});
const share = await buildShare({
teamId: user.teamId,
userId: user.id,
documentId: document.id,
urlId: "test-share",
published: true,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `${env.URL}/s/${share.urlId}/doc/${document.urlId}`,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.type).toEqual(UnfurlResourceType.Document);
expect(body.title).toEqual(document.titleWithDefault);
expect(body.id).toEqual(document.id);
});
it("should succeed with status 200 ok for share url without authentication", async () => {
const document = await buildDocument({
teamId: user.teamId,
});
const share = await buildShare({
teamId: user.teamId,
userId: user.id,
documentId: document.id,
published: true,
});
const res = await server.post("/api/urls.unfurl", {
body: {
url: `${env.URL}/s/${share.id}/doc/${document.urlId}`,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.type).toEqual(UnfurlResourceType.Document);
expect(body.title).toEqual(document.titleWithDefault);
expect(body.id).toEqual(document.id);
});
it("should return share-scoped url in unfurl response", async () => {
const document = await buildDocument({
teamId: user.teamId,
});
const share = await buildShare({
teamId: user.teamId,
userId: user.id,
documentId: document.id,
published: true,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `${env.URL}/s/${share.id}/doc/${document.urlId}`,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.url).toContain(`/s/${share.id}/doc/`);
});
it("should return 204 for collection share url without document", async () => {
const collection = await buildCollection({
teamId: user.teamId,
});
const share = await buildShare({
teamId: user.teamId,
userId: user.id,
collectionId: collection.id,
published: true,
});
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: `${env.URL}/s/${share.id}`,
},
});
expect(res.status).toEqual(204);
});
it("should succeed with status 200 ok for a valid external url", async () => {
(Iframely.requestResource as Mock).mockResolvedValue(
Promise.resolve({
url: "https://www.flickr.com",
type: "rich",
meta: {
title: "Flickr",
description:
"The safest and most inclusive global community of photography enthusiasts. The best place for inspiration, connection, and sharing!",
},
links: {
thumbnail: [
{
href: "https://combo.staticflickr.com/66a031f9fc343c5e42d965ca/671aaf5d51c929e483e8b26d_Open%20Graph%20Home.jpg",
type: "image/jpg",
rel: ["twitter", "thumbnail", "ssl", "og"],
content_length: 412824,
media: {
width: 1200,
height: 630,
},
},
],
icon: [
{
href: "https://combo.staticflickr.com/66a031f9fc343c5e42d965ca/67167dd041b0982f0f230dab_flickr-webclip.png",
rel: ["apple-touch-icon", "icon", "ssl"],
type: "image/png",
},
],
},
})
);
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: "https://www.flickr.com",
},
});
expect(res.status).toEqual(200);
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.url).toEqual("https://www.flickr.com");
expect(body.type).toEqual(UnfurlResourceType.URL);
expect(body.title).toEqual("Flickr");
expect(body.description).toEqual(
"The safest and most inclusive global community of photography enthusiasts. The best place for inspiration, connection, and sharing!"
);
expect(body.thumbnailUrl).toEqual(
"https://combo.staticflickr.com/66a031f9fc343c5e42d965ca/671aaf5d51c929e483e8b26d_Open%20Graph%20Home.jpg"
);
});
it("should succeed with status 204 no content for a non-existing external url", async () => {
(Iframely.requestResource as Mock).mockResolvedValue(
Promise.resolve({
status: 404,
error:
"Iframely could not fetch the given URL. The content is no longer available at the origin.",
})
);
const res = await server.post("/api/urls.unfurl", user, {
body: {
url: "https://random.url",
},
});
expect(res.status).toEqual(204);
});
});
describe("#urls.checkEmbed", () => {
let user: User;
beforeEach(async () => {
user = await buildUser();
});
it("should fail with status 400 bad request when url is missing", async () => {
const res = await server.post("/api/urls.checkEmbed", user);
expect(res.status).toEqual(400);
});
it("should fail with status 400 bad request when url is not a valid URL", async () => {
const res = await server.post("/api/urls.checkEmbed", user, {
body: {
url: "not-a-url",
},
});
expect(res.status).toEqual(400);
});
it("should return a result for valid URLs", async () => {
// Use a YouTube URL which matches a known embed pattern
const res = await server.post("/api/urls.checkEmbed", user, {
body: {
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
},
});
const body = await res.json();
expect(res.status).toEqual(200);
// Result depends on actual HTTP response from YouTube (or network error)
expect(body).toHaveProperty("embeddable");
});
});
describe("#urls.validateCustomDomain", () => {
it("should succeed with custom domain pointing at server", async () => {
const user = await buildUser();
const res = await server.post("/api/urls.validateCustomDomain", user, {
body: {
hostname: "valid.custom.domain",
},
});
expect(res.status).toEqual(200);
});
it("should fail with another domain", async () => {
const user = await buildUser();
const res = await server.post("/api/urls.validateCustomDomain", user, {
body: {
hostname: "google.com",
},
});
expect(res.status).toEqual(400);
});
});