Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy-translation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
if [ "$status" -eq 0 ]; then
return 0
fi
printf '%s\n' "$output" | grep -Eiq "already exists|already.*${queue}"
printf '%s\n' "$output" | grep -Eiq "already exists|already taken|already.*${queue}"
}

ensure_queue capgo-translation-refresh
Expand Down
3 changes: 2 additions & 1 deletion apps/translation-worker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"private": true,
"type": "module",
"scripts": {
"check": "tsc --noEmit",
"check": "tsc --noEmit && bun run test:parser",
"test:parser": "bun run scripts/verify-parser.ts",
"test": "bun run test:real",
"test:real": "bun run scripts/verify-real-ai.ts",
"dev": "wrangler dev -c wrangler.jsonc -c ../web/wrangler.jsonc -c ../docs/wrangler.jsonc",
Expand Down
56 changes: 56 additions & 0 deletions apps/translation-worker/scripts/verify-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { __translationWorkerTest } from '../src/index'

function assert(condition: unknown, message: string): void {
if (!condition) throw new Error(message)
}

const html = `<!doctype html>
<html lang="en">
<head>
<title>Capgo - Live Updates for Capacitor Apps</title>
<script>
!function(){for(var n=0;n<o.length;n++)g(o[n]);}();
</script>
</head>
<body>
<a href="#main-content">Skip to main content</a>
<h1>Ship mobile updates instantly to every user</h1>
<svg><svg><text>Do not collect nested SVG text</text></svg><text>Do not collect outer SVG text</text></svg>
<p>Translate the paragraph after a nested skipped SVG.</p>
<script>if (current < total) console.log(current)</script>
<p>Deploy fixes and features without waiting for app store review delays.</p>
</body>
</html>`

const { parts, segments } = __translationWorkerTest.collectSegments(html)
const bodySegments = segments.filter((segment) => segment.inBody).map((segment) => segment.text)

assert(
bodySegments.some((text) => text.includes('Skip to main content')),
'Parser did not collect body text after a script with a less-than operator',
)
assert(
bodySegments.some((text) => text.includes('Ship mobile updates instantly')),
'Parser did not collect the body heading',
)
assert(
bodySegments.every((text) => !text.includes('Do not collect')),
'Parser collected text from a nested skipped SVG',
)
assert(
bodySegments.some((text) => text.includes('paragraph after a nested skipped SVG')),
'Parser did not resume body text after a nested skipped SVG',
)
assert(
bodySegments.some((text) => text.includes('Deploy fixes and features')),
'Parser did not collect the body paragraph after a skipped body script',
)

const translations = segments.map((segment) => (segment.inBody ? `FR: ${segment.text}` : segment.text))
const stats = __translationWorkerTest.bodyTranslationStats(segments, translations)
assert(stats.candidateCount > 0, 'Body translation validator found no body candidates')
assert(stats.changedCount > 0, 'Body translation validator did not detect changed body text')

const rendered = __translationWorkerTest.renderTranslatedHtml(parts, segments, translations)
assert(rendered.includes('FR: Ship mobile updates instantly to every user'), 'Renderer did not write translated body text')
assert(rendered.includes('current < total'), 'Renderer changed skipped script content')
59 changes: 53 additions & 6 deletions apps/translation-worker/scripts/verify-real-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,31 @@ type ProbePayload = {
cache?: boolean
r2?: boolean
}
page?: {
path?: string
locale?: string
segmentCount?: number
bodySegmentCount?: number
batchCount?: number
translatedBatchCount?: number
translatedSegmentCount?: number
changedCount?: number
bodyChecks?: unknown
samples?: unknown
}
translations?: unknown
error?: string
}

const WORKER_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const MODEL = process.env.TRANSLATION_REAL_TEST_MODEL || '@cf/meta/llama-3.1-8b-instruct-fast'
const TIMEOUT_MS = Number.parseInt(process.env.TRANSLATION_REAL_TEST_TIMEOUT_MS || '180000', 10)
const REQUEST_TIMEOUT_MS = Math.min(10_000, TIMEOUT_MS)
const TIMEOUT_MS = Number.parseInt(process.env.TRANSLATION_REAL_TEST_TIMEOUT_MS || '240000', 10)
const REQUEST_TIMEOUT_MS = Math.min(60_000, TIMEOUT_MS)
const LOG_LIMIT = 16_000
const WRANGLER_CONFIG = 'wrangler.real-test.jsonc'
const DEVELOPMENT_R2_BUCKET = 'capgo-translation-cache-development'
const SOURCE_TEXTS = ['Ship updates instantly', 'Pricing', 'Keep Capgo, Capacitor, code, API, SDK, CLI, npm, bun, GitHub, and Cloudflare unchanged.']
const REAL_PAGE_PROBES = ['/', '/docs/'] as const

