import test from 'node:test'; import assert from 'node:assert/strict'; import fixture from '../../../../examples/org-chart/data-expanded-large.json'; import small from '../../../../examples/org-chart/data-expanded-small.json'; import medium from '../../../../examples/org-chart/data-expanded-medium.json'; import { companyColorPresets, type CompanyId } from '../company-color-presets.js'; import { calculateSummary, createOrgChartExampleDataset, JsonOrgChartDataSource, normalizeOrgChartDataset, snapshotOrgChartDataset, validateOrgChartDataset, type OrgChartDataset, type OrgChartExampleEmployeeCount, } from './index.js'; const base = fixture as OrgChartDataset; const companies = Object.keys(companyColorPresets) as CompanyId[]; const sizes: OrgChartExampleEmployeeCount[] = [20, 100, 300]; function assertContract(dataset: OrgChartDataset) { const result = validateOrgChartDataset(dataset); assert.equal(result.valid, true, result.valid ? undefined : JSON.stringify(result.issues)); assert.equal(new Set(dataset.employees.map(employee => employee.id)).size, dataset.employees.length); assert.equal(new Set(dataset.organizations.map(org => org.id)).size, dataset.organizations.length); assert.equal(new Set(dataset.affiliations.map(affiliation => affiliation.id)).size, dataset.affiliations.length); const summary = calculateSummary(dataset).raw; assert.equal(summary.total, dataset.employees.length, 'leaders count once as employees'); const pending = normalizeOrgChartDataset(dataset).roots.map(node => ({ node, depth: 1, underCell: false })); let maximumDepth = 0; while (pending.length) { const { node, depth, underCell } = pending.pop()!; assert.ok(!(underCell && node.organization.type === 'team'), `team ${node.organization.id} must not descend from a cell`); maximumDepth = Math.max(maximumDepth, depth); pending.push(...node.children.map(child => ({ node: child, depth: depth + 1, underCell: underCell || node.organization.type === 'cell' }))); } assert.ok(maximumDepth >= 3); assert.ok(new Set(Object.values(summary.byOrganization).map(value => value.direct)).size > 1, 'unequal organization membership'); } test('generated small/medium/large fixtures follow the hierarchy, retaining team -> cell examples', () => { for (const dataset of [small, medium, fixture] as OrgChartDataset[]) assertContract(dataset); const byId = new Map(base.organizations.map(org => [org.id, org])); const cells = base.organizations.filter(org => org.type === 'cell'); assert.equal(cells.length, companies.length * 2); for (const cell of cells) assert.equal(byId.get(cell.parentId!)!.type, 'team'); }); test('hierarchy regression check rejects a team below an indirect cell ancestor', () => { const invalid = structuredClone(base); const office = invalid.organizations.find(org => org.id === 'hanmac-office-sibling')!; office.parentId = 'hanmac-cell-0'; invalid.organizations.find(org => org.id === 'hanmac-team-sibling')!.parentId = office.id; assert.throws(() => assertContract(invalid), /must not descend from a cell/); }); for (const employeeCount of sizes) { test(`total ${employeeCount} contains exactly that many unique employees across all seven preset companies`, () => { const dataset = createOrgChartExampleDataset(base, { scope: 'total', employeeCount }); assertContract(dataset); assert.equal(dataset.employees.length, employeeCount); assert.deepEqual(dataset.affiliations.map(item => item.id), companies); for (const companyId of companies) assert.ok(dataset.employees.some(employee => employee.affiliationId === companyId)); }); test(`per-company ${employeeCount} supports every company, pairs, and the full seven-company selection`, () => { for (const companyId of companies) { const dataset = createOrgChartExampleDataset(base, { scope: 'per-company', employeeCount, companyIds: [companyId] }); assertContract(dataset); assert.equal(dataset.employees.length, employeeCount); assert.ok(dataset.organizations.every(org => org.companyId === companyId)); assert.ok(dataset.employees.every(employee => employee.affiliationId === companyId)); } for (const companyIds of [['hanmac', 'saman'], companies] as CompanyId[][]) { const dataset = createOrgChartExampleDataset(base, { scope: 'per-company', employeeCount, companyIds }); assertContract(dataset); assert.equal(dataset.employees.length, employeeCount * companyIds.length); for (const id of companyIds) assert.equal(dataset.employees.filter(employee => employee.affiliationId === id).length, employeeCount); } }); } test('total company filtering preserves company allocation without redistribution or lost ancestors', () => { for (const employeeCount of sizes) { const complete = createOrgChartExampleDataset(base, { scope: 'total', employeeCount }); const selected = createOrgChartExampleDataset(base, { scope: 'total', employeeCount, companyIds: ['saman', 'hanmac'] }); assertContract(selected); assert.deepEqual(selected.employees, complete.employees.filter(employee => ['hanmac', 'saman'].includes(employee.affiliationId))); assert.deepEqual(selected.organizations.map(org => org.id), complete.organizations.filter(org => ['hanmac', 'saman'].includes(org.companyId!)).map(org => org.id)); } }); test('total 300 reuses every P5-3 large employee record and the original organization IDs', () => { const dataset = createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 300 }); const originalById = new Map(base.employees.map(employee => [employee.id, employee])); assert.equal(dataset.employees.length, base.employees.length); for (const employee of dataset.employees) assert.deepEqual(employee, originalById.get(employee.id)); assert.deepEqual(dataset.organizations, base.organizations); }); test('selections are deterministic sets and snapshots never mutate the source or another result', async () => { const before = structuredClone(base); const frozen = snapshotOrgChartDataset(base); const first = createOrgChartExampleDataset(frozen, { scope: 'per-company', employeeCount: 100, companyIds: ['saman', 'hanmac', 'saman'] }); const equivalent = createOrgChartExampleDataset(frozen, { scope: 'per-company', employeeCount: 100, companyIds: ['hanmac', 'saman'] }); assert.deepEqual(first, equivalent); assert.notEqual(first, equivalent); assert.notEqual(first.employees[0], equivalent.employees[0]); assert.ok(Object.isFrozen(first) && Object.isFrozen(first.organizations) && Object.isFrozen(first.employees[0])); assert.throws(() => { first.employees[0]!.name = 'changed'; }, TypeError); assert.deepEqual(base, before); assert.deepEqual(await new JsonOrgChartDataSource(first).load(), first); const single = createOrgChartExampleDataset(base, { scope: 'per-company', employeeCount: 100, companyIds: ['saman'] }); assert.deepEqual(single.employees, first.employees.filter(employee => employee.affiliationId === 'saman')); }); test('expansion resolves source IDs that already use the generated employee namespace', () => { const template = structuredClone(base); const employee = template.employees.find(employee => employee.affiliationId === 'hanmac' && !template.organizations.some(org => org.leaderId === employee.id))!; employee.id = 'p7-virtual-hanmac-050'; const dataset = createOrgChartExampleDataset(template, { scope: 'per-company', employeeCount: 100 }); assertContract(dataset); assert.ok(dataset.employees.some(employee => employee.id === 'p7-virtual-hanmac-050-example')); }); test('invalid scale, company IDs, empty view selection, and incomplete templates fail explicitly', () => { assert.throws(() => createOrgChartExampleDataset(base, { scope: 'invalid' as 'total', employeeCount: 20 }), /scope/); assert.throws(() => createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 99 as 20 }), /employeeCount/); assert.throws(() => createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 20, companyIds: ['unknown' as CompanyId] }), /presets/); assert.throws(() => createOrgChartExampleDataset(base, { scope: 'total', employeeCount: 20, companyIds: [] }), /empty view selection/); const incomplete = structuredClone(base); incomplete.employees = incomplete.employees.filter(employee => employee.affiliationId !== 'baron'); incomplete.organizations.filter(org => org.companyId === 'baron').forEach(org => { org.leaderId = null; }); assert.throws(() => createOrgChartExampleDataset(incomplete, { scope: 'total', employeeCount: 20 }), /all seven/); });