This repository has been archived on 2026-03-27. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
drive/tests/server/wopi_discovery_test.ts
Sienna Meridian Satterwhite 58237d9e44 Initial commit — Drive, an S3 file browser with WOPI editing
Lightweight replacement for the upstream La Suite Numérique drive
(Django/Celery/Next.js) built as a single Deno binary.

Server (Deno + Hono):
- S3 file operations via AWS SigV4 (no SDK) with pre-signed URLs
- WOPI host for Collabora Online (CheckFileInfo, GetFile, PutFile, locks)
- Ory Kratos session auth + CSRF protection
- Ory Keto permission model (OPL namespaces, not yet wired to routes)
- PostgreSQL metadata with recursive folder sizes
- S3 backfill API for registering files uploaded outside the UI
- OpenTelemetry tracing + metrics (opt-in via OTEL_ENABLED)

Frontend (React 19 + Cunningham v4 + react-aria):
- File browser with GridList, keyboard nav, multi-select
- Collabora editor iframe (full-screen, form POST, postMessage)
- Profile menu, waffle menu, drag-drop upload, asset type badges
- La Suite integration service theming (runtime CSS)

Testing (549 tests):
- 235 server unit tests (Deno) — 90%+ coverage
- 278 UI unit tests (Vitest) — 90%+ coverage
- 11 E2E tests (Playwright)
- 12 integration service tests (Playwright)
- 13 WOPI integration tests (Playwright + Docker Compose + Collabora)

MIT licensed.
2026-03-25 18:28:37 +00:00

287 lines
8.8 KiB
TypeScript

