Add dataset library, extract-to-dataset, and render-time reference resolution

This commit is contained in:
2026-06-05 15:49:40 +03:00
parent 25849461e0
commit a4e4d96d3b
41 changed files with 3909 additions and 19 deletions
+53
View File
@@ -0,0 +1,53 @@
import { describe, expect, test } from 'vitest';
import { isNameTaken, makeUniqueName } from './naming';
describe('isNameTaken', () => {
const datasets = [
{ id: 1, name: 'Sales' },
{ id: 2, name: 'Regions' },
];
test('matches case-insensitively', () => {
expect(isNameTaken('sales', datasets)).toBe(true);
expect(isNameTaken('SALES', datasets)).toBe(true);
expect(isNameTaken('Unknown', datasets)).toBe(false);
});
test('trims the desired name before comparing', () => {
expect(isNameTaken(' Sales ', datasets)).toBe(true);
});
test('excludeId lets a record ignore itself (e.g. a case-only rename)', () => {
expect(isNameTaken('Sales', datasets, 1)).toBe(false);
expect(isNameTaken('sales', datasets, 1)).toBe(false);
// Renaming to a name owned by a *different* record is still taken.
expect(isNameTaken('Regions', datasets, 1)).toBe(true);
});
});
describe('makeUniqueName', () => {
test('returns the trimmed desired name when free', () => {
expect(makeUniqueName('Fresh', ['Sales'])).toBe('Fresh');
expect(makeUniqueName(' Fresh ', ['Sales'])).toBe('Fresh');
});
test('suffixes collisions: Name -> Name 2 -> Name 3', () => {
expect(makeUniqueName('Name', ['Name'])).toBe('Name 2');
expect(makeUniqueName('Name', ['Name', 'Name 2'])).toBe('Name 3');
});
test('comparison is case-insensitive but casing is preserved', () => {
expect(makeUniqueName('Name', ['name'])).toBe('Name 2');
expect(makeUniqueName('MyData', ['mydata'])).toBe('MyData 2');
});
test('within-batch reservation is the caller responsibility: a single call does not reserve', () => {
// Two consecutive calls with the same existing set both yield "Name 2" —
// the caller must reserve each chosen name as it goes (arch 07 §5).
const existing = ['Name'];
expect(makeUniqueName('Name', existing)).toBe('Name 2');
expect(makeUniqueName('Name', existing)).toBe('Name 2');
// Reserving manually advances to the next free slot.
expect(makeUniqueName('Name', [...existing, 'Name 2'])).toBe('Name 3');
});
});