SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
2.8 KB · 63 lines typescript
Raw Blame History
1import { describe, expect, it } from 'vitest';2import { PRESETS, csvCell, isPlausibleChange, parseScreenerParams, toCsv } from './screener';34describe('parseScreenerParams', () => {5  it('reads percent inputs as fractions and applies sort defaults', () => {6    const f = parseScreenerParams({ c30min: '10', spmax: '-15', ddmax: '-30', conf: '75', min: '1,000', size: '500' });7    expect(f.change30dMin).toBeCloseTo(0.1);8    expect(f.spreadMax).toBeCloseTo(-0.15);9    expect(f.drawdownMax).toBeCloseTo(-0.3);10    expect(f.confidenceMin).toBe(0.75); // "75" → 0.7511    expect(f.rivMin).toBe(1000);12    expect(f.pageSize).toBe(100); // capped13    expect(f.sort).toBe('riv');14    expect(f.dir).toBe('desc');15  });16  it('spread and drawdown sort ascending by default (most below first)', () => {17    expect(parseScreenerParams({ sort: 'spread' }).dir).toBe('asc');18    expect(parseScreenerParams({ sort: 'drawdown' }).dir).toBe('asc');19    expect(parseScreenerParams({ sort: 'spread', dir: 'desc' }).dir).toBe('desc');20    expect(parseScreenerParams({ sort: 'nonsense' }).sort).toBe('riv');21  });22  it('applies preset filters without overriding explicit values', () => {23    const f = parseScreenerParams({ preset: 'underpriced' });24    expect(f.sort).toBe('opportunity');25    expect(f.dir).toBe('asc');26    expect(f.spreadMax).toBe(-0.1);27    expect(f.confidenceMin).toBe(0.5);28    const g = parseScreenerParams({ preset: 'underpriced', conf: '0.75', sort: 'liquidity' });29    expect(g.confidenceMin).toBe(0.75);30    expect(g.sort).toBe('liquidity');31    expect(g.dir).toBe('desc');32  });33  it('every preset has a valid sort and a description', () => {34    for (const p of PRESETS) {35      expect(p.description.length).toBeGreaterThan(10);36      expect(parseScreenerParams({ preset: p.id }).sort).toBe(p.filters.sort);37    }38    expect(PRESETS.map((p) => p.id)).not.toContain('supply'); // not derivable honestly yet, see PRESETS comment39  });40});4142describe('plausibility', () => {43  it('holds back artefact-sized moves', () => {44    expect(isPlausibleChange(0.42)).toBe(true);45    expect(isPlausibleChange(-0.99)).toBe(true);46    expect(isPlausibleChange(85.17)).toBe(false); // the −8,516 % class of value47    expect(isPlausibleChange(null)).toBe(false);48  });49});5051describe('csv', () => {52  it('escapes quotes, commas and newlines', () => {53    expect(csvCell('Charizard, 1st Ed "Holo"')).toBe('"Charizard, 1st Ed ""Holo"""');54    expect(csvCell(null)).toBe('');55    expect(csvCell(12.5)).toBe('12.5');56    expect(csvCell(new Date('2026-09-11T00:00:00Z'))).toBe('2026-09-11T00:00:00.000Z');57  });58  it('writes comment lines, a header and CRLF rows', () => {59    const out = toCsv(['a', 'b'], [[1, 'x,y'], [null, 'z']], ['as_of=now']);60    expect(out).toBe('# as_of=now\r\na,b\r\n1,"x,y"\r\n,z\r\n');61  });62});63