/* catcrafts.net Copyright (C) 2026 Catcrafts The source code of this website is made available for viewing purposes only. No permission is granted to copy, modify, distribute, or create derivative works. */ // Status codes, redirects, response headers, and the sitemap/feed documents — // the things only a real request can show. A real 404 is the whole reason the // backend exists: a client-side router cannot produce one. import std; import Catcrafts.E2eHarness; using namespace Catcrafts::E2e; int main(int argc, char** argv) { TestServer srv(argv[1], 8210); // ── status codes ────────────────────────────────────────────────── for (std::string_view p : { "/", "/about", "/shop", "/shop/fp6-pmos", "/shop/fp6plus-pmos", "/shop/donation", "/projects", "/posts", "/demos", "/demos/raytracer", "/financials", "/legal/privacy", "/legal/terms", "/legal/imprint", "/feed.xml", "/sitemap.xml", "/api/healthz" }) { srv.CheckStatus(std::string(p), "200"); } // Trailing slashes must normalise, not 404 or duplicate the canonical URL. srv.CheckStatus("/projects/", "200"); srv.CheckStatus("/shop/", "200"); // A real 404, which a client-side router cannot produce — this is the // whole reason the backend exists. srv.CheckStatus("/nope", "404"); srv.CheckStatus("/shop/nope", "404"); srv.CheckStatus("/legal/nope", "404"); // A slug that cannot be one of ours is rejected before any lookup. srv.CheckStatus("/shop/BAD--slug", "404"); srv.CheckStatus("/demos/nope", "404"); // A post slug that parsed but names nothing must be a real 404, or every // typo and every retired post becomes an indexable empty page. srv.CheckStatus("/posts/nope", "404"); srv.CheckStatus("/posts/BAD--slug", "404"); // The retired blog URLs are still in the wild; they must redirect, not 404. srv.CheckStatus("/blog", "301"); srv.CheckStatus("/blog/hello-world", "301"); // /demo was the single-demo URL before there was a list; it must redirect, // not 404, because it was linked from the home page. srv.CheckStatus("/demo", "301"); // ── redirects ───────────────────────────────────────────────────── { const auto blog = srv.Get("/blog/hello-world"); const auto it = blog.headers.find("location"); Check(it != blog.headers.end() && it->second.starts_with("/posts"), "/blog/* sends Location: /posts", it == blog.headers.end() ? "no location header" : it->second); } { const auto demo = srv.Get("/demo"); const auto it = demo.headers.find("location"); Check(it != demo.headers.end() && it->second.starts_with("/demos"), "/demo sends Location: /demos", it == demo.headers.end() ? "no location header" : it->second); } // ── headers ─────────────────────────────────────────────────────── srv.HeaderHas("/", "x-content-type-options", "nosniff", "nosniff on pages"); srv.HeaderHas("/", "cache-control", "public", "pages are cacheable"); srv.HeaderHas("/nope", "x-robots-tag", "noindex", "404 is noindex"); srv.HeaderHas("/feed.xml", "content-type", "application/atom", "feed content-type"); srv.HeaderHas("/sitemap.xml", "content-type", "application/xml", "sitemap content-type"); // ── sitemap and feed content ────────────────────────────────────── srv.BodyHas("/sitemap.xml", "/shop/fp6-pmos", "sitemap lists the product"); srv.BodyHas("/sitemap.xml", "/shop/fp6plus-pmos", "sitemap lists the refresh — it comes off the catalogue, not a second list"); srv.BodyHas("/sitemap.xml", "/about", "sitemap lists the about page"); srv.BodyHas("/sitemap.xml", "/legal/privacy", "sitemap lists the privacy page"); srv.BodyHas("/sitemap.xml", "/demos", "sitemap lists the demos page"); srv.BodyHas("/sitemap.xml", "/financials", "sitemap lists the financials page"); srv.BodyLacks("/sitemap.xml", "/blog", "sitemap does not advertise the redirect"); srv.BodyLacks("/sitemap.xml", "/order", "sitemap does not advertise order pages"); srv.BodyHas("/feed.xml", "", "feed is Atom"); // ── abuse ───────────────────────────────────────────────────────── srv.CheckStatus("/shop/fp6-pmos", "413", "POST", "email=a%40b.example&name=" + std::string(20000, 'x') + "&street=x&postal=1&city=y&country=NL"); { const auto json = srv.Post("/shop/fp6-pmos", "{}", "application/json"); Check(json.status == "415", "POST with a JSON content-type -> 415", json.status); } srv.CheckStatus("/shop/nope", "404", "POST", "email=a%40b.example&name=Ada&street=x&postal=1&city=y&country=NL"); srv.CheckStatus("/projects", "405", "POST", "x=1"); // HEAD must not be a 500 or a body — some crawlers use it exclusively. srv.CheckStatus("/", "200", "HEAD"); { const auto head = srv.Head("/"); Check(head.body.empty(), "HEAD carries no body"); } return Finish(); }