/**
* Tests for WOPI discovery XML parsing and cache behavior.
*/
import {
assertEquals,
assertNotEquals,
assertRejects,
} from "https://deno.land/std@0.224.0/assert/mod.ts";
import {
parseDiscoveryXml,
getCollaboraActionUrl,
clearDiscoveryCache,
} from "../../server/wopi/discovery.ts";
// ── Fetch mock infrastructure ────────────────────────────────────────────────
const originalFetch = globalThis.fetch;
function mockFetch(
handler: (url: string, init?: RequestInit) => Promise<Response> | Response,
) {
globalThis.fetch = ((input: string | URL | Request, init?: RequestInit) => {
const url = typeof input === "string"
? input
: input instanceof URL
? input.toString()
: input.url;
return Promise.resolve(handler(url, init));
}) as typeof globalThis.fetch;
}
function restoreFetch() {
globalThis.fetch = originalFetch;
}
// ── parseDiscoveryXml tests ─────────────────────────────────────────────────
Deno.test("parseDiscoveryXml — parses single app with single action", () => {
const xml = `
<wopi-discovery>
<net-zone name="external-http">
<app name="application/vnd.oasis.opendocument.text" favIconUrl="http://example.com/icon.png">
<action name="edit" ext="odt" urlsrc="http://collabora:9980/loleaflet/dist/loleaflet.html?" />
</app>
</net-zone>
</wopi-discovery>
`;
const result = parseDiscoveryXml(xml);
assertEquals(result.size, 1);
const actions = result.get("application/vnd.oasis.opendocument.text");
assertNotEquals(actions, undefined);
assertEquals(actions!.length, 1);
assertEquals(actions![0].name, "edit");
assertEquals(actions![0].ext, "odt");
assertEquals(actions![0].urlsrc, "http://collabora:9980/loleaflet/dist/loleaflet.html?");
});
Deno.test("parseDiscoveryXml — parses multiple apps", () => {
const xml = `
<wopi-discovery>
<net-zone name="external-http">
<app name="application/vnd.oasis.opendocument.text">
<action name="edit" ext="odt" urlsrc="http://collabora:9980/edit/odt?" />
<action name="view" ext="odt" urlsrc="http://collabora:9980/view/odt?" />
</app>
<app name="application/pdf">
<action name="view" ext="pdf" urlsrc="http://collabora:9980/view/pdf?" />
</app>
</net-zone>
</wopi-discovery>
`;
const result = parseDiscoveryXml(xml);
assertEquals(result.size, 2);
const odtActions = result.get("application/vnd.oasis.opendocument.text");
assertEquals(odtActions!.length, 2);
assertEquals(odtActions![0].name, "edit");
assertEquals(odtActions![1].name, "view");
const pdfActions = result.get("application/pdf");
assertEquals(pdfActions!.length, 1);
assertEquals(pdfActions![0].name, "view");
});
Deno.test("parseDiscoveryXml — returns empty map for empty XML", () => {
const result = parseDiscoveryXml("<wopi-discovery></wopi-discovery>");
assertEquals(result.size, 0);
});
Deno.test("parseDiscoveryXml — skips actions without name or urlsrc", () => {
const xml = `
<wopi-discovery>
<net-zone>
<app name="text/plain">
<action name="" ext="txt" urlsrc="http://example.com/view?" />
<action name="edit" ext="txt" urlsrc="" />
<action name="view" ext="txt" urlsrc="http://example.com/view?" />
</app>
</net-zone>
</wopi-discovery>
`;
const result = parseDiscoveryXml(xml);
const actions = result.get("text/plain");
assertEquals(actions!.length, 1);
assertEquals(actions![0].name, "view");
});
Deno.test("parseDiscoveryXml — handles realistic Collabora discovery XML", () => {
const xml = `<?xml version="1.0" encoding="utf-8"?>
<wopi-discovery>
<net-zone name="external-http">
<app name="application/vnd.openxmlformats-officedocument.wordprocessingml.document" favIconUrl="http://collabora:9980/favicon.ico">
<action name="edit" ext="docx" urlsrc="http://collabora:9980/browser/dist/cool.html?" />
<action name="view" ext="docx" urlsrc="http://collabora:9980/browser/dist/cool.html?" />
</app>
<app name="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" favIconUrl="http://collabora:9980/favicon.ico">
<action name="edit" ext="xlsx" urlsrc="http://collabora:9980/browser/dist/cool.html?" />
</app>
<app name="application/vnd.oasis.opendocument.text" favIconUrl="http://collabora:9980/favicon.ico">
<action name="edit" ext="odt" urlsrc="http://collabora:9980/browser/dist/cool.html?" />
</app>
</net-zone>
</wopi-discovery>`;
const result = parseDiscoveryXml(xml);
assertEquals(result.size, 3);
assertNotEquals(
result.get("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
undefined,
);
assertNotEquals(
result.get("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
undefined,
);
assertNotEquals(
result.get("application/vnd.oasis.opendocument.text"),
undefined,
);
});
// ── getCollaboraActionUrl tests ─────────────────────────────────────────────
Deno.test("getCollaboraActionUrl — returns urlsrc for matching mimetype + action", async () => {
clearDiscoveryCache();
mockFetch(() =>
new Response(
`<wopi-discovery>
<net-zone>
<app name="text/plain">
<action name="edit" ext="txt" urlsrc="http://collabora:9980/edit/txt?" />
<action name="view" ext="txt" urlsrc="http://collabora:9980/view/txt?" />
</app>
</net-zone>
</wopi-discovery>`,
{ status: 200 },
)
);
try {
const url = await getCollaboraActionUrl("text/plain", "edit");
assertEquals(url, "http://collabora:9980/edit/txt?");
} finally {
restoreFetch();
clearDiscoveryCache();
}
});
Deno.test("getCollaboraActionUrl — returns null for unknown mimetype", async () => {
clearDiscoveryCache();
mockFetch(() =>
new Response(
`<wopi-discovery>
<net-zone>
<app name="text/plain">
<action name="edit" ext="txt" urlsrc="http://collabora:9980/edit/txt?" />
</app>
</net-zone>
</wopi-discovery>`,
{ status: 200 },
)
);
try {
const url = await getCollaboraActionUrl("application/unknown", "edit");
assertEquals(url, null);
} finally {
restoreFetch();
clearDiscoveryCache();
}
});
Deno.test("getCollaboraActionUrl — returns null for unknown action", async () => {
clearDiscoveryCache();
mockFetch(() =>
new Response(
`<wopi-discovery>
<net-zone>
<app name="text/plain">
<action name="edit" ext="txt" urlsrc="http://collabora:9980/edit/txt?" />
</app>
</net-zone>
</wopi-discovery>`,
{ status: 200 },
)
);
try {
const url = await getCollaboraActionUrl("text/plain", "view");
assertEquals(url, null);
} finally {
restoreFetch();
clearDiscoveryCache();
}
});
Deno.test("getCollaboraActionUrl — defaults to 'edit' action", async () => {
clearDiscoveryCache();
mockFetch(() =>
new Response(
`<wopi-discovery>
<net-zone>
<app name="text/plain">
<action name="edit" ext="txt" urlsrc="http://collabora:9980/edit/txt?" />
</app>
</net-zone>
</wopi-discovery>`,
{ status: 200 },
)
);
try {
const url = await getCollaboraActionUrl("text/plain");
assertEquals(url, "http://collabora:9980/edit/txt?");
} finally {
restoreFetch();
clearDiscoveryCache();
}
});
Deno.test("getCollaboraActionUrl — uses cache on second call", async () => {
clearDiscoveryCache();
let fetchCount = 0;
mockFetch(() => {
fetchCount++;
return new Response(
`<wopi-discovery>
<net-zone>
<app name="text/plain">
<action name="edit" ext="txt" urlsrc="http://collabora:9980/edit/txt?" />
</app>
</net-zone>
</wopi-discovery>`,
{ status: 200 },
);
});
try {
await getCollaboraActionUrl("text/plain", "edit");
await getCollaboraActionUrl("text/plain", "edit");
assertEquals(fetchCount, 1); // Should only fetch once due to cache
} finally {
restoreFetch();
clearDiscoveryCache();
}
});
Deno.test("getCollaboraActionUrl — throws after 3 failed retries", async () => {
clearDiscoveryCache();
mockFetch(() => new Response("Server Error", { status: 500 }));
try {
await assertRejects(
() => getCollaboraActionUrl("text/plain", "edit"),
Error,
"Collabora discovery fetch failed",
);
} finally {
restoreFetch();
clearDiscoveryCache();
}
});