From b2888950cdd2d948c15acf0004a0cdc91eeb70e6 Mon Sep 17 00:00:00 2001 From: Ochenstarik Date: Fri, 21 Aug 2026 08:44:34 +0700 Subject: [PATCH] docs(report): update Phase 2.4 report with UNVERIFIED and migration details and fix test typechecks --- .../done/TASK-2026-08-20-seed-precommit.md | 76 ++++++++++++------- tests/concurrency-draw-100.test.ts | 2 + .../concurrency-participants-snapshot.test.ts | 2 + ...ffective-capabilities-truthfulness.test.ts | 36 ++------- tests/fisher-yates.test.ts | 1 + tests/giveaway-listing-idor.test.ts | 2 + tests/origin-and-csrf-gate.test.ts | 12 +-- tests/payload-summary-regression.test.ts | 1 + tests/provider-safety.test.ts | 2 +- tests/seed-precommit-gate.test.ts | 3 + tests/snapshot-seed-atomicity.test.ts | 2 + tests/storage-driver.test.ts | 6 ++ tests/token-refresh-correctness-gate.test.ts | 3 +- tests/token-vault.test.ts | 8 +- tests/winner-count-contract.test.ts | 4 + 15 files changed, 90 insertions(+), 70 deletions(-) diff --git a/agents/antigravity/done/TASK-2026-08-20-seed-precommit.md b/agents/antigravity/done/TASK-2026-08-20-seed-precommit.md index 0e8f684..ec9d010 100644 --- a/agents/antigravity/done/TASK-2026-08-20-seed-precommit.md +++ b/agents/antigravity/done/TASK-2026-08-20-seed-precommit.md @@ -2,7 +2,8 @@ **Date:** 2026-08-20 **Base Commit SHA:** `9927e74421223135a170de640255803ab513fd48` -**Status:** COMPLETED / PASS +**Result Commit SHA:** `78151572bd2ae01645d70a0768c6ece517e2cab0` +**Status:** IMPLEMENTED / READY FOR INDEPENDENT RE-REVIEW **Assigned Agent:** Antigravity (Implementation Orchestrator) --- @@ -12,9 +13,9 @@ Закрыта критическая уязвимость манипуляции результатами розыгрышей (**Seed Grinding / Pre-computation attack**), при которой организатор мог локально перебрать seed'ы на открытом списке участников и передать в `POST /api/giveaways/[id]/draw` подобранный seed, гарантирующий победу нужного участника при успешном статусе верификации `verified: true`. Реализована схема **Cryptographic Seed Pre-Commitment**: -1. Клиентский `seed` полностью исключён из входных схем (`createGiveawaySchema`, `executeDrawSchema`). Попытка передать `seed` в теле запроса строго отклоняется со статусом `400 VALIDATION_ERROR`. -2. Seed генерируется на сервере исключительно через CSPRNG (`crypto.randomBytes(16).toString('hex')`) в момент создания и блокировки неизменяемого слепка участников (`createAndLockSnapshot`) и сохраняется в БД (`Giveaway.seed`) в единой атомарной транзакции. -3. До момента проведения жеребьёвки (`DRAWN`) открытый `seed` скрыт от клиента во всех публичных и приватных эндпоинтах (`POST /api/giveaways/[id]/snapshot`, `GET /api/giveaways/[id]`, `GET /api/giveaways`, `GET /api/giveaways/[id]/participants`). Клиенту отдаётся только криптографическое обязательство `seedCommitment = sha256(seed)`. +1. Клиентский `seed` полностью исключён из входных схем (`createGiveawaySchema`, `executeDrawSchema`). Попытка передать `seed` в теле запроса `POST /draw` строго отклоняется со статусом `400 VALIDATION_ERROR`. +2. Seed генерируется на сервере исключительно через CSPRNG (`generateCryptoSecureSeed()`) в момент создания и блокировки неизменяемого слепка участников (`createAndLockSnapshot`) и сохраняется в БД (`Giveaway.seed`) в единой атомарной операции. +3. До момента проведения жеребьёвки (`DRAWN`) открытый `seed` скрыт от клиента во всех эндпоинтах (`POST /api/giveaways/[id]/snapshot`, `GET /api/giveaways/[id]`, `GET /api/giveaways`, `GET /api/giveaways/[id]/participants`). Клиенту отдаётся только криптографическое обязательство `seedCommitment = sha256(seed)`. 4. Роут жеребьёвки `POST /api/giveaways/[id]/draw` читает seed строго из базы данных (`giveaway.seed`). Любой fallback на генерацию seed в роуте жеребьёвки удалён. Если seed отсутствует — возвращается `409 CONFLICT`. 5. После завершения жеребьёвки `seed` раскрывается публично (`giveaway.seed` и `drawResult.seedUsed`), позволяя любому участнику подтвердить равенство `sha256(drawResult.seedUsed) === seedCommitment` и математическую честность через независимый `GET /api/giveaways/[id]/verify`. @@ -24,10 +25,10 @@ | File | Type | Description | |------|------|-------------| -| `src/core/randomizer/hasher.ts` | Backend | Добавлена функция `computeSeedCommitment(seed: string): string` (SHA-256 hex digest). | -| `src/core/validation/giveaway-schemas.ts` | Validation | Удалено поле `seed` из `createGiveawaySchema` и `executeDrawSchema` (строгая валидация). | +| `src/core/randomizer/hasher.ts` | Core | Добавлена функция `computeSeedCommitment(seed: string): string` (SHA-256 hex digest). | +| `src/core/validation/giveaway-schemas.ts` | Validation | Удалено поле `seed` из `createGiveawaySchema` и `executeDrawSchema` (строгая `.strict()` валидация на draw). | | `src/lib/repository/giveaway-repository.ts` | Repository | Добавлено поле `seedCommitment?: string \| null` в `GiveawayWithRelations`, удален `seed` из `CreateGiveawayInput`. | -| `src/lib/repository/memory-repository.ts` | Storage Driver | Инициализация `seed: null`, атомарная генерация и фиксация `seed` + `seedCommitment` в `createAndLockSnapshot`. | +| `src/lib/repository/memory-repository.ts` | Storage Driver | Инициализация `seed: null`, генерация и фиксация `seed` + `seedCommitment` в `createAndLockSnapshot`. | | `src/lib/repository/prisma-repository.ts` | Storage Driver | Фиксация `seed` в БД внутри `$transaction` при `createAndLockSnapshot`, маппинг `seedCommitment`. | | `src/app/api/giveaways/route.ts` | API Route | Удалена передача клиентского seed при создании розыгрыша. | | `src/app/api/giveaways/[id]/snapshot/route.ts` | API Route | Возврат `seedCommitment` вместо раскрытия plaintext seed. | @@ -36,6 +37,7 @@ | `src/app/giveaways/new/page.tsx` | Frontend UI | Удалено поле ручного ввода seed из шага 4; добавлен индикатор защиты от подбора (Seed Pre-Commitment) со значением SHA-256 commitment. | | `tests/api-validation.test.ts` | Tests | Обновлены тесты валидации на строгое отклонение `seed`. | | `tests/seed-precommit-gate.test.ts` | Tests (NEW) | Комплексный adversarial & regression test suite (7 тестов). | +| `tests/storage-driver.test.ts` | Tests | Исправлен мок `IGiveawayRepository` (добавлены `listGiveawaysSummary` и `getParticipantsPaginated`). | --- @@ -53,30 +55,46 @@ --- -## 4. Verification Evidence & Test Gate +## 4. API Contract & Database Migration -Фактически выполненные команды: - -1. `npx prisma generate` → Exit code 0 (Prisma Client v5.22.0 generated). -2. `npm test` → Exit code 0 (48 test files, 280 tests passed, 0 failed). -3. `npm run lint` → Exit code 0 (Next.js ESLint passed clean). -4. `npm run build` → Exit code 0 (Production build & static generation compiled successfully). - -### Regression Tests Summary (`tests/seed-precommit-gate.test.ts`): -- `adversarial attempt to pass custom seed in draw body fails with 400 and keeps status SNAPSHOT_LOCKED` → PASS -- `grinding regression: local brute-force of 100 seeds cannot alter the pre-committed API winner` → PASS -- `draw attempt on giveaway without locked snapshot and seed returns 409 Conflict` → PASS -- `GET /api/giveaways/[id] masks seed before DRAWN and exposes seedCommitment` → PASS -- `after DRAWN, sha256(seedUsed) strictly equals seedCommitment and verify endpoint succeeds` → PASS -- `MemoryGiveawayRepository generates and locks seed during createAndLockSnapshot` → PASS -- `PrismaGiveawayRepository maps seedCommitment correctly` → PASS +- **Database Migration Required:** `NO` (Поле `Giveaway.seed` уже существует в `prisma/schema.prisma` как nullable `String?` и готово к сохранению CSPRNG seed). +- **API Contract Changes:** + - `POST /api/giveaways`: поле `seed` удалено из входящего тела (автоматически отбрасывается `.strip()`). + - `POST /api/giveaways/[id]/draw`: поле `seed` строго запрещено в теле запроса (`.strict()`), возвращает `400 VALIDATION_ERROR` при попытке передачи. + - `POST /api/giveaways/[id]/snapshot`: в ответ добавлено поле `seedCommitment: string` (SHA-256 hex от сгенерированного seed). + - `GET /api/giveaways/[id]`: в объекте `giveaway` возвращается `seedCommitment: string | null`. До статуса `DRAWN` поле `giveaway.seed` маскируется (`null`), после проведения розыгрыша раскрывается исходный `seed`. + - `GET /api/giveaways`: поле `seed` отсутствует в `GiveawaySummary` и не утекает в списках. --- -## 5. Security & Risk Assessment +## 5. Verification Evidence & Test Gate -- **CRITICAL/HIGH findings:** 0 open -- **Seed Grinding Attack:** ELIMINATED & MATHEMATICALLY PREVENTED -- **IDOR / Cross-User Access:** PRESERVED (protected by session & `requireGiveawayOwner`) -- **Secrets:** 0 leaked -- **UNVERIFIED statements:** None +Фактически выполненные команды: + +```text +npx prisma generate -> Exit code 0 (Prisma Client v5.22.0 generated) +npm test -> Exit code 0 (49 test files, 284 tests passed, 0 failed) +npm run lint -> Exit code 0 (Next.js ESLint passed clean) +npm run build -> Exit code 0 (Next.js production build compiled successfully) +npx tsc --noEmit -> Exit code 0 (Clean TypeScript check) +``` + +### Regression Tests Summary (`tests/seed-precommit-gate.test.ts`): +- `adversarial attempt to pass custom seed in draw body fails with 400 and keeps status SNAPSHOT_LOCKED` → **PASS** +- `grinding regression: local brute-force of 100 seeds cannot alter the pre-committed API winner` → **PASS** +- `draw attempt on giveaway without locked snapshot and seed returns 409 Conflict` → **PASS** +- `GET /api/giveaways/[id] masks seed before DRAWN and exposes seedCommitment` → **PASS** +- `after DRAWN, sha256(seedUsed) strictly equals seedCommitment and verify endpoint succeeds` → **PASS** +- `MemoryGiveawayRepository generates and locks seed during createAndLockSnapshot` → **PASS** +- `PrismaGiveawayRepository maps seedCommitment correctly` → **PASS** + +--- + +## 6. UNVERIFIED Assertions & Tech Debt + +1. **UNVERIFIED: Prisma integration harness with live DB:** + - В текущем тестовом сьюте все функциональные тесты выполняются с драйвером `STORAGE_DRIVER=memory`. Хотя `PrismaGiveawayRepository` полностью реализован, компилируется (`tsc --noEmit`), собирается (`npm run build`) и покрыт маппинг-тестами, его сквозное выполнение в интеграционном тесте с реальной БД PostgreSQL не автоматизировано в Vitest. + - **Рекомендация / Proposed Next Task:** Добавить тестовый сьют `tests/prisma-integration.test.ts` для запуска прогона репозитория против тестового экземпляра PostgreSQL. + +2. **CRITICAL Finding Status:** + - Исполнитель не объявляет CRITICAL finding автоматически закрытым самостоятельно. Требуется независимое re-review ревизии. diff --git a/tests/concurrency-draw-100.test.ts b/tests/concurrency-draw-100.test.ts index 938e4ec..7d67111 100644 --- a/tests/concurrency-draw-100.test.ts +++ b/tests/concurrency-draw-100.test.ts @@ -31,6 +31,7 @@ describe('100-Draw Concurrency Regression & Mixed Race', () => { postId: '100', sourceUrl: 'https://vk.com/wall-100_100', title: '100 Concurrency Test', + text: 'Test description', likesCount: 50, commentsCount: 0, repostsCount: 0, @@ -98,6 +99,7 @@ describe('100-Draw Concurrency Regression & Mixed Race', () => { postId: '200', sourceUrl: 'https://vk.com/wall-100_200', title: 'Mixed Race Test', + text: 'Test description', likesCount: 50, commentsCount: 0, repostsCount: 0, diff --git a/tests/concurrency-participants-snapshot.test.ts b/tests/concurrency-participants-snapshot.test.ts index 4a20363..efc32aa 100644 --- a/tests/concurrency-participants-snapshot.test.ts +++ b/tests/concurrency-participants-snapshot.test.ts @@ -43,6 +43,7 @@ describe('Concurrency: Participants Update vs Snapshot Lock Race', () => { postId: '1', sourceUrl: 'https://vk.com/wall-100_1', title: 'Race Test', + text: 'Test description', likesCount: 10, commentsCount: 0, repostsCount: 0, @@ -80,6 +81,7 @@ describe('Concurrency: Participants Update vs Snapshot Lock Race', () => { postId: '2', sourceUrl: 'https://vk.com/wall-100_2', title: 'Simultaneous Race Test', + text: 'Test description', likesCount: 10, commentsCount: 0, repostsCount: 0, diff --git a/tests/effective-capabilities-truthfulness.test.ts b/tests/effective-capabilities-truthfulness.test.ts index 670d61e..44a21f8 100644 --- a/tests/effective-capabilities-truthfulness.test.ts +++ b/tests/effective-capabilities-truthfulness.test.ts @@ -14,6 +14,7 @@ import { VkPrivateResourceError } from '../src/integrations/vk/vk-errors'; import { ProviderFactory } from '../src/providers/factory'; import { GiveawayStore } from '../src/lib/giveaway-store'; import { MemoryGiveawayRepository } from '../src/lib/repository/memory-repository'; +import { DEFAULT_FILTER_RULES } from '../src/core/types/giveaway'; describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { let userRepo: MemoryUserRepository; @@ -299,11 +300,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { // 6a. Create giveaway for Alice (has credentials) const gwAlice = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-101_101', - platform: 'VK', - platformOwnerId: '-101', - platformPostId: '101', - title: 'Alice Giveaway', organizerId: loggedInUser.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-101', @@ -332,11 +330,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { // 6b. Create giveaway for Charlie (no credentials) const gwCharlie = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-303_303', - platform: 'VK', - platformOwnerId: '-303', - platformPostId: '303', - title: 'Charlie Giveaway', organizerId: loggedInUserWithoutCreds.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-303', @@ -382,11 +377,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { const gw = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-555_555', - platform: 'VK', - platformOwnerId: '-555', - platformPostId: '555', - title: 'David Giveaway', organizerId: expiredUser.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-555', @@ -430,11 +422,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { const gw = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-777_777', - platform: 'VK', - platformOwnerId: '-777', - platformPostId: '777', - title: 'Eve Giveaway', organizerId: expiredNoRefreshUser.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-777', @@ -479,11 +468,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { const gw = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-888_888', - platform: 'VK', - platformOwnerId: '-888', - platformPostId: '888', - title: 'Frank Giveaway', organizerId: legacyUser.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-888', @@ -529,11 +515,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { const gw = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-999_999', - platform: 'VK', - platformOwnerId: '-999', - platformPostId: '999', - title: 'Grace Giveaway', organizerId: legacyRefreshUser.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-999', @@ -564,11 +547,8 @@ describe('Phase 2.3.1 — Effective Capabilities Truthfulness Gate', () => { it('11. giveaway detail response never leaks token or secret fields', async () => { const gwAlice = await GiveawayStore.create({ sourceUrl: 'https://vk.com/wall-101_101', - platform: 'VK', - platformOwnerId: '-101', - platformPostId: '101', - title: 'Alice Giveaway', organizerId: loggedInUser.id, + filterRules: DEFAULT_FILTER_RULES, post: { platform: 'VK', ownerId: '-101', diff --git a/tests/fisher-yates.test.ts b/tests/fisher-yates.test.ts index cc918c3..9ddea18 100644 --- a/tests/fisher-yates.test.ts +++ b/tests/fisher-yates.test.ts @@ -27,6 +27,7 @@ describe('True Partial Fisher-Yates (HMAC_SHA256_FY_V1)', () => { version: 1, createdAt: '2026-08-17T12:00:00.000Z', eligibleParticipants: participants, + filterRulesSnapshot: DEFAULT_FILTER_RULES, participantCount: size, participantsSnapshotHash: computeParticipantsSnapshotHash(participants), conditionsHash: computeConditionsHash(DEFAULT_FILTER_RULES), diff --git a/tests/giveaway-listing-idor.test.ts b/tests/giveaway-listing-idor.test.ts index 8a94e11..3588789 100644 --- a/tests/giveaway-listing-idor.test.ts +++ b/tests/giveaway-listing-idor.test.ts @@ -35,6 +35,7 @@ describe('Phase 2.2.3 Claude PoC: GET /api/giveaways IDOR & Scoped Query Gate', postId: '100', sourceUrl: 'https://vk.com/wall-10_100', title: 'Secret Giveaway of Alice', + text: 'Text', likesCount: 10, commentsCount: 2, repostsCount: 0, @@ -52,6 +53,7 @@ describe('Phase 2.2.3 Claude PoC: GET /api/giveaways IDOR & Scoped Query Gate', postId: '200', sourceUrl: 'https://vk.com/wall-20_200', title: 'Confidential Giveaway of Bob', + text: 'Text', likesCount: 50, commentsCount: 15, repostsCount: 5, diff --git a/tests/origin-and-csrf-gate.test.ts b/tests/origin-and-csrf-gate.test.ts index 84a0d32..1f07bf3 100644 --- a/tests/origin-and-csrf-gate.test.ts +++ b/tests/origin-and-csrf-gate.test.ts @@ -18,21 +18,21 @@ describe('Phase 2.2.3 Origin, CSRF Trusted Host & Rate Limiting Gate', () => { describe('Production Base URL & VK_REDIRECT_URI Fail-Fast Policy', () => { it('fails fast in production when APP_BASE_URL is missing', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; delete process.env.APP_BASE_URL; expect(() => getAppBaseUrl()).toThrow(/APP_BASE_URL environment variable is strictly required in production/i); }); it('fails fast in production when APP_BASE_URL is not HTTPS', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.APP_BASE_URL = 'http://insecure-http-url.com'; expect(() => getAppBaseUrl()).toThrow(/must be a valid HTTPS URL in production/i); }); it('fails fast in production when VK_REDIRECT_URI is missing', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.APP_BASE_URL = 'https://randomayzer.org'; delete process.env.VK_REDIRECT_URI; @@ -40,7 +40,7 @@ describe('Phase 2.2.3 Origin, CSRF Trusted Host & Rate Limiting Gate', () => { }); it('accepts valid HTTPS configuration in production', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.APP_BASE_URL = 'https://randomayzer.org'; process.env.VK_REDIRECT_URI = 'https://randomayzer.org/api/auth/vk/callback'; @@ -51,7 +51,7 @@ describe('Phase 2.2.3 Origin, CSRF Trusted Host & Rate Limiting Gate', () => { describe('CSRF Trusted Host & Host-Spoofing Immunity', () => { it('rejects attacker sending evil Origin even if attacker injects spoofed X-Forwarded-Host', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.APP_BASE_URL = 'https://trusted-randomayzer.org'; const req = new NextRequest('http://localhost/api/auth/logout', { @@ -78,7 +78,7 @@ describe('Phase 2.2.3 Origin, CSRF Trusted Host & Rate Limiting Gate', () => { }); it('accepts valid origin matching configured trusted host', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.APP_BASE_URL = 'https://trusted-randomayzer.org'; const req = new NextRequest('https://trusted-randomayzer.org/api/auth/logout', { diff --git a/tests/payload-summary-regression.test.ts b/tests/payload-summary-regression.test.ts index 65a1932..6a7ee76 100644 --- a/tests/payload-summary-regression.test.ts +++ b/tests/payload-summary-regression.test.ts @@ -27,6 +27,7 @@ describe('POST /participants Payload Summary Regression Test', () => { postId: '1', sourceUrl: 'https://vk.com/wall-100_1', title: 'Large Payload Test', + text: 'Test description', likesCount: 100000, commentsCount: 50000, repostsCount: 0, diff --git a/tests/provider-safety.test.ts b/tests/provider-safety.test.ts index 6dbb4f2..fdca715 100644 --- a/tests/provider-safety.test.ts +++ b/tests/provider-safety.test.ts @@ -29,7 +29,7 @@ describe('Provider Safety (No unconfigured mocks in Production)', () => { }); it('should throw DependencyUnavailableError in production when VK credentials and USE_VK_MOCK are missing', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; delete process.env.USE_VK_MOCK; delete process.env.VK_SERVICE_TOKEN; diff --git a/tests/seed-precommit-gate.test.ts b/tests/seed-precommit-gate.test.ts index cb234e0..c96900d 100644 --- a/tests/seed-precommit-gate.test.ts +++ b/tests/seed-precommit-gate.test.ts @@ -51,6 +51,7 @@ describe('Phase 2.4 — Seed Pre-Commit Gate (Seed Grinding Elimination)', () => postId: '1054', sourceUrl: 'https://vk.com/wall-22446688_1054', title: 'Fairness Test Post', + text: 'Test description', likesCount: 100, commentsCount: 0, repostsCount: 0, @@ -191,6 +192,7 @@ describe('Phase 2.4 — Seed Pre-Commit Gate (Seed Grinding Elimination)', () => postId: '1054', sourceUrl: 'https://vk.com/wall-22446688_1054', title: 'No Seed Post', + text: 'Test description', likesCount: 10, commentsCount: 0, repostsCount: 0, @@ -295,6 +297,7 @@ describe('Phase 2.4 — Seed Pre-Commit Gate (Seed Grinding Elimination)', () => postId: '1', sourceUrl: 'https://vk.com/wall-1_1', title: 'Memory Parity', + text: 'Test description', likesCount: 10, commentsCount: 0, repostsCount: 0, diff --git a/tests/snapshot-seed-atomicity.test.ts b/tests/snapshot-seed-atomicity.test.ts index cd8515f..cc0a0dc 100644 --- a/tests/snapshot-seed-atomicity.test.ts +++ b/tests/snapshot-seed-atomicity.test.ts @@ -47,6 +47,7 @@ describe('Phase 2.4.1 — Atomic Snapshot + Seed Commitment Binding', () => { postId: '789', sourceUrl: 'https://vk.com/wall-33445566_789', title: 'Atomic Snapshot Test Post', + text: 'Test description', likesCount: 50, commentsCount: 0, repostsCount: 0, @@ -70,6 +71,7 @@ describe('Phase 2.4.1 — Atomic Snapshot + Seed Commitment Binding', () => { postId: '1', sourceUrl: 'https://vk.com/wall-1_1', title: 'Parity Test', + text: 'Test description', likesCount: 50, commentsCount: 0, repostsCount: 0, diff --git a/tests/storage-driver.test.ts b/tests/storage-driver.test.ts index a93158f..23a3565 100644 --- a/tests/storage-driver.test.ts +++ b/tests/storage-driver.test.ts @@ -40,6 +40,12 @@ describe('Storage Driver Policy & No Silent Fallback', () => { listGiveaways: async () => { throw new Error('Database connection failed'); }, + listGiveawaysSummary: async () => { + throw new Error('Database connection failed'); + }, + getParticipantsPaginated: async () => { + throw new Error('Database connection failed'); + }, updateStatus: async () => { throw new Error('DB error'); }, saveParticipants: async () => { throw new Error('DB error'); }, createAndLockSnapshot: async () => { throw new Error('DB error'); }, diff --git a/tests/token-refresh-correctness-gate.test.ts b/tests/token-refresh-correctness-gate.test.ts index fac8eb9..29e6f74 100644 --- a/tests/token-refresh-correctness-gate.test.ts +++ b/tests/token-refresh-correctness-gate.test.ts @@ -138,7 +138,7 @@ describe('Phase 2.3.1 — Token Refresh Correctness Gate', () => { callCount++; await new Promise(r => setTimeout(r, 20)); const res = await orig(p); - delete res.user_id; + delete (res as any).user_id; return res; }; @@ -323,7 +323,6 @@ describe('Phase 2.3.1 — Token Refresh Correctness Gate', () => { const provider = new VkProvider('svc_token', mockClient as any, mockResolver as any); const results = await provider.fetchParticipants({ - platform: 'VK', ownerId: '-123', postId: '456', organizerId: 'org1', diff --git a/tests/token-vault.test.ts b/tests/token-vault.test.ts index 8437519..4c6b2d0 100644 --- a/tests/token-vault.test.ts +++ b/tests/token-vault.test.ts @@ -13,21 +13,21 @@ describe('Phase 2.2.1 Token Vault Security & Fail-Fast Policies', () => { }); it('fails fast with fatal configuration error in production when TOKEN_ENCRYPTION_KEY is missing', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; delete process.env.TOKEN_ENCRYPTION_KEY; expect(() => new AesGcmTokenVault()).toThrow(/TOKEN_ENCRYPTION_KEY environment variable is strictly required in production/i); }); it('fails fast in production when TOKEN_ENCRYPTION_KEY is shorter than 32 characters', () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.TOKEN_ENCRYPTION_KEY = 'too-short-key'; expect(() => new AesGcmTokenVault()).toThrow(/must be at least 32 characters long in production/i); }); it('successfully initializes and encrypts/decrypts with valid 32+ character key in production', async () => { - process.env.NODE_ENV = 'production'; + (process.env as any).NODE_ENV = 'production'; process.env.TOKEN_ENCRYPTION_KEY = 'a-super-secret-production-encryption-key-32chars!'; const vault = new AesGcmTokenVault(); @@ -42,7 +42,7 @@ describe('Phase 2.2.1 Token Vault Security & Fail-Fast Policies', () => { }); it('allows explicit dev/test key when in development or test environment', async () => { - process.env.NODE_ENV = 'test'; + (process.env as any).NODE_ENV = 'test'; delete process.env.TOKEN_ENCRYPTION_KEY; const vault = new AesGcmTokenVault(); diff --git a/tests/winner-count-contract.test.ts b/tests/winner-count-contract.test.ts index 87f7d4c..ff178f4 100644 --- a/tests/winner-count-contract.test.ts +++ b/tests/winner-count-contract.test.ts @@ -43,6 +43,7 @@ describe('Winner Count Contract & Draw Retry Invariants', () => { postId: '1', sourceUrl: 'https://vk.com/wall-100_1', title: 'Title', + text: 'Test description', likesCount: 3, commentsCount: 0, repostsCount: 0, @@ -79,6 +80,7 @@ describe('Winner Count Contract & Draw Retry Invariants', () => { postId: '2', sourceUrl: 'https://vk.com/wall-100_2', title: 'Title', + text: 'Test description', likesCount: 3, commentsCount: 0, repostsCount: 0, @@ -115,6 +117,7 @@ describe('Winner Count Contract & Draw Retry Invariants', () => { postId: '3', sourceUrl: 'https://vk.com/wall-100_3', title: 'Title', + text: 'Test description', likesCount: 3, commentsCount: 0, repostsCount: 0, @@ -149,6 +152,7 @@ describe('Winner Count Contract & Draw Retry Invariants', () => { postId: '4', sourceUrl: 'https://vk.com/wall-100_4', title: 'Title', + text: 'Test description', likesCount: 3, commentsCount: 0, repostsCount: 0,