mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
test(int): C149 — isola o advisory lock do harness e2e (vitest fora do processo Playwright)
This commit is contained in:
parent
534d5f1d02
commit
8408a15b6d
8 changed files with 62 additions and 52 deletions
|
|
@ -163,13 +163,13 @@ Estado da conexão é **derivado** (nunca armazenado), função de `oauthRefresh
|
|||
|
||||
## Débitos (triage do /simplify)
|
||||
|
||||
| ID | Achado | Score | Tipo | Destino |
|
||||
| --- | ------------------------------------------------------------------- | ----- | ------------- | ------------------------------------------------------------- |
|
||||
| S1 | Cookie HMAC — 3ª cópia (`googleCalendarOAuth` vs WebAuthn vs import) | 3 | defer_trigger | Defer: extrair `signedCampaignCookie` no 4º cookie assinado ou no próximo toque de um dos três |
|
||||
| S2 | Refresh token OAuth em texto puro no DB | 3 | defer_trigger | Defer: cifrar em repouso quando compliance/counsel exigir (o campo já é `read: false` e o precedente do repo é texto puro) |
|
||||
| S3 | Upsert do singleton sem constraint única | 2 | defer_trigger | Defer para **C150** (dono do `calendarId`): decidir constraint se twins virarem risco real |
|
||||
| S4 | Helper de env dos testes duplicado em 3 specs | 2 | cheap_polish | Descartado (barato, local; os specs estão verdes) |
|
||||
| S5 | `docToView` com 3 flags posicionais | 1 | cheap_polish | Descartado (cosmético) |
|
||||
| ID | Achado | Score | Tipo | Destino |
|
||||
| --- | -------------------------------------------------------------------- | ----- | ------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| S1 | Cookie HMAC — 3ª cópia (`googleCalendarOAuth` vs WebAuthn vs import) | 3 | defer_trigger | Defer: extrair `signedCampaignCookie` no 4º cookie assinado ou no próximo toque de um dos três |
|
||||
| S2 | Refresh token OAuth em texto puro no DB | 3 | defer_trigger | Defer: cifrar em repouso quando compliance/counsel exigir (o campo já é `read: false` e o precedente do repo é texto puro) |
|
||||
| S3 | Upsert do singleton sem constraint única | 2 | defer_trigger | Defer para **C150** (dono do `calendarId`): decidir constraint se twins virarem risco real |
|
||||
| S4 | Helper de env dos testes duplicado em 3 specs | 2 | cheap_polish | Descartado (barato, local; os specs estão verdes) |
|
||||
| S5 | `docToView` com 3 flags posicionais | 1 | cheap_polish | Descartado (cosmético) |
|
||||
|
||||
- **Já resolvido no simplify (não reabrir):** índice em `oauthConnectedAt` removido; `failure()` sem diagnóstico falso; copy do card ajustada; `Date.parse` na derivação; guarda do `parseAccessToken`; validação de escopo concedido; prefixo morto do manifest; callback grava erro só do exchange; `runAction` no dialog; classify puro `oauthErrorPatchFor`; serialização int.
|
||||
- **Explicitamente fora:** criptografia do refresh token agora; revogação remota no Google (só orientação); rate limiting nas rotas OAuth; verificação completa do app Google; aposentadoria da service account.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Client } from 'pg'
|
||||
import { afterAll, beforeAll } from 'vitest'
|
||||
|
||||
import { assertTestDatabase } from './assertTestDatabase'
|
||||
|
||||
|
|
@ -18,8 +17,11 @@ export const ADMIN_LOGIN_LOCK_KEY = 727_001
|
|||
* in parallel against one database: several google specs create/pick/delete
|
||||
* configured rows concurrently, and the loader's "configured row wins"
|
||||
* preference then resolves to ANOTHER file's row (channel/count assertions
|
||||
* read the wrong doc, snapshots land on a row being deleted). The lock
|
||||
* serializes the whole google calendar family for the duration of each file.
|
||||
* read the wrong doc, snapshots land on a row being deleted). Serialize the
|
||||
* whole google calendar family for the duration of each file with
|
||||
* `serializeGoogleCalendarSyncSpec` (`tests/helpers/googleCalendarSyncLock.ts`
|
||||
* — the vitest hooks live there so this module stays loadable by Playwright
|
||||
* fixtures, which must never pull `vitest` into the e2e process).
|
||||
*/
|
||||
export const GOOGLE_CALENDAR_SYNC_LOCK_KEY = 727_002
|
||||
|
||||
|
|
@ -46,24 +48,18 @@ export const withAdvisoryLock = async <T>(key: number, fn: () => Promise<T>): Pr
|
|||
}
|
||||
|
||||
/**
|
||||
* Holds the given advisory lock for the WHOLE spec file: `beforeAll` acquires
|
||||
* on a dedicated connection and `afterAll` releases it. Call at the top level
|
||||
* of a spec. A crashed worker drops its connection, releasing the lock.
|
||||
* Acquires the lock on a dedicated connection and returns the release
|
||||
* function. The caller owns the lifetime (a spec file holds it between its
|
||||
* `beforeAll` and `afterAll`); a crashed worker drops the connection and the
|
||||
* lock is released by Postgres.
|
||||
*/
|
||||
export const serializeSpecWithAdvisoryLock = (key: number): void => {
|
||||
let client: Client | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
assertTestDatabase(process.env.DATABASE_URL)
|
||||
client = new Client({ connectionString: process.env.DATABASE_URL })
|
||||
await client.connect()
|
||||
await client.query('SELECT pg_advisory_lock($1)', [key])
|
||||
}, 60_000)
|
||||
|
||||
afterAll(async () => {
|
||||
if (!client) return
|
||||
export const acquireAdvisoryLock = async (key: number): Promise<() => Promise<void>> => {
|
||||
assertTestDatabase(process.env.DATABASE_URL)
|
||||
const client = new Client({ connectionString: process.env.DATABASE_URL })
|
||||
await client.connect()
|
||||
await client.query('SELECT pg_advisory_lock($1)', [key])
|
||||
return async () => {
|
||||
await client.query('SELECT pg_advisory_unlock($1)', [key]).catch(() => {})
|
||||
await client.end().catch(() => {})
|
||||
client = null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
tests/helpers/googleCalendarSyncLock.ts
Normal file
29
tests/helpers/googleCalendarSyncLock.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { afterAll, beforeAll } from 'vitest'
|
||||
|
||||
import { acquireAdvisoryLock, GOOGLE_CALENDAR_SYNC_LOCK_KEY } from './advisoryLock'
|
||||
|
||||
/**
|
||||
* C149 — holds the google-calendar advisory lock for the WHOLE spec file
|
||||
* (`beforeAll` acquires on a dedicated connection, `afterAll` releases it).
|
||||
* Call at the top level of every int spec that touches the singleton
|
||||
* `googleCalendarSync` row so parallel files cannot read/write each other's
|
||||
* configured row.
|
||||
*
|
||||
* This module imports vitest hooks ON PURPOSE and must only be imported by
|
||||
* int specs: `tests/helpers/advisoryLock.ts` is also loaded by Playwright
|
||||
* fixtures, and pulling `vitest` into the e2e process breaks its matchers
|
||||
* (`Cannot redefine property: Symbol($$jest-matchers-object)`).
|
||||
*/
|
||||
export const serializeGoogleCalendarSyncSpec = (): void => {
|
||||
let release: (() => Promise<void>) | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
release = await acquireAdvisoryLock(GOOGLE_CALENDAR_SYNC_LOCK_KEY)
|
||||
}, 60_000)
|
||||
|
||||
afterAll(async () => {
|
||||
if (!release) return
|
||||
await release()
|
||||
release = null
|
||||
})
|
||||
}
|
||||
|
|
@ -7,17 +7,14 @@ import config from '@/payload.config'
|
|||
import { loadGoogleCalendarSyncConfig } from '@/utilities/googleCalendarSync'
|
||||
import { hookFilledCreateData } from '@/utilities/hookFilledData'
|
||||
|
||||
import {
|
||||
GOOGLE_CALENDAR_SYNC_LOCK_KEY,
|
||||
serializeSpecWithAdvisoryLock,
|
||||
} from '../helpers/advisoryLock'
|
||||
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
||||
import { serializeGoogleCalendarSyncSpec } from '../helpers/googleCalendarSyncLock'
|
||||
import {
|
||||
createSlowGoogleFetch,
|
||||
withValidGoogleCalendarCredential,
|
||||
} from '../helpers/googleCalendarTestKey'
|
||||
|
||||
serializeSpecWithAdvisoryLock(GOOGLE_CALENDAR_SYNC_LOCK_KEY)
|
||||
serializeGoogleCalendarSyncSpec()
|
||||
|
||||
let payload: Payload
|
||||
const campaignFixtures = installCampaignFixtures({
|
||||
|
|
|
|||
|
|
@ -24,12 +24,9 @@ import {
|
|||
recordGoogleCalendarOAuthError,
|
||||
} from '@/utilities/googleCalendarSync'
|
||||
|
||||
import {
|
||||
GOOGLE_CALENDAR_SYNC_LOCK_KEY,
|
||||
serializeSpecWithAdvisoryLock,
|
||||
} from '../helpers/advisoryLock'
|
||||
import { serializeGoogleCalendarSyncSpec } from '../helpers/googleCalendarSyncLock'
|
||||
|
||||
serializeSpecWithAdvisoryLock(GOOGLE_CALENDAR_SYNC_LOCK_KEY)
|
||||
serializeGoogleCalendarSyncSpec()
|
||||
|
||||
let payload: Payload
|
||||
|
||||
|
|
|
|||
|
|
@ -52,13 +52,10 @@ import {
|
|||
} from '@/utilities/googleCalendarOAuth'
|
||||
import { recordGoogleCalendarOAuthConnection } from '@/utilities/googleCalendarSync'
|
||||
|
||||
import {
|
||||
GOOGLE_CALENDAR_SYNC_LOCK_KEY,
|
||||
serializeSpecWithAdvisoryLock,
|
||||
} from '../helpers/advisoryLock'
|
||||
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
||||
import { serializeGoogleCalendarSyncSpec } from '../helpers/googleCalendarSyncLock'
|
||||
|
||||
serializeSpecWithAdvisoryLock(GOOGLE_CALENDAR_SYNC_LOCK_KEY)
|
||||
serializeGoogleCalendarSyncSpec()
|
||||
|
||||
let payload: Payload
|
||||
const campaignFixtures = installCampaignFixtures({
|
||||
|
|
|
|||
|
|
@ -37,14 +37,11 @@ import {
|
|||
} from '@/utilities/googleCalendarSync'
|
||||
import { hookFilledCreateData } from '@/utilities/hookFilledData'
|
||||
|
||||
import {
|
||||
GOOGLE_CALENDAR_SYNC_LOCK_KEY,
|
||||
serializeSpecWithAdvisoryLock,
|
||||
} from '../helpers/advisoryLock'
|
||||
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
||||
import { serializeGoogleCalendarSyncSpec } from '../helpers/googleCalendarSyncLock'
|
||||
import { withGoogleCalendarTestCredential } from '../helpers/googleCalendarTestKey'
|
||||
|
||||
serializeSpecWithAdvisoryLock(GOOGLE_CALENDAR_SYNC_LOCK_KEY)
|
||||
serializeGoogleCalendarSyncSpec()
|
||||
|
||||
let payload: Payload
|
||||
const campaignFixtures = installCampaignFixtures({
|
||||
|
|
|
|||
|
|
@ -10,11 +10,8 @@ import {
|
|||
import config from '@/payload.config'
|
||||
import { loadGoogleCalendarSyncConfig } from '@/utilities/googleCalendarSync'
|
||||
|
||||
import {
|
||||
GOOGLE_CALENDAR_SYNC_LOCK_KEY,
|
||||
serializeSpecWithAdvisoryLock,
|
||||
} from '../helpers/advisoryLock'
|
||||
import { installCampaignFixtures } from '../helpers/campaignFixtures'
|
||||
import { serializeGoogleCalendarSyncSpec } from '../helpers/googleCalendarSyncLock'
|
||||
import { withGoogleCalendarTestCredential } from '../helpers/googleCalendarTestKey'
|
||||
|
||||
/**
|
||||
|
|
@ -34,7 +31,7 @@ vi.mock('@/utilities/campaignActionContext', async (importOriginal) => {
|
|||
|
||||
import { getCampaignActionContext, reloadStaffActor } from '@/utilities/campaignActionContext'
|
||||
|
||||
serializeSpecWithAdvisoryLock(GOOGLE_CALENDAR_SYNC_LOCK_KEY)
|
||||
serializeGoogleCalendarSyncSpec()
|
||||
|
||||
let payload: Payload
|
||||
const campaignFixtures = installCampaignFixtures({
|
||||
|
|
|
|||
Loading…
Reference in a new issue