Blame · Line-by-line history
status-seo.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 9b07ca9 | 1 | /** |
| 2 | * Smoke tests for the public /status page, /status.svg badge, and SEO | |
| 3 | * routes (/robots.txt + /sitemap.xml). These don't stub the DB — they | |
| 4 | * tolerate either success (dev DB reachable) or a graceful error page | |
| 5 | * so they work in the sandbox environment. | |
| 6 | */ | |
| 7 | ||
| 8 | import { test, expect } from "bun:test"; | |
| 9 | import app from "../app"; | |
| 10 | ||
| 11 | test("/status returns 200 with HTML body", async () => { | |
| 12 | const res = await app.request("/status"); | |
| 13 | expect(res.status).toBe(200); | |
| 14 | const body = await res.text(); | |
| 15 | expect(body).toContain("<html"); | |
| 16 | // Either the green or red headline should appear | |
| 17 | expect( | |
| 18 | body.includes("All systems operational") || | |
| 74a8784 | 19 | body.includes("Degraded performance") || |
| 20 | body.includes("Major outage") | |
| 9b07ca9 | 21 | ).toBe(true); |
| 22 | }); | |
| 23 | ||
| 24 | test("/status.svg returns an SVG badge", async () => { | |
| 25 | const res = await app.request("/status.svg"); | |
| 26 | expect(res.status).toBe(200); | |
| 27 | expect(res.headers.get("content-type")).toMatch(/image\/svg/); | |
| 28 | const body = await res.text(); | |
| 29 | expect(body).toContain("<svg"); | |
| 30 | expect(body).toContain("</svg>"); | |
| 31 | }); | |
| 32 | ||
| 33 | test("/robots.txt returns 200 with crawler directives", async () => { | |
| 34 | const res = await app.request("/robots.txt"); | |
| 35 | expect(res.status).toBe(200); | |
| 36 | expect(res.headers.get("content-type")).toMatch(/text\/plain/); | |
| 37 | const body = await res.text(); | |
| 38 | expect(body).toContain("User-agent:"); | |
| 39 | expect(body).toContain("Sitemap:"); | |
| 40 | expect(body).toContain("Disallow: /admin"); | |
| 41 | }); | |
| 42 | ||
| 43 | test("/sitemap.xml returns valid-looking XML", async () => { | |
| 44 | const res = await app.request("/sitemap.xml"); | |
| 45 | expect(res.status).toBe(200); | |
| 46 | expect(res.headers.get("content-type")).toMatch(/xml/); | |
| 47 | const body = await res.text(); | |
| 48 | expect(body).toContain('<?xml'); | |
| 49 | expect(body).toContain("<urlset"); | |
| 50 | expect(body).toContain("<loc>"); | |
| 51 | expect(body).toContain("</urlset>"); | |
| 52 | }); | |
| 53 | ||
| 54 | test("sitemap includes the landing, status, explore URLs", async () => { | |
| 55 | const res = await app.request("/sitemap.xml"); | |
| 56 | const body = await res.text(); | |
| 57 | expect(body).toMatch(/<loc>[^<]*\/<\/loc>/); | |
| 58 | expect(body).toContain("/status"); | |
| 59 | expect(body).toContain("/explore"); | |
| 60 | }); | |
| a0071d7 | 61 | |
| 62 | test("/llms.txt maps the platform for AI agents", async () => { | |
| 63 | const res = await app.request("/llms.txt"); | |
| 64 | expect(res.status).toBe(200); | |
| 65 | expect(res.headers.get("content-type")).toMatch(/text\/plain/); | |
| 66 | const body = await res.text(); | |
| 67 | // llmstxt.org shape: H1 title + blockquote summary. | |
| 68 | expect(body.startsWith("# Gluecron")).toBe(true); | |
| 69 | expect(body).toContain("\n> "); | |
| 70 | // Must point agents at the load-bearing surfaces. | |
| 71 | expect(body).toContain("/mcp"); | |
| 72 | expect(body).toContain("/.well-known/oauth-authorization-server"); | |
| 73 | expect(body).toContain("/api/docs"); | |
| 74 | }); | |
| 75 | ||
| 76 | test("/.well-known/llms.txt serves the same agent map", async () => { | |
| 77 | const a = await (await app.request("/llms.txt")).text(); | |
| 78 | const b = await (await app.request("/.well-known/llms.txt")).text(); | |
| 79 | expect(b).toBe(a); | |
| 80 | expect(b.length).toBeGreaterThan(100); | |
| 81 | }); |