caterium-app/tests/release-check.mjs
pavlov346346-source 195e0ba5c3 fix: reconcile version/test drift from the unreviewed production merge
The just-merged production-only commits were never run through QA
(they were pushed directly to the production branch), so several
version markers and hardcoded test strings had drifted out of sync
with each other:

- package.json was bumped to 17.8.0 but package-lock.json,
  release-manifest.json and app-runtime.js's own VERSION constant were
  never updated to match - reverted to 17.7.3 since no other release
  artifact actually changed.
- service-worker.js's cache name legitimately moved to
  v81-20260912-account-center-loader (real new modules need the cache
  bust), but release-manifest.json's pwaCache field and two
  release-check.mjs assertions still expected the old v78 name.
- edge-security-v1774.mjs and static-security.mjs asserted the old
  employee role list (with "admin") and old PWA cache name that
  production's own commits had already changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 14:40:22 +03:00

81 lines
12 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
const root=process.cwd(), pub=path.join(root,'public');
const read=p=>fs.readFileSync(path.join(pub,p),'utf8');
const readRoot=p=>fs.readFileSync(path.join(root,p),'utf8');
let bad=0;const check=(v,m)=>{console.log(`${v?'OK':'FAIL'}: ${m}`);if(!v)bad++};
const index=read('index.html'),runtime=read('app-runtime.js'),sw=read('service-worker.js'),css=read('core/stability-v1760.css'),performance=read('core/performance.js'),dataLayer=read('core/data-layer-v1773.js'),legacy=read('legacy/bootstrap.js'),ops=read('core/ops-ux-v1762.js'),hotfix=read('core/hotfix-v1763.js'),ux=read('core/ux-fixes-v1764.js'),classic=read('core/classic-offer-pdf-v1767.js'),developerUX=read('core/developer-console-v1768.js'),offerWorkspace=read('core/offer-workspace-v1769.js');
const pkg=JSON.parse(readRoot('package.json'));
const lock=JSON.parse(readRoot('package-lock.json'));
const releaseManifest=JSON.parse(readRoot('docs/release-manifest.json'));
check(index.includes('core/stability-v1760.css'),'mobile stability stylesheet loaded');
check(css.includes('overflow-x:hidden')&&css.includes('.cats'),'mobile overflow guard present');
check(!index.includes('offer-gallery-data.js'),'blocking Base64 gallery absent');
check((runtime.match(/\/Type \/Catalog/g)||[]).length===0,'runtime contains no PDF binary writer');
check(read('core/pdf-engine.js').includes('595.28')&&read('core/pdf-engine.js').includes('841.89'),'PDF engine uses A4 MediaBox');
check([...index.matchAll(/@page\{([^}]*)\}/g)].every(m=>/size:A4/i.test(m[1])),'compact @page rules use A4');
check(sw.includes('v81-20260912-account-center-loader')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js')&&sw.includes('offer-workspace-v1769.js'),'service worker cache is v17.7.3');
check(index.includes('20260909-v17-7-3-clients-server-read')&&index.includes('classic-offer-pdf-v1767.js')&&!index.includes('20260907-v17-6-0-stability-security'),'index cache-busting points to v17.7.3');
check(performance.includes('SunAttachmentGuard')&&performance.includes('TARGET=2*1024*1024'),'chat photo auto-compression is versioned');
check(performance.includes("rpc('sun_dev_dashboard')")&&performance.includes('server_size')&&performance.includes('storage_size'),'Developer Console server/storage counters are versioned');
check(performance.includes('MEMORY_REFRESH_MS=30000')&&performance.includes('MEMORY_TIMEOUT_MS=8000')&&performance.includes('memoryPromise'),'Developer Console memory refresh is bounded');
check(!performance.includes('records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory()'),'Developer Console refresh is not mutation-driven');
check(performance.includes('ux-fixes-v1764.js')&&performance.includes('SunUXFixV1764'),'v17.6.4 UX module is loaded');
check(performance.includes('hotfix-v1763.js')&&performance.includes('SunHotfixV1763'),'developer/SaaS hotfix is loaded');
check(hotfix.includes('patchDeveloperOpen')&&hotfix.includes('enhanceDeveloperGate')&&hotfix.includes('data-saas-admin'),'developer gate and SaaS click hotfix is versioned');
check(hotfix.includes('source instanceof HTMLElement')&&hotfix.includes('stopImmediatePropagation'),'SaaS event object cannot reach Developer Console as a nav button');
check(performance.includes('ops-ux-v1762.js'),'operations UX module is loaded by performance core');
check(ops.includes('supportReadPermission')&&ops.includes('SUPPORT_POLL_MS=20000')&&ops.includes('refreshSupportWorkspace'),'developer support read-only refresh is lightweight and bounded');
check(ops.includes('sun-menu-editor-v1762')&&ops.includes('Премиум боксы'),'full-page menu editor is versioned');
check(ops.includes('showCalendarDay')&&ops.includes('.cal-more'),'calendar overflow panel is versioned');
check(ops.includes("ROUTE_BASE_KEY='sunRouteBaseV1'")&&ops.includes('saveRouteStart'),'configurable route origin is versioned');
check(ops.includes('showRouteOrder')&&ops.includes('data-route-open'),'route order modal is versioned');
check(ux.includes("AUTO_DELAY_MS=60*1000")&&ux.includes("order.status='Отдан заказчику'")&&ux.includes('order.prepayment=total'),'orders auto-complete and auto-pay one minute after due time');
check(ux.includes('classificationDate')&&ux.includes('sunAutoCompletedAt'),'auto-completed orders move to completed list without losing original date');
check(ux.includes('persistOfferTemplate')&&ux.includes('clientOfferTemplateId')&&ux.includes('offerTemplateId'),'client proposal template is stored per offer');
check(ux.includes('sunMenuIconV1766')&&ux.includes('--sun-icon:url('),'menu uses the native sidebar mask icon without a duplicate square');
check(fs.existsSync(path.join(root,'supabase/functions/caterium-create-employee/index.ts')),'employee Edge Function source is versioned');
check(!/sb_secret_[A-Za-z0-9_-]{20,}|service_role\s*[:=]\s*["'][A-Za-z0-9._-]{30,}/i.test(index+runtime+performance+ops+hotfix+ux+classic),'no client secret-like token');
check(lock.version===pkg.version&&lock.packages?.['']?.version===pkg.version,'package.json and package-lock.json versions match');
check(releaseManifest.version===`v${pkg.version}`,'release manifest version matches package.json');
check(releaseManifest.channel==='production','release manifest channel is production');
check(String(releaseManifest.pwaCache||'').includes('v81-20260912-account-center-loader'),'release manifest points to current PWA cache');
check(['17.6.2','17.6.3','17.6.4','17.6.5','17.6.6','17.6.7','17.6.8','17.6.9','17.7.0','17.7.1','17.7.2','17.7.3'].every(v=>fs.existsSync(path.join(root,`docs/releases/V${v}-CHANGES.txt`))),'release notes exist through v17.7.3');
check(runtime.includes('CLOUD_RPC_TIMEOUT_MS=12000')&&runtime.includes('CLOUD_CONFLICT_MAX_RETRIES=4')&&runtime.includes('retryCount'),'cloud sync has timeout and capped exponential conflict retries');
check(runtime.includes("const VERSION = '17.7.3'")&&runtime.includes('ERROR_DEDUPE_MS=5*60*1000')&&runtime.includes('mirrorBusy=false')&&runtime.includes('backupBusy=false'),'stability logger uses current version, dedupe and single-flight guards');
check(runtime.includes('refreshSupportWorkspace')&&runtime.includes('sun_dev_support_snapshot'),'cloud exposes lightweight read-only support refresh');
check(runtime.includes('DEV_ADMIN_TTL_MS=30000')&&hotfix.includes('checkPlatformAdmin?.(false)')&&hotfix.includes('},10000);'),'developer access checks are throttled');
check(performance.includes('pendingImageRoots')&&performance.includes('queueImageScan')&&ux.includes('},5000);'),'background DOM maintenance is batched/throttled');
check(runtime.includes('explicitTemplate')&&runtime.includes("OFFER_TEMPLATE_IDS.has(explicitTemplate)"),'per-order proposal template survives render and PDF');
check(runtime.includes("else if(id==='midnight-glass')")&&runtime.includes("else if(id==='emerald-gold')")&&runtime.includes("if(id==='editorial-grid')"),'existing proposal layout sequences remain available');
check(runtime.includes('data-template-mini')&&ux.includes('data-template-mini'),'global and per-offer selectors show structural PDF previews');
check(pkg.version==='17.7.3','package version is v17.7.3');
check(classic.includes("const VERSION='17.6.7'")&&classic.includes('CLASSIC_IDS')&&classic.includes('ARCHIVE_IDS'),'classic proposal PDF module is v17.6.7');
check(runtime.includes("'warm-sun'")&&runtime.includes("'bento-cards'")&&runtime.includes("'event-story'")&&runtime.includes("'personal-letter'")&&runtime.includes("'event-ticket'")&&runtime.includes("'solar-experience'"),'10 classic proposal designs are available');
check(runtime.includes("'midnight-compact'")&&runtime.includes("'black-gold'")&&runtime.includes("'neon-emerald'"),'hidden archive proposal templates are restored');
check(runtime.includes('SunClassicOfferPDFV1767')&&runtime.includes('renderOfferPdfPagesBase'),'classic renderer wraps the same PDF pages used by preview/download');
check(classic.includes('data-sun-classic-cover')&&classic.includes('renderPages'),'classic renderer adds real A4 cover pages');
check(releaseManifest.offerTemplates===13&&releaseManifest.classicOfferTemplates===10,'release manifest exposes 10 classic + 3 archive templates');
check(developerUX.includes("const VERSION='17.6.8'")&&developerUX.includes('sun_dev_delete_company_v1768')&&developerUX.includes('sun_dev_error_groups_v1768'),'Developer Console v17.6.8 module is versioned');
check(developerUX.includes('selectedAccountsWorkspace')&&developerUX.includes('sun-dev-plan-matrix'),'accounts are grouped by company and plans use compact matrix');
check(developerUX.includes('KNOWN_DOM_RACE')&&developerUX.includes('prepareAsyncRoots'),'Developer Console async DOM race is guarded');
check(releaseManifest.developerCompanyNumbers===true&&releaseManifest.developerCompanyDelete===true&&releaseManifest.developerAccountsGroupedByCompany===true&&releaseManifest.developerPlansCompactMatrix===true&&releaseManifest.developerJournalGroupedErrors===true,'release manifest records Developer Console UX changes');
check(offerWorkspace.includes("const VERSION='17.6.9'")&&offerWorkspace.includes('sun-offer-workspace-tabs-v1769'),'offer workspace v17.6.9 is versioned');
check(offerWorkspace.includes('PDF и предпросмотр')&&offerWorkspace.includes('Оформление PDF')&&offerWorkspace.includes('data-offer-gallery-slot'),'offer workspace separates preview/editor/templates and exposes two gallery uploads');
check(offerWorkspace.includes('SunClassicOfferPDFV1767')&&offerWorkspace.includes('finalGallery=galleryFor'),'custom gallery is injected into PDF renderer');
check(releaseManifest.offerWorkspaceTabs===true&&releaseManifest.offerTemplatesSeparateTab===true&&releaseManifest.offerTwoCustomGalleryPhotos===true,'release manifest records offer workspace changes');
check(pkg.version==='17.7.3','package version is v17.7.3');
check(index.includes('20260909-v17-7-3-clients-server-read'),'index cache bust is v17.7.3');
check(sw.includes('v81-20260912-account-center-loader')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js'),'PWA caches v17.7.3 client foundation modules');
check(fs.existsSync(path.join(root,'public/core/data-layer-v1773.js'))&&fs.existsSync(path.join(root,'public/core/server-automation-v1770.js')),'data layer and server automation modules exist');
check(ux.includes('CateriumServerAutomationV1770?.enabled'),'cloud browser auto completion is disabled when server automation is active');
check(runtime.includes("const VERSION = '17.7.3'")&&runtime.includes("v17.7.3 Clients Server Read"),'stability logger reports v17.7.3');
check(fs.existsSync(path.join(root,'ops/sql/SUPABASE-V17.7.0-SERVER-ORDER-AUTOMATION.sql'))&&fs.existsSync(path.join(root,'ops/sql/SUPABASE-V17.7.0-ERROR-TELEMETRY-HYGIENE.sql')),'v17.7.0 server migrations are versioned');
check(dataLayer.includes("const VERSION='17.7.3'")&&dataLayer.includes('catalogRepo')&&dataLayer.includes('clientsRepo')&&dataLayer.includes('CateriumDataV1772=api'),'v17.7.3 data layer exposes order/catalog/client repositories with compatibility aliases');
check(legacy.includes("persistOrders('order.save')")&&legacy.includes("persistOrders('order.status')")&&legacy.includes("persistOrders('order.delete')")&&legacy.includes("persistCatalog('catalog.save')")&&legacy.includes("persistCatalog('catalog.delete')"),'base legacy CRUD routes through data layer');
check(releaseManifest.dataLayerPhase===4&&releaseManifest.clientDataLayer===true&&releaseManifest.clientLegacyDoubleWrite===true&&releaseManifest.clientServerRead===true&&releaseManifest.clientLegacyFallback===true,'release manifest records client server-read data layer phase 4');
check(dataLayer.includes('sun_v17_clients_snapshot_v1773')&&dataLayer.includes('refreshServerClients')&&dataLayer.includes('compareClientSources'),'v17.7.3 client server snapshot and diagnostics are versioned');
check(fs.existsSync(path.join(root,'ops/sql/SUPABASE-V17.7.3-CLIENTS-SERVER-READ.sql')),'v17.7.3 client server-read migration is versioned');
if(bad)process.exit(1);