a11oy / packages /sequence-pipeline /src /__tests__ /wilson-ci.test.ts
betterwithage's picture
sync(space): complete build context — fix BUILD_ERROR (CTO)
518343a verified
Raw History Blame
1.74 kB
import { describe, expect, it } from 'vitest';
import { wilsonInterval } from '../wilson-ci.js';
describe('wilsonInterval — CRISPResso2 CI primitive', () => {
it('matches a published Wilson 95% interval (Newcombe 1998, k=81 / n=263)', () => {
// p ≈ 0.308; published Wilson 95% ≈ [0.255, 0.366].
const r = wilsonInterval(81, 263);
expect(r.p).toBeCloseTo(0.308, 3);
expect(r.ciLower).toBeCloseTo(0.255, 2);
expect(r.ciUpper).toBeCloseTo(0.366, 2);
});
it('stays inside [0, 1] for an edge proportion (Wald would not)', () => {
const r = wilsonInterval(0, 5);
expect(r.ciLower).toBe(0);
expect(r.ciUpper).toBeGreaterThan(0);
expect(r.ciUpper).toBeLessThan(1);
expect(r.ciUpper).toBeLessThanOrEqual(1);
});
it('degenerate trials=0 returns the widest honest interval, not NaN', () => {
const r = wilsonInterval(0, 0);
expect(r.ciLower).toBe(0);
expect(r.ciUpper).toBe(1);
});
it('p is always contained in [ciLower, ciUpper] (property)', () => {
for (let n = 1; n <= 50; n += 7) {
for (let k = 0; k <= n; k += 3) {
const r = wilsonInterval(k, n);
expect(r.p).toBeGreaterThanOrEqual(r.ciLower - 1e-12);
expect(r.p).toBeLessThanOrEqual(r.ciUpper + 1e-12);
}
}
});
it('99% interval is wider than 95% for the same data', () => {
const r95 = wilsonInterval(10, 50, '0.95');
const r99 = wilsonInterval(10, 50, '0.99');
expect(r99.ciUpper - r99.ciLower).toBeGreaterThan(r95.ciUpper - r95.ciLower);
});
it('rejects malformed inputs loudly', () => {
expect(() => wilsonInterval(-1, 10)).toThrow();
expect(() => wilsonInterval(11, 10)).toThrow();
expect(() => wilsonInterval(1, -5)).toThrow();
});
});