Files
decision/maquette/frontend/tests/engine/threshold.spec.ts
T
YvvandClaude Opus 5.5 67ddee28b2 v3 : socle Rust (moteur pur, accès trustinWallet, serveur) ; maquette Nuxt rangée dans maquette/
- crates/ld-engine : formule inertielle (parité avec la maquette, garde C),
  coquille / changement de fond, assistant d'édition à règles, composition
  d'un document sous vote permanent (adoptions, aperçus, rivales).
- crates/trustin-access : qui voit, qui ouvre, qui gère les accès.
- crates/ld-server : API axum + SQLite, connexion Ğ1 (client OAuth2 de
  connect.monnaie-libre.fr), invitations, entrée en vigueur automatique,
  signalements de contradiction, traductions éditoriales, publication git,
  taille de la toile lue sur la chaîne.
- seeds/acte-engagement-certification.json : 33 articles, fr + en + es ;
  en vigueur : préambule, E1, E2 (seed a minima).
- maquette/ : frontend, backend et docker v2 déplacés tels quels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 02:04:42 +02:00

175 lines
6.2 KiB
TypeScript

/**
* Ported from backend/app/tests/test_threshold.py — every pytest case
* exists here with the same expected values. Exact integer expectations
* were cross-checked by executing the Python engine (backend/.venv).
*
* Real-world reference case:
* Vote Engagement Forgeron v2.0.0 (Feb 2026)
* wotSize=7224, votesFor=97, votesAgainst=23, total=120
* params M=50, B=0.1, G=0.2 => threshold=94 => adopted (97 >= 94)
*/
import { describe, expect, it } from 'vitest'
import { smithThreshold, techcommThreshold, wotThreshold } from '../../app/engine/threshold'
// ---------------------------------------------------------------------------
// WoT threshold: real-world vote Forgeron
// ---------------------------------------------------------------------------
describe('wotThreshold — Forgeron reference case', () => {
it('forgeron vote passes (97 for out of 120 total, wot=7224)', () => {
const threshold = wotThreshold(7224, 120, 50, 0.1, 0.2)
// With low participation (120/7224 ~ 1.66%), near-unanimity is required.
// The historical threshold was 94, and 97 >= 94.
expect(97).toBeGreaterThanOrEqual(threshold)
// The threshold should be high relative to total votes (inertia effect)
expect(threshold).toBeGreaterThan(60)
})
it('forgeron threshold value is in a reasonable range', () => {
const threshold = wotThreshold(7224, 120, 50, 0.1, 0.2)
// At ~1.66% participation, inertia should push threshold close to 78-95%
// of total votes.
expect(threshold).toBeGreaterThanOrEqual(80)
expect(threshold).toBeLessThanOrEqual(120)
})
it('forgeron threshold is exactly 94 (Python cross-check)', () => {
expect(wotThreshold(7224, 120, 50, 0.1, 0.2)).toBe(94)
})
})
// ---------------------------------------------------------------------------
// WoT threshold: low participation
// ---------------------------------------------------------------------------
describe('wotThreshold — low participation', () => {
it('10 votes out of 7224 requires near-unanimity', () => {
const threshold = wotThreshold(7224, 10, 50, 0.1, 0.2)
// With participation ratio 10/7224 ~ 0.14%, threshold should be
// very close to totalVotes (near-unanimity).
expect(threshold).toBeGreaterThanOrEqual(9)
expect(threshold).toBeLessThanOrEqual(10)
// Python cross-check: exact value
expect(threshold).toBe(9)
})
})
// ---------------------------------------------------------------------------
// WoT threshold: high participation
// ---------------------------------------------------------------------------
describe('wotThreshold — high participation', () => {
it('3000 votes out of 7224 approaches simple majority', () => {
const threshold = wotThreshold(7224, 3000, 50, 0.1, 0.2)
// With ~42% participation, the inertia factor diminishes.
const simpleMajority = Math.ceil(3000 * 0.5)
expect(threshold).toBeGreaterThanOrEqual(simpleMajority)
// Should be noticeably less than near-unanimity
expect(threshold).toBeLessThan(2700)
// Python cross-check: exact value
expect(threshold).toBe(1742)
})
})
// ---------------------------------------------------------------------------
// WoT threshold: edge cases
// ---------------------------------------------------------------------------
describe('wotThreshold — edge cases', () => {
it('zero total votes => ceil(C + B^W)', () => {
const threshold = wotThreshold(7224, 0, 50, 0.1, 0.2)
// B^W = 0.1^7224 is effectively 0
const expected = Math.ceil(0.0 + 0.1 ** 7224)
expect(threshold).toBe(expected)
expect(threshold).toBe(0)
})
it('throws on wotSize = 0', () => {
expect(() => wotThreshold(0, 10)).toThrow(/wotSize/)
})
it('throws on negative totalVotes', () => {
expect(() => wotThreshold(100, -1)).toThrow(/totalVotes/)
})
it('throws on majorityPct out of range', () => {
expect(() => wotThreshold(100, 10, 150)).toThrow(/majorityPct/)
})
})
// ---------------------------------------------------------------------------
// WoT threshold: Python↔TS parity (values executed from backend/.venv)
// ---------------------------------------------------------------------------
describe('wotThreshold — Python parity cases', () => {
it('T > W (100 wot, 150 votes) => 69', () => {
expect(wotThreshold(100, 150, 50, 0.1, 0.2)).toBe(69)
})
it('T = W (full participation) => simple majority 50', () => {
expect(wotThreshold(100, 100, 50, 0.1, 0.2)).toBe(50)
})
it('constantBase C=10 on Forgeron numbers => 96', () => {
expect(wotThreshold(7224, 120, 50, 0.1, 0.2, 10.0)).toBe(96)
})
it('majorityPct 80 on Forgeron numbers => 110', () => {
expect(wotThreshold(7224, 120, 80, 0.1, 0.2)).toBe(110)
})
it('minimal corpus W=1, T=1 => 1', () => {
expect(wotThreshold(1, 1, 50, 0.1, 0.2)).toBe(1)
})
it('W=10, T=5 => 3', () => {
expect(wotThreshold(10, 5, 50, 0.1, 0.2)).toBe(3)
})
})
// ---------------------------------------------------------------------------
// Smith threshold
// ---------------------------------------------------------------------------
describe('smithThreshold — ceil(smithSize ^ S)', () => {
it('smithSize=20, exponent=0.1 => ceil(20^0.1) = 2', () => {
const result = smithThreshold(20, 0.1)
expect(result).toBe(Math.ceil(20 ** 0.1))
// 20^0.1 ~ 1.35, ceil => 2
expect(result).toBe(2)
})
it('smithSize=1 => ceil(1^0.1) = 1', () => {
expect(smithThreshold(1, 0.1)).toBe(1)
})
it('throws on smithSize = 0', () => {
expect(() => smithThreshold(0)).toThrow()
})
it('smithSize=1000, exponent=0.5 => 32 (Python cross-check)', () => {
expect(smithThreshold(1000, 0.5)).toBe(32)
})
})
// ---------------------------------------------------------------------------
// TechComm threshold
// ---------------------------------------------------------------------------
describe('techcommThreshold — ceil(cotecSize ^ T)', () => {
it('cotecSize=5, exponent=0.1 => ceil(5^0.1) = 2', () => {
const result = techcommThreshold(5, 0.1)
expect(result).toBe(Math.ceil(5 ** 0.1))
// 5^0.1 ~ 1.175, ceil => 2
expect(result).toBe(2)
})
it('cotecSize=1 => 1', () => {
expect(techcommThreshold(1, 0.1)).toBe(1)
})
it('throws on cotecSize = 0', () => {
expect(() => techcommThreshold(0)).toThrow()
})
})