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.
176 lines
5.5 KiB
TypeScript
176 lines
5.5 KiB
TypeScript
/**
|
|
* Tests for the telemetry module.
|
|
*
|
|
* These tests run with OTEL_ENABLED=false (the default) to verify
|
|
* that the no-op / graceful-degradation path works correctly, and
|
|
* that the public API surface behaves as expected.
|
|
*/
|
|
|
|
import {
|
|
assertEquals,
|
|
assertExists,
|
|
} from "https://deno.land/std@0.224.0/assert/mod.ts";
|
|
import { Hono } from "hono";
|
|
import {
|
|
tracingMiddleware,
|
|
metricsMiddleware,
|
|
withSpan,
|
|
traceDbQuery,
|
|
OTEL_ENABLED,
|
|
shutdown,
|
|
} from "../../server/telemetry.ts";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Sanity: OTEL_ENABLED should be false in the test environment
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("OTEL_ENABLED is false by default", () => {
|
|
assertEquals(OTEL_ENABLED, false);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Middleware no-op behaviour when OTEL_ENABLED = false
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("tracingMiddleware passes through when OTEL disabled", async () => {
|
|
const app = new Hono();
|
|
app.use("/*", tracingMiddleware);
|
|
app.get("/ping", (c) => c.text("pong"));
|
|
|
|
const res = await app.request("/ping");
|
|
assertEquals(res.status, 200);
|
|
assertEquals(await res.text(), "pong");
|
|
});
|
|
|
|
Deno.test("metricsMiddleware passes through when OTEL disabled", async () => {
|
|
const app = new Hono();
|
|
app.use("/*", metricsMiddleware);
|
|
app.get("/ping", (c) => c.text("pong"));
|
|
|
|
const res = await app.request("/ping");
|
|
assertEquals(res.status, 200);
|
|
assertEquals(await res.text(), "pong");
|
|
});
|
|
|
|
Deno.test("both middlewares together pass through when OTEL disabled", async () => {
|
|
const app = new Hono();
|
|
app.use("/*", tracingMiddleware);
|
|
app.use("/*", metricsMiddleware);
|
|
app.get("/hello", (c) => c.json({ msg: "world" }));
|
|
|
|
const res = await app.request("/hello");
|
|
assertEquals(res.status, 200);
|
|
const body = await res.json();
|
|
assertEquals(body.msg, "world");
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// withSpan utility — no-op when disabled
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("withSpan executes the function and returns its result when OTEL disabled", async () => {
|
|
const result = await withSpan("test.span", { key: "val" }, async (_span) => {
|
|
return 42;
|
|
});
|
|
assertEquals(result, 42);
|
|
});
|
|
|
|
Deno.test("withSpan propagates errors from the wrapped function", async () => {
|
|
let caught = false;
|
|
try {
|
|
await withSpan("test.error", {}, async () => {
|
|
throw new Error("boom");
|
|
});
|
|
} catch (e) {
|
|
caught = true;
|
|
assertEquals((e as Error).message, "boom");
|
|
}
|
|
assertEquals(caught, true);
|
|
});
|
|
|
|
Deno.test("withSpan provides a span object to the callback", async () => {
|
|
await withSpan("test.span_object", {}, async (span) => {
|
|
assertExists(span);
|
|
// The no-op span should have standard methods
|
|
assertEquals(typeof span.end, "function");
|
|
assertEquals(typeof span.setAttribute, "function");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// traceDbQuery utility — no-op when disabled
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("traceDbQuery executes the function and returns result", async () => {
|
|
const result = await traceDbQuery("SELECT 1", async () => {
|
|
return [{ count: 1 }];
|
|
});
|
|
assertEquals(result, [{ count: 1 }]);
|
|
});
|
|
|
|
Deno.test("traceDbQuery propagates errors", async () => {
|
|
let caught = false;
|
|
try {
|
|
await traceDbQuery("SELECT bad", async () => {
|
|
throw new Error("db error");
|
|
});
|
|
} catch (e) {
|
|
caught = true;
|
|
assertEquals((e as Error).message, "db error");
|
|
}
|
|
assertEquals(caught, true);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Middleware does not break error responses
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("tracingMiddleware handles 404 routes gracefully", async () => {
|
|
const app = new Hono();
|
|
app.use("/*", tracingMiddleware);
|
|
app.use("/*", metricsMiddleware);
|
|
// No routes registered for /missing
|
|
|
|
const res = await app.request("/missing");
|
|
assertEquals(res.status, 404);
|
|
});
|
|
|
|
Deno.test("tracingMiddleware handles handler errors gracefully", async () => {
|
|
const app = new Hono();
|
|
app.use("/*", tracingMiddleware);
|
|
app.use("/*", metricsMiddleware);
|
|
app.get("/explode", () => {
|
|
throw new Error("handler error");
|
|
});
|
|
|
|
const res = await app.request("/explode");
|
|
// Hono returns 500 for unhandled errors
|
|
assertEquals(res.status, 500);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// shutdown is safe when SDK was never initialised
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("shutdown is a no-op when OTEL disabled", async () => {
|
|
// Should not throw
|
|
await shutdown();
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Middleware preserves response headers from downstream handlers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Deno.test("tracingMiddleware preserves custom response headers", async () => {
|
|
const app = new Hono();
|
|
app.use("/*", tracingMiddleware);
|
|
app.get("/custom", (c) => {
|
|
c.header("X-Custom", "test-value");
|
|
return c.text("ok");
|
|
});
|
|
|
|
const res = await app.request("/custom");
|
|
assertEquals(res.status, 200);
|
|
assertEquals(res.headers.get("X-Custom"), "test-value");
|
|
});
|