teqo-backup/tests/unit/testMunicipalityAllocatorConventions.unit.spec.ts
Cursor Agent 03314e620a test(miss-73): deterministic allocator guardrails in conventions spec
Recurrence prevention for the deadlock class, three rules:
1. a spec file calling getMunicipality(<arg>) while mutating
   municipalities (assign/touch/create*/payload.update municipality)
   fails the build — the class miss #73 named;
2. hardcoded municipio=<slug> deep links banned in tests/e2e —
   claimMunicipality().slug is the only source;
3. e2e name: /.../ regex literals containing a catalog municipality name
   must be ^-anchored — the literal-regex dodge of the P3-C template
   guard, keyed on the catalog so copy regexes stay untouched.
2026-07-31 20:57:17 -03:00

98 lines
4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { readdirSync, readFileSync } from 'node:fs'
import { relative, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { municipalityCatalog } from '@/lib/municipalityCatalog'
// Miss #73 (2026-07-31): int specs pinning seeded municipality slugs
// (`getMunicipality('cairu')` et al.) and mutating them deadlocked
// `municipality_rels` under the parallel suite (Postgres 40P01), and e2e specs
// deep-linking `?municipio=cairu` raced the same row from two workers. The
// allocator (`getMunicipality()` / `claimMunicipality()`, Postgres sequence)
// hands out a unique row per claim; pinning is what breaks the contract.
//
// Rule 3 is the literal-regex sibling of the anchored-template guard (P3-C):
// `new RegExp(\`…\`)` was hardened there, but `/Cairu/i` literals dodged it —
// and catalog names are not prefix-unique (Conde/Condeúba, Barra ×4, Laje ×3).
const repoRoot = process.cwd()
const repoPath = (absolute: string) => relative(repoRoot, absolute)
const walkFiles = (root: string, extensions: readonly string[]): string[] =>
readdirSync(root, { recursive: true, withFileTypes: true })
.filter((entry) => entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext)))
.map((entry) => resolve(entry.parentPath, entry.name))
const specFiles = [
...walkFiles(resolve(repoRoot, 'tests/int'), ['.ts']),
...walkFiles(resolve(repoRoot, 'tests/e2e'), ['.ts']),
]
/** `getMunicipality(…)` called with a non-empty argument (a pinned slug). */
const FIXED_SLUG_CALL = /\bgetMunicipality\s*\(\s*[^)\s]/
const MUTATION_PATTERN = new RegExp(
[
'\\b(?:assignMunicipalityAdvisors(?:Record)?|touchMunicipality|createLeadership|createVotePledge|createCampaignDemand|createMunicipalityUpdate|setStateDeputyMunicipalitiesBatchRecord)\\s*\\(',
'payload\\.update\\(\\s*\\{\\s*collection:\\s*[\'"]municipality[\'"]',
].join('|'),
)
describe('municipality allocator discipline (miss #73)', () => {
it('never pins a fixed-slug municipality in a spec that mutates municipalities', () => {
const offenders: string[] = []
for (const file of specFiles) {
const source = readFileSync(file, 'utf8')
if (!FIXED_SLUG_CALL.test(source)) continue
if (!MUTATION_PATTERN.test(source)) continue
offenders.push(repoPath(file))
}
expect(
offenders,
'allocate with getMunicipality() (no slug) — read-only catalog lookups belong to getMunicipalityCatalogEntry',
).toEqual([])
})
it('never deep-links a hardcoded municipality slug in e2e specs', () => {
const offenders: string[] = []
for (const file of walkFiles(resolve(repoRoot, 'tests/e2e'), ['.ts'])) {
const lines = readFileSync(file, 'utf8').split('\n')
for (const [index, line] of lines.entries()) {
if (/municipio=[a-z0-9-]/.test(line)) offenders.push(`${repoPath(file)}:${index + 1}`)
}
}
expect(offenders, 'interpolate claimMunicipality().slug into wizard deep links').toEqual([])
})
it('anchors e2e name regexes that mention a catalog municipality', () => {
const offenders: string[] = []
const nameLiteral = /name:\s*\/([^/\n]+)\/i?\b/g
const catalogNames = municipalityCatalog
.map((entry) => entry.name)
.filter((name) => name.length >= 4)
for (const file of walkFiles(resolve(repoRoot, 'tests/e2e'), ['.ts'])) {
const source = readFileSync(file, 'utf8')
nameLiteral.lastIndex = 0
let match: RegExpExecArray | null
while ((match = nameLiteral.exec(source)) !== null) {
const pattern = match[1]!
if (pattern.startsWith('^')) continue
const hit = catalogNames.find((name) => pattern.toLowerCase().includes(name.toLowerCase()))
if (hit) {
const line = source.slice(0, match.index).split('\n').length
offenders.push(`${repoPath(file)}:${line} (unanchored, matches "${hit}")`)
}
}
}
expect(
offenders,
'anchor with ^ (or locate by href) — catalog names are not prefix-unique',
).toEqual([])
})
})