test(createEntityStore): cover createEntityStore helper with unit tests

This commit is contained in:
Ilia Mashkov
2026-02-18 20:19:26 +03:00
parent ff71d1c8c9
commit 206e609a2d

View File

@@ -0,0 +1,420 @@
import {
beforeEach,
describe,
expect,
it,
} from 'vitest';
import {
type Entity,
EntityStore,
createEntityStore,
} from './createEntityStore.svelte';
interface TestEntity {
id: string;
name: string;
value: number;
}
describe('createEntityStore', () => {
describe('Construction and Initialization', () => {
it('should create an empty store when no initial entities are provided', () => {
const store = createEntityStore<TestEntity>();
expect(store.all).toEqual([]);
});
it('should create a store with initial entities', () => {
const initialEntities: TestEntity[] = [
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
];
const store = createEntityStore(initialEntities);
expect(store.all).toHaveLength(2);
expect(store.all).toEqual(initialEntities);
});
it('should create EntityStore instance', () => {
const store = createEntityStore<TestEntity>();
expect(store).toBeInstanceOf(EntityStore);
});
});
describe('Selectors', () => {
let store: EntityStore<TestEntity>;
let entities: TestEntity[];
beforeEach(() => {
entities = [
{ id: '1', name: 'First', value: 10 },
{ id: '2', name: 'Second', value: 20 },
{ id: '3', name: 'Third', value: 30 },
];
store = createEntityStore(entities);
});
it('should return all entities as an array', () => {
const all = store.all;
expect(all).toEqual(entities);
expect(all).toHaveLength(3);
});
it('should get a single entity by ID', () => {
const entity = store.getById('2');
expect(entity).toEqual({ id: '2', name: 'Second', value: 20 });
});
it('should return undefined for non-existent ID', () => {
const entity = store.getById('999');
expect(entity).toBeUndefined();
});
it('should get multiple entities by IDs', () => {
const entities = store.getByIds(['1', '3']);
expect(entities).toEqual([
{ id: '1', name: 'First', value: 10 },
{ id: '3', name: 'Third', value: 30 },
]);
});
it('should filter out undefined results when getting by IDs', () => {
const entities = store.getByIds(['1', '999', '3']);
expect(entities).toEqual([
{ id: '1', name: 'First', value: 10 },
{ id: '3', name: 'Third', value: 30 },
]);
expect(entities).toHaveLength(2);
});
it('should return empty array when no IDs match', () => {
const entities = store.getByIds(['999', '888']);
expect(entities).toEqual([]);
});
it('should check if entity exists by ID', () => {
expect(store.has('1')).toBe(true);
expect(store.has('999')).toBe(false);
});
});
describe('CRUD Operations - Create', () => {
it('should add a single entity', () => {
const store = createEntityStore<TestEntity>();
store.addOne({ id: '1', name: 'First', value: 1 });
expect(store.all).toHaveLength(1);
expect(store.getById('1')).toEqual({ id: '1', name: 'First', value: 1 });
});
it('should add multiple entities at once', () => {
const store = createEntityStore<TestEntity>();
store.addMany([
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
{ id: '3', name: 'Third', value: 3 },
]);
expect(store.all).toHaveLength(3);
});
it('should replace entity when adding with existing ID', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'Original', value: 1 }]);
store.addOne({ id: '1', name: 'Updated', value: 2 });
expect(store.all).toHaveLength(1);
expect(store.getById('1')).toEqual({ id: '1', name: 'Updated', value: 2 });
});
});
describe('CRUD Operations - Update', () => {
it('should update an existing entity', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'Original', value: 1 }]);
store.updateOne('1', { name: 'Updated' });
expect(store.getById('1')).toEqual({ id: '1', name: 'Updated', value: 1 });
});
it('should update multiple properties at once', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'Original', value: 1 }]);
store.updateOne('1', { name: 'Updated', value: 2 });
expect(store.getById('1')).toEqual({ id: '1', name: 'Updated', value: 2 });
});
it('should do nothing when updating non-existent entity', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'Original', value: 1 }]);
store.updateOne('999', { name: 'Updated' });
expect(store.all).toHaveLength(1);
expect(store.getById('1')).toEqual({ id: '1', name: 'Original', value: 1 });
});
it('should preserve entity when no changes are provided', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'Original', value: 1 }]);
store.updateOne('1', {});
expect(store.getById('1')).toEqual({ id: '1', name: 'Original', value: 1 });
});
});
describe('CRUD Operations - Delete', () => {
it('should remove a single entity', () => {
const store = createEntityStore<TestEntity>([
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
]);
store.removeOne('1');
expect(store.all).toHaveLength(1);
expect(store.getById('1')).toBeUndefined();
expect(store.getById('2')).toEqual({ id: '2', name: 'Second', value: 2 });
});
it('should remove multiple entities', () => {
const store = createEntityStore<TestEntity>([
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
{ id: '3', name: 'Third', value: 3 },
]);
store.removeMany(['1', '3']);
expect(store.all).toHaveLength(1);
expect(store.getById('2')).toEqual({ id: '2', name: 'Second', value: 2 });
});
it('should do nothing when removing non-existent entity', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'First', value: 1 }]);
store.removeOne('999');
expect(store.all).toHaveLength(1);
});
it('should handle empty array when removing many', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'First', value: 1 }]);
store.removeMany([]);
expect(store.all).toHaveLength(1);
});
});
describe('Bulk Operations', () => {
it('should set all entities, replacing existing', () => {
const store = createEntityStore<TestEntity>([
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
]);
store.setAll([{ id: '3', name: 'Third', value: 3 }]);
expect(store.all).toHaveLength(1);
expect(store.getById('1')).toBeUndefined();
expect(store.getById('3')).toEqual({ id: '3', name: 'Third', value: 3 });
});
it('should clear all entities', () => {
const store = createEntityStore<TestEntity>([
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
]);
store.clear();
expect(store.all).toEqual([]);
expect(store.all).toHaveLength(0);
});
});
describe('Reactivity with SvelteMap', () => {
it('should return reactive arrays', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'First', value: 1 }]);
// The all getter should return a fresh array (or reactive state)
const first = store.all;
const second = store.all;
// Both should have the same content
expect(first).toEqual(second);
});
it('should reflect changes in subsequent calls', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'First', value: 1 }]);
expect(store.all).toHaveLength(1);
store.addOne({ id: '2', name: 'Second', value: 2 });
expect(store.all).toHaveLength(2);
});
});
describe('Edge Cases', () => {
it('should handle empty initial array', () => {
const store = createEntityStore<TestEntity>([]);
expect(store.all).toEqual([]);
});
it('should handle single entity', () => {
const store = createEntityStore<TestEntity>([{ id: '1', name: 'First', value: 1 }]);
expect(store.all).toHaveLength(1);
expect(store.getById('1')).toEqual({ id: '1', name: 'First', value: 1 });
});
it('should handle entities with complex objects', () => {
interface ComplexEntity extends Entity {
id: string;
data: {
nested: {
value: string;
};
};
tags: string[];
}
const entity: ComplexEntity = {
id: '1',
data: { nested: { value: 'test' } },
tags: ['a', 'b', 'c'],
};
const store = createEntityStore<ComplexEntity>([entity]);
expect(store.getById('1')).toEqual(entity);
});
it('should handle numeric string IDs', () => {
const store = createEntityStore<TestEntity>([
{ id: '123', name: 'First', value: 1 },
{ id: '456', name: 'Second', value: 2 },
]);
expect(store.getById('123')).toEqual({ id: '123', name: 'First', value: 1 });
expect(store.getById('456')).toEqual({ id: '456', name: 'Second', value: 2 });
});
it('should handle UUID-like IDs', () => {
const uuid1 = '550e8400-e29b-41d4-a716-446655440000';
const uuid2 = '550e8400-e29b-41d4-a716-446655440001';
const store = createEntityStore<TestEntity>([
{ id: uuid1, name: 'First', value: 1 },
{ id: uuid2, name: 'Second', value: 2 },
]);
expect(store.getById(uuid1)).toEqual({ id: uuid1, name: 'First', value: 1 });
});
});
describe('Type Safety', () => {
it('should enforce Entity type with id property', () => {
// This test verifies type checking at compile time
const validEntity: TestEntity = { id: '1', name: 'Test', value: 1 };
const store = createEntityStore<TestEntity>([validEntity]);
expect(store.getById('1')).toEqual(validEntity);
});
it('should work with different entity types', () => {
interface User extends Entity {
id: string;
name: string;
email: string;
}
interface Product extends Entity {
id: string;
title: string;
price: number;
}
const userStore = createEntityStore<User>([
{ id: 'u1', name: 'John', email: 'john@example.com' },
]);
const productStore = createEntityStore<Product>([
{ id: 'p1', title: 'Widget', price: 9.99 },
]);
expect(userStore.getById('u1')?.email).toBe('john@example.com');
expect(productStore.getById('p1')?.price).toBe(9.99);
});
});
describe('Large Datasets', () => {
it('should handle large number of entities efficiently', () => {
const entities: TestEntity[] = Array.from({ length: 1000 }, (_, i) => ({
id: `id-${i}`,
name: `Entity ${i}`,
value: i,
}));
const store = createEntityStore(entities);
expect(store.all).toHaveLength(1000);
expect(store.getById('id-500')).toEqual({
id: 'id-500',
name: 'Entity 500',
value: 500,
});
});
it('should efficiently check existence in large dataset', () => {
const entities: TestEntity[] = Array.from({ length: 1000 }, (_, i) => ({
id: `id-${i}`,
name: `Entity ${i}`,
value: i,
}));
const store = createEntityStore(entities);
expect(store.has('id-999')).toBe(true);
expect(store.has('id-1000')).toBe(false);
});
});
describe('Method Chaining', () => {
it('should support chaining add operations', () => {
const store = createEntityStore<TestEntity>();
store.addOne({ id: '1', name: 'First', value: 1 });
store.addOne({ id: '2', name: 'Second', value: 2 });
store.addOne({ id: '3', name: 'Third', value: 3 });
expect(store.all).toHaveLength(3);
});
it('should support chaining update operations', () => {
const store = createEntityStore<TestEntity>([
{ id: '1', name: 'First', value: 1 },
{ id: '2', name: 'Second', value: 2 },
]);
store.updateOne('1', { value: 10 });
store.updateOne('2', { value: 20 });
expect(store.getById('1')?.value).toBe(10);
expect(store.getById('2')?.value).toBe(20);
});
});
});