/*
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.
*/
// Post media and post pages over real HTTP. The media IS the content of these
// posts (screen recordings of the work), and it must come from our own
// origin: the privacy notice states that everything the browser loads comes
// from catcrafts.net, and a third-party embed would send every visitor's IP
// to whichever instance hosted the file.
import std;
import Catcrafts.E2eHarness;
using namespace Catcrafts::E2e;
namespace {
// Media loaded from a third party — `poster` is in the list because a video
// poster is fetched on page load exactly like an src is.
const std::regex kThirdPartyMedia(
R"((src|srcset|href|poster)="https?://[^"]*\.(mp4|webm|webp|avif|png|jpe?g|gif))");
std::vector PostPathsFromSitemap(TestServer& srv, std::size_t limit) {
const std::string sitemap = srv.Body("/sitemap.xml");
const std::regex slug(R"(/posts/[a-z0-9-]+)");
std::vector out;
for (auto it = std::sregex_iterator(sitemap.begin(), sitemap.end(), slug);
it != std::sregex_iterator() && out.size() < limit; ++it) {
if (std::ranges::find(out, it->str()) == out.end()) out.push_back(it->str());
}
return out;
}
} // namespace
int main(int argc, char** argv) {
TestServer srv(argv[1], 8214);
const std::string posts = srv.Body("/posts");
// ── post media ────────────────────────────────────────────────────
Check(std::regex_search(posts, std::regex(R"(<(img|video) class="post-media__item")")),
"/posts embeds its media");
Check(!std::regex_search(posts, kThirdPartyMedia),
"/posts loads no media from a third party");
// Dimensions prevent layout shift as each file arrives. Needs ffprobe at
// fetch time — a build host without it produces no dimensions at all,
// which is what this catches.
Check(std::regex_search(posts, std::regex(
R"(]*width="[0-9]+" height="[0-9]+")")),
"images carry width/height");
// Videos too. This assertion exists because they silently lost theirs:
// ffprobe appends an empty CSV field for some files, so parsing
// `width,height` as one joined string yielded a height of "480x" and the
// guard discarded both.
Check(std::regex_search(posts, std::regex(
R"(