let wranglerLog = ''

Expand Down Expand Up @@ -127,7 +140,7 @@ function assertProbePayload(payload: ProbePayload): void {
}
}

async function fetchProbe(url: string): Promise<ProbePayload> {
async function fetchJsonProbe(url: string): Promise<ProbePayload> {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS)
let response: Response
Expand All @@ -151,18 +164,49 @@ async function fetchProbe(url: string): Promise<ProbePayload> {
}

if (!response.ok) throw new Error(payload.error || `Probe returned HTTP ${response.status}`)
return payload
}

async function fetchRuntimeProbe(url: string): Promise<ProbePayload> {
const payload = await fetchJsonProbe(url)
assertProbePayload(payload)
return payload
}

async function fetchRealPageProbe(url: string, path: string): Promise<ProbePayload> {
const payload = await fetchJsonProbe(url)
if (!payload.ok) throw new Error(payload.error || `Real page probe failed for ${path}`)
if (payload.model !== MODEL) throw new Error(`Real page probe used ${payload.model || 'unknown model'} instead of ${MODEL}`)

const page = payload.page
if (!page) throw new Error(`Real page probe returned no page result for ${path}`)
if (page.path !== path) throw new Error(`Real page probe returned ${page.path || 'unknown path'} instead of ${path}`)
if (page.locale !== 'es') throw new Error(`Real page probe returned ${page.locale || 'unknown locale'} instead of es`)
if (!page.segmentCount || page.segmentCount < 1) throw new Error(`Real page probe found no segments for ${path}`)
if (!page.bodySegmentCount || page.bodySegmentCount < 1) throw new Error(`Real page probe found no body segments for ${path}`)
if (!page.batchCount || page.batchCount < 1) throw new Error(`Real page probe found no batches for ${path}`)
if (!page.translatedBatchCount || page.translatedBatchCount < 1) throw new Error(`Real page probe translated no batches for ${path}`)
if (!page.translatedSegmentCount || page.translatedSegmentCount < 1) throw new Error(`Real page probe translated no segments for ${path}`)
if (!page.changedCount || page.changedCount < 1) throw new Error(`Real page probe left ${path} untranslated`)
if (!Array.isArray(page.bodyChecks) || page.bodyChecks.length < 1) throw new Error(`Real page probe returned no translated body checks for ${path}`)
if (!Array.isArray(page.samples) || page.samples.length < 1) throw new Error(`Real page probe returned no translated samples for ${path}`)

return payload
}

async function exitedCode(process: Bun.Subprocess<'pipe', 'pipe', 'inherit'>): Promise<number | null> {
return await Promise.race([process.exited, sleep(0).then(() => null)])
}

await ensureDevelopmentBucket()

const port = await getFreePort()
const probeUrl = `http://127.0.0.1:${port}/__translation-test__/real-runtime`
const probeBaseUrl = `http://127.0.0.1:${port}`
const runtimeProbeUrl = `${probeBaseUrl}/__translation-test__/real-runtime`
const realPageProbeUrls = REAL_PAGE_PROBES.map((path) => ({
path,
url: `${probeBaseUrl}/__translation-test__/real-page?path=${encodeURIComponent(path)}&locale=es&batches=2`,
}))
const wrangler = Bun.spawn(
[
'bunx',
Expand Down Expand Up @@ -209,8 +253,11 @@ try {
if (code !== null) throw new Error(`wrangler dev exited early with code ${code}`)

try {
const payload = await fetchProbe(probeUrl)
console.log(`Real translation worker probe passed with ${payload.model}`)
const payload = await fetchRuntimeProbe(runtimeProbeUrl)
for (const probe of realPageProbeUrls) {
await fetchRealPageProbe(probe.url, probe.path)
}
console.log(`Real translation worker probe passed with ${payload.model} on ${REAL_PAGE_PROBES.join(', ')}`)
passed = true
break
} catch (error) {
Expand Down
Loading
Loading