== A. renderer == PASS GD + FreeType + a font are available for these tests PASS the production example renders a valid 1200x630 PNG PASS rendering is deterministic for the same fields PASS a normal timed event renders PASS a different event renders different pixels PASS a long two-line title renders PASS a long calendar name renders PASS unicode, apostrophes and dashes render PASS emoji / unsupported glyphs render without a fatal PASS a pathological unbroken 10k-char title renders bounded PASS invalid UTF-8 input renders without a fatal PASS empty fields render a valid card PASS no render emitted a PHP warning/notice (0) PASS with GD absent the renderer returns "" for the caller to fall back PASS "No Students..." wraps to <= 2 in-bounds lines PASS "Year 2 Choir Performance..." wraps to <= 2 in-bounds lines PASS "Year 2 Choir Performance and Family Info..." wraps to <= 2 in-bounds lines PASS "Parents & Friends Association Annual Com..." wraps to <= 2 in-bounds lines PASS overflowing text ends in an ellipsis on the final line PASS an unbroken 400-char token is hard-broken, never overflowing PASS empty text wraps to no lines == B. privacy == PASS extra (forbidden) keys cannot change the rendered pixels PASS extra (forbidden) keys cannot change the version tag PASS the production projection really is the expected four strings PASS private-field-only changes do not change the public projection PASS ...nor the version tag PASS ...nor the rendered pixels PASS the version tag is bounded opaque hex PASS the image URL is exactly origin + /e//preview-.png PASS the image URL carries no query string and no separate field PASS the renderer library defines no second privacy projection == C. version tag == PASS changing title changes the version tag PASS changing title changes the image URL PASS changing date changes the version tag PASS changing date changes the image URL PASS changing time changes the version tag PASS changing time changes the image URL PASS changing calendar changes the version tag PASS changing calendar changes the image URL PASS the renderer version is an input to the tag PASS the default renderer version is the production constant PASS two field sets that could concatenate alike stay distinct == D. canonical URL == PASS the active page renders (200) PASS rel=canonical is the exact /e/ URL PASS og:url is the exact /e/ URL PASS the Share/Copy URL is the exact /e/ URL PASS the Share/Copy URL is NOT the preview image URL PASS og:image is the versioned event-specific preview URL PASS og:image:secure_url matches og:image PASS twitter:image matches og:image PASS og:image still declares image/png 1200x630 PASS og:image:alt describes the public event safely PASS og:title / og:description semantics are unchanged == preview endpoint (active) == PASS GET the advertised og:image returns 200 PASS the body is a valid 1200x630 PNG PASS Content-Type is image/png PASS Content-Length matches the body PASS the preview state header reports ok PASS the endpoint renders exactly the approved projection PASS Cache-Control is public and bounded == C2. versioning over HTTP == PASS a title update changes the image URL PASS a date update changes the image URL PASS a calendar-name update changes the image URL PASS a LOCATION-only update does NOT change the image URL PASS a DESCRIPTION-only update does NOT change the image URL PASS the /e/ page URL itself never moved through any of that PASS a stale/made-up version tag serves the CURRENT card == E. security == PASS a tampered reference yields 404 + a generic PNG PASS a tampered reference never serves the event-specific card PASS a tampered reference response is an image, never HTML PASS a flipped signature yields 404 + a generic PNG PASS a flipped signature never serves the event-specific card PASS a flipped signature response is an image, never HTML PASS an unsupported version yields 404 + a generic PNG PASS an unsupported version never serves the event-specific card PASS an unsupported version response is an image, never HTML PASS a garbage reference yields 404 + a generic PNG PASS a garbage reference never serves the event-specific card PASS a garbage reference response is an image, never HTML PASS every rejected reference reports the bounded invalid state PASS a hostile Host header changes nothing about the image PASS no application response header echoes the hostile host PASS the page og:image ignores Host/X-Forwarded-Host PASS the endpoint reads exactly the ref and v parameters (ref,v) PASS the endpoint never reads $_GET directly beyond the scalar helper PASS no base/token/uid/calendarUrl/title parameter exists PASS the endpoint builds no upstream URL of its own PASS the endpoint never derives anything from Host headers PASS the raw script without a signed ref serves only the generic 404 card PASS preview headers never carry previewTOKEN0001 PASS preview headers never carry no-students-1 PASS preview headers never carry calembed-event-link-test PASS preview headers never carry remote.php/dav == F. non-active == PASS a cancelled event serves a generic card, not the old title card PASS the cancelled card equals the generic render (no event data at all) PASS the cancelled response is 200 with a short cache lifetime PASS the cancelled state header is bounded PASS the cancelled PAGE also reverts og:image to the generic static image PASS ...and its og:image:alt is generic again PASS a removed event serves the generic card (crawler race) PASS the removed-event card carries no event data PASS a revoked calendar serves the generic card PASS a revoked calendar and a removed event are indistinguishable PASS a temporary upstream failure returns a valid generic PNG PASS the temporary failure is 503 with a short cache lifetime PASS the temporary response is an image, never an HTML error page PASS with NO secret the preview fails closed to a generic 503 PNG == G. caching == PASS the restored active preview serves again PASS an ETag is sent PASS the ETag is opaque (hashed), never the raw token/reference PASS a conditional request with the current ETag yields 304 PASS a conditional request with a stale ETag re-serves 200 PASS the render landed in the event-preview cache namespace PASS no cache filename contains the raw token or UID PASS a repeated request serves byte-identical output PASS a corrupted cache entry is discarded and the card re-renders == H. route / CI / docs == PASS .htaccess routes the preview URL to event-link-preview.php PASS the preview rule comes BEFORE the plain /e/ rule PASS the preview rule is NOT [QSA] PASS the plain /e/ rule is unchanged PASS the ordinary /e/ page still resolves PASS CI runs this suite as a gated step PASS CI still has exactly one workflow and one job PASS README documents the event-specific preview image PASS README lists this self-test PASS calendar-preview.php is unchanged by this feature PASS lib/calendar_preview_renderer.php is unchanged by this feature PASS lib/calendar_event_link.php is unchanged by this feature PASS assets/event-link-preview.png is unchanged by this feature event link preview tests passed