import { describe, expect, it } from 'vitest' import { overall, validateReport } from '@extant2000/evidence-record' import { compare, formatComparison, toReport, totalCost, type Candidate, type Comparison, } from '../src/index.js' const cmp: Comparison = { horizonYears: 3, dayRate: 800 } const build = (p: Partial = {}): Candidate => ({ name: 'in-house build', approach: 'build', costs: { buildDays: 20, operateDaysPerYear: 2 }, where: 'decision-record.md', ...p, }) describe('a prohibited option is not an expensive one', () => { // A provider whose terms forbid the intended use is not a cheap option. // No cost comparison can produce the right answer, because "buy" is not // on the table. const hostedProvider = build({ name: 'hosted provider', approach: 'buy-hosted', costs: { buildDays: 1, operateDaysPerYear: 0, cashPerYear: 600 }, blockers: [{ reason: 'terms of service prohibit the intended use', where: 'provider-terms.md' }], }) it('disqualifies before any arithmetic', () => { const r = compare([build(), hostedProvider], cmp) expect(r.disqualified.map(c => c.name)).toEqual(['hosted provider']) expect(r.viable.map(c => c.name)).toEqual(['in-house build']) }) it('reports it as not-applicable, never as a costly option', () => { const f = toReport([hostedProvider], cmp).findings[0]! expect(f.determination).toBe('not-applicable') expect(f.detail).toContain('pricing it implies it could win') }) it('would otherwise have been the cheapest — which is the point', () => { // 1 day + $600/yr = $2.6k, against build's $20.8k. Cost was never the // deciding term. const { blockers, ...unblocked } = hostedProvider expect(totalCost(unblocked as Candidate, cmp)!).toBeLessThan(totalCost(build(), cmp)!) }) it('cites the terms that disqualified it', () => { expect(formatComparison([hostedProvider], cmp, { evidence: 'full' })).toContain('provider-terms.md') }) }) describe('self-hosting a third-party tool is a build wearing a buy label', () => { const selfHosted = build({ name: 'self-hosted tool', approach: 'adopt-self-hosted', costs: { buildDays: 3, operateDaysPerYear: 6 }, }) it('reports not-assessed when the operational surface is not enumerated', () => { const f = toReport([selfHosted], cmp).findings[0]! expect(f.determination).toBe('not-assessed') expect(f.detail).toContain('taking on operating it') expect(overall(toReport([selfHosted], cmp))).toBe('not-assessed') }) it('excludes it from the ranking until it is enumerated', () => { const r = compare([build(), selfHosted], cmp) expect(r.uncostable.map(c => c.name)).toEqual(['self-hosted tool']) expect(r.viable.map(c => c.name)).toEqual(['in-house build']) }) it('costs it once the surface is listed', () => { const listed = { ...selfHosted, operationalSurface: ['backups', 'upgrades', 'capacity', 'on-call'] } const r = compare([build(), listed], cmp) expect(r.uncostable).toHaveLength(0) expect(r.viable).toHaveLength(2) }) it('does not demand a surface from a hosted buy', () => { const hosted = build({ name: 'hosted', approach: 'buy-hosted', costs: { buildDays: 1, operateDaysPerYear: 0, cashPerYear: 1200 } }) expect(toReport([hosted], cmp).findings[0]!.determination).toBe('pass') }) }) describe('the operating term is the one nobody fills in', () => { it('refuses to treat an unestimated operating cost as zero', () => { // Treating it as free is exactly how "adopt" wins on paper. const vague = build({ name: 'vague', costs: { buildDays: 5, operateDaysPerYear: null } }) expect(totalCost(vague, cmp)).toBeNull() const f = toReport([vague], cmp).findings[0]! expect(f.determination).toBe('not-assessed') expect(f.detail).toContain('how "adopt" wins on paper') }) it('costs over the horizon, not at a point', () => { // 20 build days + 2 op days/yr x 3 yrs = 26 days = $20,800 expect(totalCost(build(), cmp)).toBe(20_800) expect(totalCost(build(), { ...cmp, horizonYears: 1 })).toBe(17_600) }) it('flags when most of the cost is operating', () => { const opHeavy = build({ name: 'op-heavy', costs: { buildDays: 2, operateDaysPerYear: 10 } }) const f = toReport([opHeavy], cmp).findings[0]! expect(f.summary).toContain('% of it operating') expect(f.detail).toContain('paid every year and estimated never') }) it('includes cash cost across the horizon', () => { const paid = build({ costs: { buildDays: 0, operateDaysPerYear: 0, cashPerYear: 1000 } }) expect(totalCost(paid, cmp)).toBe(3000) }) }) describe('recommendation', () => { it('names the margin over the runner-up', () => { const r = compare([ build(), build({ name: 'cheap', costs: { buildDays: 1, operateDaysPerYear: 1 } }), ], cmp) expect(r.winner!.name).toBe('cheap') expect(r.reason).toContain('cheaper than in-house build') }) it('calls the answer provisional while anything is unestimated', () => { const r = compare([build(), build({ name: 'unknown', costs: { buildDays: null, operateDaysPerYear: null } })], cmp) expect(r.reason).toContain('this is a provisional answer') }) it('makes no recommendation when nothing can be costed', () => { const r = compare([build({ costs: { buildDays: null, operateDaysPerYear: null } })], cmp) expect(r.winner).toBeNull() expect(r.reason).toContain('unestimated') }) }) describe('conformance with the evidence-record standard', () => { it('no candidates is not-assessed, and says why', () => { expect(overall(toReport([], cmp))).toBe('not-assessed') expect(formatComparison([], cmp)) .toContain('A decision with one option considered is not a comparison') }) it('every conclusion carries a citation', () => { expect(validateReport(toReport([ build(), build({ name: 'x', approach: 'adopt-self-hosted', operationalSurface: ['backups'] }), ], cmp))).toEqual([]) }) })