// Live check of the offline sync branch (5ee7517) on a real PostgreSQL 17. import { createRequire } from 'node:module'; import { spawn } from 'node:child_process'; import { randomBytes } from 'node:crypto'; const REPO = 'C:/Users/Ochenstarik/Agent_projects/krona-staff-sync'; const require = createRequire(`${REPO}/package.json`); const pg = require('pg'); const { ulid } = require('ulid'); const SUPER = 'postgres://postgres:audit-superuser@127.0.0.1:55432/krona_staff_sync'; const APP = 'postgres://krona_staff_app:audit-app-password@127.0.0.1:55432/krona_staff_sync'; const PORT = 18081; const BASE = `http://127.0.0.1:${PORT}/api/v1/staff`; const TENANT = ulid(); const MANAGER_TOKEN = randomBytes(32).toString('base64url'); const TERMINAL_TOKEN = randomBytes(32).toString('base64url'); async function superQuery(sql, params = []) { const client = new pg.Client({ connectionString: SUPER }); await client.connect(); try { return await client.query(sql, params); } finally { await client.end(); } } async function call(method, path, { token, body } = {}) { const response = await fetch(`${BASE}${path}`, { method, headers: { ...(body !== undefined ? { 'content-type': 'application/json' } : {}), ...(token ? { authorization: `Bearer ${token}` } : {}), }, body: body !== undefined ? JSON.stringify(body) : undefined, }); const text = await response.text(); try { return { status: response.status, json: JSON.parse(text) }; } catch { return { status: response.status, json: text }; } } const brief = (value) => { const text = typeof value === 'string' ? value : JSON.stringify(value); return text.length > 260 ? `${text.slice(0, 260)}…` : text; }; // S9: deleting an org unit referenced by a sync device const s9Unit = ulid(); const s9Device = ulid(); await superQuery(`INSERT INTO staff_org_units (id, tenant_id, code, name) VALUES ($1, $2, 's9', 'S9')`, [ s9Unit, TENANT, ]); const deviceColumns = ( await superQuery( `SELECT column_name, is_nullable, column_default FROM information_schema.columns WHERE table_name = 'staff_sync_devices' ORDER BY ordinal_position`, ) ).rows; const values = { id: s9Device, tenant_id: TENANT, device_code: 's9-device', name: 'S9', token_hash: 'a'.repeat(64), token_hint: 'hint', org_unit_id: s9Unit, created_by: ulid(), status: 'active', }; const insertable = deviceColumns.filter( (column) => values[column.column_name] !== undefined, ); await superQuery( `INSERT INTO staff_sync_devices (${insertable.map((c) => c.column_name).join(', ')}) VALUES (${insertable.map((_, index) => `$${index + 1}`).join(', ')})`, insertable.map((column) => values[column.column_name]), ).catch((error) => console.log(`S9 setup: ${error.code} ${error.message}`)); try { await superQuery(`DELETE FROM staff_org_units WHERE id = $1`, [s9Unit]); console.log('[S9] не подтверждено — точку с привязанным устройством удалить удалось'); } catch (error) { console.log(`[S9] ПОДТВЕРЖДЕНО — удаление точки с устройством → ${error.code} ${error.message}`); } // Seed an employee and a unit for S1 const orgUnit = ulid(); const employee = ulid(); await superQuery( `INSERT INTO staff_org_units (id, tenant_id, code, name) VALUES ($1, $2, 'sync-unit', 'Точка')`, [orgUnit, TENANT], ); await superQuery( `INSERT INTO staff_employees (id, tenant_id, personnel_number, display_name, status) VALUES ($1, $2, 'S-001', 'Сотрудник Синхронизации', 'active')`, [employee, TENANT], ); const server = spawn(process.execPath, ['node_modules/tsx/dist/cli.mjs', 'src/server.ts'], { cwd: REPO, env: { ...process.env, NODE_ENV: 'development', HOST: '127.0.0.1', PORT: String(PORT), LOG_LEVEL: 'silent', STAFF_RUNTIME_MODE: 'standalone', STANDALONE_ACCESS: 'employee', STANDALONE_MANAGER_TOKEN: MANAGER_TOKEN, STANDALONE_TERMINAL_TOKEN: TERMINAL_TOKEN, DATABASE_URL: APP, STANDALONE_TENANT_ID: TENANT, STANDALONE_ACTOR_ID: ulid(), }, stdio: 'ignore', }); try { for (let attempt = 0; attempt < 120; attempt += 1) { try { if ((await fetch(`${BASE}/live`)).ok) break; } catch {} await new Promise((resolve) => setTimeout(resolve, 500)); } const registered = await call('POST', '/sync/devices', { token: MANAGER_TOKEN, body: { device_code: 'kiosk-1', name: 'Киоск', org_unit_id: orgUnit }, }); const deviceToken = registered.json?.token; console.log(`device registration → ${registered.status}; scopes ${JSON.stringify(registered.json?.device?.allowed_scopes)}`); const forged = await call('POST', '/sync/push', { token: deviceToken, body: { commands: [ { command_type: 'upsert_work_session', schema_version: 1, idempotency_key: `forge-${Date.now()}`, correlation_id: ulid(), created_at: new Date().toISOString(), payload: { employee_id: employee, org_unit_id: orgUnit, business_date: '2026-09-15', worked_minutes: 2880, status: 'confirmed', }, }, { command_type: 'record_attendance', schema_version: 1, idempotency_key: `old-${Date.now()}`, correlation_id: ulid(), created_at: new Date().toISOString(), payload: { employee_id: employee, event_type: 'check_in', occurred_at: '2020-01-01T00:00:00Z', }, }, ], }, }); console.log(`device push → ${forged.status} ${brief(forged.json)}`); const session = await superQuery( `SELECT status, worked_minutes, confirmed_by, correction_note FROM staff_work_sessions WHERE tenant_id = $1 AND employee_id = $2 AND business_date = '2026-09-15'`, [TENANT, employee], ); const oldEvent = await superQuery( `SELECT occurred_at FROM staff_attendance_events WHERE tenant_id = $1 AND employee_id = $2 AND occurred_at < '2021-01-01'`, [TENANT, employee], ); const audit = await superQuery( `SELECT count(*)::int AS entries FROM staff_audit_log WHERE tenant_id = $1`, [TENANT], ); const row = session.rows[0]; console.log( `[S1] ${row?.status === 'confirmed' ? 'ПОДТВЕРЖДЕНО' : 'не подтверждено'} — смена от устройства: статус ${row?.status}, минут ${row?.worked_minutes}, подтвердил ${row?.confirmed_by ?? 'никто'}, записей в журнале аудита арендатора: ${audit.rows[0].entries}`, ); console.log( `[S2] ${oldEvent.rowCount > 0 ? 'ПОДТВЕРЖДЕНО' : 'не подтверждено'} — отметка с временем 2020-01-01 через синхронизацию сохранена: ${oldEvent.rowCount > 0}`, ); // The forged session flows into payroll (outbox default only in this throwaway database, see N2) await superQuery('ALTER TABLE staff_outbox ALTER COLUMN occurred_at SET DEFAULT now()'); try { const rules = await call('GET', '/pay-rules', { token: MANAGER_TOKEN }); const hourly = (rules.json?.pay_rules ?? []).find((rule) => /hour/.test(rule.code)); await call('PUT', `/employees/${employee}/pay-assignment`, { token: MANAGER_TOKEN, body: { pay_rule_id: hourly?.id, org_unit_id: orgUnit, rate_minor: 30000, effective_from: '2026-09-01' }, }); const run = await call('POST', '/payroll/calculate', { token: MANAGER_TOKEN, body: { month: '2026-09' } }); const total = run.json?.total_accrued_minor ?? run.json?.totals?.accrued_minor; console.log( `[S1→payroll] расчёт сентября → ${run.status}; начислено по поддельной смене: ${total !== undefined ? Number(total) / 100 : brief(run.json)} ₽ (48 ч × 300 ₽)`, ); } finally { await superQuery('ALTER TABLE staff_outbox ALTER COLUMN occurred_at DROP DEFAULT'); } } finally { server.kill(); }