Počas nedávnej transformácie blogu, najmä migráciou zo Zoly na Astro, som začal pridávať funkcie ovládané env flagmi: komentáre, newsletter, sidebar s inými projektmi, upozornenie na preklad, digitálna záhrada. Funkcia komentárov prišla ako prvá, potom prechod na dvojjazyčnosť so samostatnou slovenskou doménou znamenal, že flag upozornenia na preklad musel fungovať inak pre každú lokalizáciu. Chcel som mať Playwright testy pokrývajúce toto všetko poriadne — teda funkcie zapnuté aj vypnuté, v angličtine aj slovenčine. Celkovo štyri kombinácie.

Chcel som mať testovacie pokrytie ako vždy, no dostať sa tam sa ukázalo byť oveľa dlhšou cestou, než som čakal.

Naivný prístup #

Môj prvý inštinkt bol ponechať dva buildy (EN a SK) a odovzdávať feature flagy za behu pri spúšťaní Wrangler dev servera:

wrangler pages dev dist-en --binding FEATURE_COMMENTS=true --port 4321
wrangler pages dev dist-en --binding FEATURE_COMMENTS= --port 4322

Flag --binding vyzeral sľubne. Nič nespravil. Feature flagy stále prichádzali z .dev.vars, ktorý má tichú prednosť pred --binding bez akéhokoľvek varovania. Na to som prišiel až vylúčením všetkého ostatného.

Ďalší pokus bol --env-file:

wrangler pages dev dist-en --env-file tests/env/features-on.env

Tento niečo robí, len nie to, čo chcete. Flag --env-file vkladá hodnoty do Node.js process.env, procesu bežiaceho samotný Wrangler. Nedotýka sa runtime prostredia Cloudflare Workera, z ktorého číta locals.runtime.env vo vnútri Astro endpointu alebo server island. Worker teda nič nevidí, flagy zostávajú vypnuté.

Pečenie funkcií do buildu #

V tomto bode bolo jasné, že jediná spoľahlivá možnosť je vpiect flagy priamo do buildu. Vite má na to konfiguráciu define — robí doslovnú substitúciu reťazcov počas buildu:

vite: {
  define: {
    "import.meta.env.FEATURE_COMMENTS": JSON.stringify(process.env.FEATURE_COMMENTS === "true"),
  }
}

Zdalo sa, že to funguje, kým som neskontroloval skutočný súbor workera a nenašiel:

const showComments = false

Vpečené ako false aj vo features-on builde. Dôvod: SSR transform Astro Cloudflare adaptéra prepísuje každý prístup import.meta.env.X na process.env.X v bundle workera. Toto beží po Vite define passe a ticho to zruší. Žiadna chyba sa neobjaví. Hodnota sa ticho stane tým, čo process.env obsahuje za behu vo Workeri — čo nie je nič.

Riešením je použiť vlastné identifikátory, ktoré Astro nerozpozná ako prístupy import.meta.env a teda ich neprepíše:

vite: {
  define: {
    __FEATURE_COMMENTS__: JSON.stringify(process.env.FEATURE_COMMENTS === "true"),
  }
}

A v komponente:

const showComments = __FEATURE_COMMENTS__

Po tomto sa správne true alebo false vpečie do každého buildu.

Štyri buildy, zdieľané D1 #

So štyrmi samostatnými buildmi (EN off, EN on, SK off, SK on) testovací setup spúšťa štyri Wrangler inštancie paralelne. Databáza D1 musela byť dostupná zo všetkých. Keď každá bežala s vlastným adresárom stavu, wrangler d1 migrations apply blog-db --local sa aplikovalo len na tú inštanciu, ktorú náhodou našlo ako prvú. Ostatné tri buď preskočili alebo ticho zlyhali s:

✅ No migrations to apply!

Aj keď tabuľka v skutočnosti ešte neexistovala.

Riešením je --persist-to .wrangler/state na každej inštancii:

wrangler pages dev dist-test/en-off --port 4321 --persist-to .wrangler/state
wrangler pages dev dist-test/en-on  --port 4322 --persist-to .wrangler/state
wrangler pages dev dist-test/sk-off --port 4323 --persist-to .wrangler/state
wrangler pages dev dist-test/sk-on  --port 4324 --persist-to .wrangler/state

Všetky štyri teraz zdieľajú rovnaký SQLite súbor pod .wrangler/state. Migrácie sa aplikujú raz a všetky inštancie vidia výsledok.

Ešte pár prekvapení #

Server islands. Sekcia komentárov sa načítava cez Astro server:defer — asynchrónne v prehliadači po načítaní stránky. Playwright skontroluje DOM pred načítaním island, nenájde nič, test zlyhá. Pridanie await page.waitForLoadState("networkidle") po navigácii to opraví.

Content Security Policy. Súbor public/_headers mal script-src 'self' bez 'unsafe-inline'. Mechanizmus server island v Astro vkladá obsah pomocou inline skriptov, ktoré prehliadač ticho zablokuje. Island zostane zaseknutý na “Loading comments…” navždy. Pridanie 'unsafe-inline' do script-src to opravilo. Za zmienku stojí, že to platí aj v produkcii, keďže rovnaký súbor _headers sa nasadí na Cloudflare Pages.

CSRF ochrana. Astro 5 štandardne zapína CSRF ochranu v SSR režime. Kontroluje hlavičku Origin pri POST požiadavkách. API request context Playwright ho neodosiela, takže každý API test dostal:

Cross-site POST form submissions are forbidden

ako 403 odpoveď. Opravené pridaním extraHTTPHeaders: { Origin: baseURL } pre každý projekt v playwright.config.ts.

Výsledok #

Štyri Playwright projekty, každý smerujúci na vlastnú predzostavenú Wrangler inštanciu. Funkcie vpečené počas buildu, nie odovzdávané za behu. Jeden zdieľaný adresár stavu D1. Trvalo to dlhšie, než malo, ale setup je teraz čistý.

Odkazy #