mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
sectionPotential.ts parseia/valida o shard (ordem fail-closed), busca por
seção e computa o cenário aprovado — válidos = nominais, X₁ = comparecimento
− Lula − Flávio (todos os terceiros; o bulk tem 13 candidaturas) e X₂ =
aptos − Lula − Flávio — com formatadores pt-BR e o nome do PNG. O loader
server-only lê os shards por fs com cache por processo; as rotas públicas
/api/potencial/{municipios,secao} aplicam same-origin, rate-limit e zod
strict, com 404 honesto e cache de um dia. O story 1080×1920 é desenhado
por um renderer puro com contexto estrutural. 'potencial' entra nos slugs
reservados e o módulo novo no pin do top-level de utilities.
253 lines
8.5 KiB
TypeScript
253 lines
8.5 KiB
TypeScript
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
isReservedShareLinkSlug,
|
|
isValidShareLinkDestination,
|
|
isValidShareLinkSlug,
|
|
normalizeAbsoluteHttpUrl,
|
|
normalizeShareLinkDescription,
|
|
resolveLiveShareLinkDestination,
|
|
resolveShareLinkMode,
|
|
resolveShareLinkYoutubeVideoId,
|
|
SHARE_LINK_MODES,
|
|
SHARE_LINK_RESERVED_SLUGS,
|
|
SHARE_LINK_SLUG_PATTERN,
|
|
shareLinkIcsPath,
|
|
shareLinkIcsUid,
|
|
shareLinkPath,
|
|
} from '@/lib/shareLink'
|
|
|
|
describe('shareLink slug', () => {
|
|
it('accepts lowercase kebab-case slugs', () => {
|
|
expect(isValidShareLinkSlug('plenaria-saude-15-07')).toBe(true)
|
|
expect(isValidShareLinkSlug('meet')).toBe(true)
|
|
expect(isValidShareLinkSlug('a1')).toBe(true)
|
|
})
|
|
|
|
it.each([
|
|
'Plenaria',
|
|
'plenaria_saude',
|
|
'plenaria--saude',
|
|
'-plenaria',
|
|
'plenaria-',
|
|
'',
|
|
'a/b',
|
|
'a.b',
|
|
])('rejects %s', (value) => {
|
|
expect(isValidShareLinkSlug(value)).toBe(false)
|
|
expect(SHARE_LINK_SLUG_PATTERN.test(value)).toBe(false)
|
|
})
|
|
|
|
it('flags the reserved slugs literally', () => {
|
|
expect([...SHARE_LINK_RESERVED_SLUGS].sort()).toEqual(
|
|
[
|
|
'abaixo-assinado',
|
|
'admin',
|
|
'api',
|
|
'artigo',
|
|
'artigos',
|
|
'campanha',
|
|
'cards',
|
|
'conteudos',
|
|
'corte',
|
|
'evento',
|
|
'fotos',
|
|
'jingles',
|
|
'mandato-no-whatsapp',
|
|
'noticia',
|
|
'potencial',
|
|
'privacidade',
|
|
].sort(),
|
|
)
|
|
expect(isReservedShareLinkSlug('corte')).toBe(true)
|
|
expect(isReservedShareLinkSlug('plenaria-saude')).toBe(false)
|
|
})
|
|
|
|
it('builds the single-segment public path', () => {
|
|
expect(shareLinkPath('plenaria-saude')).toBe('/plenaria-saude')
|
|
})
|
|
})
|
|
|
|
describe('shareLink destination', () => {
|
|
it.each([
|
|
'https://meet.google.com/abc-defg-hij',
|
|
'http://example.com',
|
|
'https://example.com/path?x=1#y',
|
|
])('accepts %s', (value) => {
|
|
expect(isValidShareLinkDestination(value)).toBe(true)
|
|
})
|
|
|
|
it.each([
|
|
'meet.google.com/abc',
|
|
'ftp://example.com/file',
|
|
'javascript:alert(1)',
|
|
'data:text/html,x',
|
|
'mailto:x@example.com',
|
|
'http:foo',
|
|
'https:example.com',
|
|
'https://example.com/path\nX-Injected: yes',
|
|
'',
|
|
])('rejects %s', (value) => {
|
|
expect(isValidShareLinkDestination(value)).toBe(false)
|
|
})
|
|
|
|
it('normalizes absolute HTTP URLs and rejects control characters', () => {
|
|
expect(normalizeAbsoluteHttpUrl(' https://example.com/path ')).toBe('https://example.com/path')
|
|
expect(normalizeAbsoluteHttpUrl('https://example.com/path\r\nX-Injected: yes')).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('shareLink mode (S29)', () => {
|
|
it('treats a legacy/null value as direct and lists both modes', () => {
|
|
expect(SHARE_LINK_MODES).toEqual(['direct', 'announcement'])
|
|
expect(resolveShareLinkMode(null)).toBe('direct')
|
|
expect(resolveShareLinkMode(undefined)).toBe('direct')
|
|
expect(resolveShareLinkMode('direct')).toBe('direct')
|
|
expect(resolveShareLinkMode('announcement')).toBe('announcement')
|
|
expect(resolveShareLinkMode('outro')).toBe('direct')
|
|
})
|
|
})
|
|
|
|
describe('resolveLiveShareLinkDestination (S29)', () => {
|
|
it('returns the first live row with a valid http/https URL', () => {
|
|
expect(
|
|
resolveLiveShareLinkDestination([
|
|
{ label: 'Meet', url: 'https://meet.google.com/abc', live: false },
|
|
{ label: 'YouTube', url: 'https://youtube.com/live/x', live: true },
|
|
{ label: 'Outro', url: 'https://example.com/outro', live: true },
|
|
]),
|
|
).toEqual({ href: 'https://youtube.com/live/x', label: 'YouTube' })
|
|
})
|
|
|
|
it('fails closed on empty, unflagged or invalid rows', () => {
|
|
expect(resolveLiveShareLinkDestination(null)).toBeNull()
|
|
expect(resolveLiveShareLinkDestination([])).toBeNull()
|
|
expect(resolveLiveShareLinkDestination([{ label: 'Meet', url: 'https://x.com' }])).toBeNull()
|
|
expect(
|
|
resolveLiveShareLinkDestination([{ label: 'X', url: 'javascript:alert(1)', live: true }]),
|
|
).toBeNull()
|
|
expect(resolveLiveShareLinkDestination([{ live: true, url: null }])).toBeNull()
|
|
})
|
|
|
|
it('trims the URL and tolerates a missing label', () => {
|
|
expect(resolveLiveShareLinkDestination([{ url: ' https://x.com/a ', live: true }])).toEqual({
|
|
href: 'https://x.com/a',
|
|
label: '',
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('resolveShareLinkYoutubeVideoId (S44)', () => {
|
|
it.each([
|
|
'https://www.youtube.com/live/77bUgl7cvQ8',
|
|
'https://youtube.com/live/77bUgl7cvQ8?feature=share',
|
|
'https://youtu.be/77bUgl7cvQ8',
|
|
'https://www.youtube.com/watch?v=77bUgl7cvQ8',
|
|
'https://m.youtube.com/watch?v=77bUgl7cvQ8&t=10s',
|
|
'https://www.youtube.com/embed/77bUgl7cvQ8',
|
|
'https://www.youtube-nocookie.com/embed/77bUgl7cvQ8',
|
|
'https://www.youtube.com/shorts/77bUgl7cvQ8',
|
|
])('extracts the id from %s', (url) => {
|
|
expect(resolveShareLinkYoutubeVideoId([{ label: 'YouTube', url }])).toBe('77bUgl7cvQ8')
|
|
})
|
|
|
|
it('skips invalid rows and picks the first valid YouTube destination', () => {
|
|
expect(
|
|
resolveShareLinkYoutubeVideoId([
|
|
{ label: 'Meet', url: 'https://meet.google.com/abc-defg-hij' },
|
|
{ label: 'YouTube', url: 'https://www.youtube.com/live/77bUgl7cvQ8' },
|
|
{ label: 'Outro', url: 'https://youtu.be/AAAAAAAAAAA' },
|
|
]),
|
|
).toBe('77bUgl7cvQ8')
|
|
})
|
|
|
|
it('fails closed without a valid YouTube id', () => {
|
|
expect(resolveShareLinkYoutubeVideoId(null)).toBeNull()
|
|
expect(resolveShareLinkYoutubeVideoId([])).toBeNull()
|
|
expect(
|
|
resolveShareLinkYoutubeVideoId([{ label: 'Meet', url: 'https://meet.google.com/x' }]),
|
|
).toBeNull()
|
|
expect(
|
|
resolveShareLinkYoutubeVideoId([{ label: 'Y', url: 'https://youtu.be/curto' }]),
|
|
).toBeNull()
|
|
expect(resolveShareLinkYoutubeVideoId([{ label: 'Y', url: 'javascript:alert(1)' }])).toBeNull()
|
|
expect(
|
|
resolveShareLinkYoutubeVideoId([
|
|
{ label: 'Y', url: 'https://notyoutube.com/live/77bUgl7cvQ8' },
|
|
]),
|
|
).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('shareLink ics contract (S29)', () => {
|
|
it('hangs the download off the slug namespace', () => {
|
|
expect(shareLinkIcsPath('plenaria-saude')).toBe('/plenaria-saude/evento.ics')
|
|
expect(shareLinkIcsUid('plenaria-saude')).toBe('plenaria-saude@teqo.jorgesolla.com.br')
|
|
})
|
|
})
|
|
|
|
describe('normalizeShareLinkDescription', () => {
|
|
it('collapses newlines and repeated spaces so the meta tag stays single-line', () => {
|
|
expect(normalizeShareLinkDescription(' Venha para a\n\nplenária de saúde ')).toBe(
|
|
'Venha para a plenária de saúde',
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('reserved slugs drift', () => {
|
|
const ROUTE_FILES = new Set(['page.tsx', 'page.ts', 'route.ts', 'route.tsx'])
|
|
|
|
/** Any route file below `dir` (dynamic segments included) makes the segment a URL. */
|
|
const hasRouteFile = (dir: string): boolean => {
|
|
if (!existsSync(dir)) return false
|
|
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
if (entry.isFile() && ROUTE_FILES.has(entry.name)) return true
|
|
if (entry.isDirectory() && hasRouteFile(path.join(dir, entry.name))) return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
const staticRouteSegments = (): string[] => {
|
|
const roots = ['src/app/(frontend)', 'src/app/(frontend)/(home)']
|
|
const segments = new Set<string>()
|
|
|
|
for (const root of roots) {
|
|
const absolute = path.resolve(process.cwd(), root)
|
|
if (!existsSync(absolute)) continue
|
|
|
|
for (const entry of readdirSync(absolute, { withFileTypes: true })) {
|
|
if (!entry.isDirectory() || entry.name.startsWith('[') || entry.name.startsWith('('))
|
|
continue
|
|
|
|
if (hasRouteFile(path.join(absolute, entry.name))) segments.add(entry.name)
|
|
}
|
|
}
|
|
|
|
return [...segments]
|
|
}
|
|
|
|
/** The post type enum is private to the server-only `posts.ts`; read it as source. */
|
|
const postTypesOnDisk = (): string[] => {
|
|
const source = readFileSync(path.resolve(process.cwd(), 'src/utilities/posts.ts'), 'utf8')
|
|
const match = source.match(/const POST_TYPES = \[([^\]]+)\]/)
|
|
if (!match?.[1]) return []
|
|
return [...match[1].matchAll(/'([^']+)'/g)].map((entry) => entry[1]!)
|
|
}
|
|
|
|
it('reserves every static route segment at the root of the public site', () => {
|
|
const missing = staticRouteSegments().filter((segment) => !isReservedShareLinkSlug(segment))
|
|
expect(missing).toEqual([])
|
|
})
|
|
|
|
it('reserves every post type value (the `[type]` branch answers them first)', () => {
|
|
const types = postTypesOnDisk()
|
|
expect(types).toEqual(['noticia', 'campanha', 'artigo', 'evento'])
|
|
|
|
const missing = types.filter((type) => !isReservedShareLinkSlug(type))
|
|
expect(missing).toEqual([])
|
|
})
|
|
})
|