/** * Tests for the motion scoring arithmetic. * * A qualification score is not a dashboard ornament: the band it lands in is * what a seller is told to do about a deal, and the row is append-only * evidence that the judgement was made. So the cases below pin the * *decisions* — the empty book, the rounding direction, and above all the band * boundaries, each of which a plausible-but-wrong implementation gets wrong by * exactly one basis point and reports no error about. */ import { strict as assert } from 'node:assert'; import { describe, it } from 'node:test'; import { DEMAND_STAGES } from '../src/ontology'; import { MOTION_BANDS, MOTION_KINDS, MOTION_KIND_DESCRIPTIONS, MOTION_KIND_LABELS, MOTION_KIND_STAGES, isArtifactStatus, isMotionKind, isMotionVisibility, motionBand, motionScoreBasisPoints, } from '../src/motion'; describe('motionScoreBasisPoints', () => { it('returns 0 for an empty framework rather than NaN', () => { // A framework with no dimensions renders somewhere. `NaN` renders as "NaN". const score = motionScoreBasisPoints([]); assert.equal(score, 0); assert.ok(!Number.isNaN(score)); }); it('returns 0 rather than throwing when every weight is zero', () => { // The naive version divides by the weight total and produces Infinity, // which `motionBand` would then clamp to "Strategic" — a deal nobody // scored recommended as the best one in the book. const score = motionScoreBasisPoints([ { id: 'fit', weight: 0, score: 4 }, { id: 'budget', weight: 0, score: 4 }, ]); assert.equal(score, 0); }); it('scores a full house at exactly the maximum', () => { assert.equal( motionScoreBasisPoints([ { id: 'fit', weight: 30, score: 4 }, { id: 'budget', weight: 70, score: 4 }, ]), 10_000, ); }); it('does not require weights to sum to 100', () => { // Two frameworks expressing the same judgement must score the same, or the // number means nothing across templates. const outOf100 = motionScoreBasisPoints([ { id: 'fit', weight: 50, score: 4 }, { id: 'budget', weight: 50, score: 2 }, ]); const outOf6 = motionScoreBasisPoints([ { id: 'fit', weight: 3, score: 4 }, { id: 'budget', weight: 3, score: 2 }, ]); assert.equal(outOf100, 7500); assert.equal(outOf6, 7500); }); it('rounds half-up rather than truncating', () => { // 10000 × 5 / (4 × 8) = 1562.5 exactly. Truncation gives 1562, which is // the whole difference between this and a naive `Math.floor` — small, and // it moves a score across a band boundary once every few hundred deals. const score = motionScoreBasisPoints([ { id: 'fit', weight: 5, score: 1 }, { id: 'budget', weight: 3, score: 0 }, ]); assert.equal(score, 1563); }); it('does not let a zero-weight dimension move the total', () => { // The case that flatters: a dimension somebody scored 4 and weighted out // of the framework must not drag the score up on its way past. const withoutIt = motionScoreBasisPoints([{ id: 'fit', weight: 10, score: 2 }]); const withIt = motionScoreBasisPoints([ { id: 'fit', weight: 10, score: 2 }, { id: 'vanity', weight: 0, score: 4 }, ]); assert.equal(withoutIt, 5000); assert.equal(withIt, withoutIt); }); it('always returns an integer inside 0–10000', () => { for (const weight of [1, 3, 7, 17, 100]) { for (const score of [0, 1, 2, 3, 4]) { const basisPoints = motionScoreBasisPoints([ { id: 'a', weight, score }, { id: 'b', weight: weight + 1, score: 4 - score }, ]); assert.ok(Number.isInteger(basisPoints), `${weight}/${score} produced a fraction`); assert.ok(basisPoints >= 0 && basisPoints <= 10_000, `${basisPoints} is out of range`); } } }); }); describe('motionBand', () => { /** * The boundaries, one assertion per edge. Inclusive at `min`: an * implementation using `>` instead of `>=` passes every other test in this * file and tells a seller to defer a deal that qualified. */ it('is inclusive at the lower edge of every band', () => { assert.equal(motionBand(3500).label, 'Not yet'); assert.equal(motionBand(5500).label, 'Qualified'); assert.equal(motionBand(7500).label, 'Strategic'); }); it('keeps the basis point below each edge in the band underneath', () => { assert.equal(motionBand(3499).label, 'Decline or defer'); assert.equal(motionBand(5499).label, 'Not yet'); assert.equal(motionBand(7499).label, 'Qualified'); }); it('covers both ends of the scale', () => { assert.equal(motionBand(0).label, 'Decline or defer'); assert.equal(motionBand(10_000).label, 'Strategic'); }); it('leaves no gap and no overlap across the whole range', () => { // Written as a sweep because a hand-edited band table is exactly the kind // of data where a typo produces a score that belongs to two bands, or none. for (let basisPoints = 0; basisPoints <= 10_000; basisPoints += 1) { const matches = MOTION_BANDS.filter( (band) => basisPoints >= band.min && basisPoints <= band.max, ); assert.equal(matches.length, 1, `${basisPoints} matched ${matches.length} bands`); } }); }); describe('the kind register', () => { it('labels and describes every kind', () => { for (const kind of MOTION_KINDS) { assert.ok(MOTION_KIND_LABELS[kind], `${kind} has no label`); assert.ok(MOTION_KIND_DESCRIPTIONS[kind], `${kind} has no description`); } }); it('maps every kind onto stages that actually exist', () => { // The stage list is the demand pipeline, not a second vocabulary. A typo // here would produce a template nothing can ever be filed against. for (const kind of MOTION_KINDS) { const stages = MOTION_KIND_STAGES[kind]; assert.ok(stages.length > 0, `${kind} serves no stage`); for (const stage of stages) { assert.ok(DEMAND_STAGES.includes(stage), `${kind} claims unknown stage ${stage}`); } } }); it('never files a kind against a closed stage', () => { // A won or lost deal has left the motion; offering to instantiate into it // is an invitation to do work against a dead deal. for (const kind of MOTION_KINDS) { for (const stage of MOTION_KIND_STAGES[kind]) { assert.ok(stage !== 'closed_won' && stage !== 'closed_lost', `${kind} serves ${stage}`); } } }); it('gives the playbook the whole live motion', () => { assert.equal(MOTION_KIND_STAGES.playbook.length, 8); }); }); describe('type guards', () => { it('accepts members and rejects near misses', () => { assert.equal(isMotionKind('playbook'), true); assert.equal(isMotionKind('Playbook'), false); assert.equal(isMotionVisibility('shared'), true); assert.equal(isMotionVisibility('public'), false, 'shared is book-wide, not public'); assert.equal(isArtifactStatus('final'), true); assert.equal(isArtifactStatus('published'), false); }); });