screentinker/server/test/i18n-tokens.test.js
ScreenTinker 538f4a7b03 test(api): close #92 follow-up coverage gaps
The non-security gaps named in the public-API self-review:
- gap-fix: zone_id (playlist items) + layout_id (device PUT) accepted and returned on read,
  INCLUDING the cross-tenant rejection (the is_template OR workspace_id guard - the
  security-relevant one).
- docs serving: /openapi.yaml serves the spec, /docs returns the Redoc page.
- i18n drift-guard: apitoken.* keys have full parity across en/es/fr/de/pt (a key missing
  in one locale fails CI).
- token lifecycle branches: token-create workspace-membership validation and last_used_at
  stamping (integration), plus the must_change_password gate (unit test via the in-memory
  DB injection - cross-process WAL visibility is unreliable for that branch in-process).

119 tests total (was 108), all in the existing node --test job.

Closes #92

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 20:10:36 -05:00

31 lines
1.4 KiB
JavaScript

'use strict';
// i18n drift-guard for the public-API token UI: the apitoken.* keys must have full parity
// across all five locales. A key added to en (or any locale) without the others fails CI,
// so the Settings "API Tokens" UI can't ship a missing translation. No server needed.
const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const LOCALES = ['en', 'es', 'fr', 'de', 'pt'];
const I18N_DIR = path.join(__dirname, '..', '..', 'frontend', 'js', 'i18n');
function apitokenKeys(locale) {
const text = fs.readFileSync(path.join(I18N_DIR, locale + '.js'), 'utf8');
return new Set((text.match(/['"]apitoken\.[a-z_]+['"]/g) || []).map(s => s.replace(/['"]/g, '')));
}
test('i18n: apitoken.* keys have full parity across all 5 locales (drift fails CI)', () => {
const base = apitokenKeys('en');
assert.ok(base.size >= 20, `en should define the apitoken keys (found ${base.size})`);
for (const loc of LOCALES) {
const keys = apitokenKeys(loc);
const missing = [...base].filter(k => !keys.has(k));
const extra = [...keys].filter(k => !base.has(k));
assert.deepEqual(missing, [], `${loc}.js is missing apitoken keys present in en: ${missing.join(', ')}`);
assert.deepEqual(extra, [], `${loc}.js has apitoken keys not in en: ${extra.join(', ')}`);
}
});