/**
* @fileoverview Tests for Entity base class
*/
import { describe, it, expect } from 'vitest';
import { Entity } from '../entity.js';
/**
* Test ID type
*/
type TestId = string & { readonly __brand: unique symbol };
/**
* Test implementation of Entity
*/
class TestEntity extends Entity<TestId> {
private _name: string;
private _value: number;
constructor(id: TestId, name: string, value: number) {
super(id);
this._name = name;
this._value = value;
}
get name(): string {
return this._name;
}
get value(): number {
return this._value;
}
updateName(name: string): void {
this._name = name;
}
updateValue(value: number): void {
this._value = value;
}
}
/**
* Another test entity for comparison
*/
class AnotherTestEntity extends Entity<TestId> {}
describe('Entity', () => {
const createTestId = (value: string): TestId => value as TestId;
describe('constructor', () => {
it('should create an entity with the given id', () => {
const id = createTestId('test-123');
const entity = new TestEntity(id, 'Test', 42);
expect(entity.id).toBe(id);
expect(entity.name).toBe('Test');
expect(entity.value).toBe(42);
});
});
describe('equals', () => {
it('should return true for entities with the same id', () => {
const id = createTestId('test-123');
const entity1 = new TestEntity(id, 'Test1', 42);
const entity2 = new TestEntity(id, 'Test2', 99);
expect(entity1.equals(entity2)).toBe(true);
});
it('should return false for entities with different ids', () => {
const entity1 = new TestEntity(createTestId('test-123'), 'Test', 42);
const entity2 = new TestEntity(createTestId('test-456'), 'Test', 42);
expect(entity1.equals(entity2)).toBe(false);
});
it('should return true even if entities have different state', () => {
const id = createTestId('test-123');
const entity1 = new TestEntity(id, 'Original', 1);
const entity2 = new TestEntity(id, 'Modified', 999);
expect(entity1.equals(entity2)).toBe(true);
});
it('should work with different entity types that have the same id', () => {
const id = createTestId('test-123');
const testEntity = new TestEntity(id, 'Test', 42);
const anotherEntity = new AnotherTestEntity(id);
expect(testEntity.equals(anotherEntity)).toBe(true);
expect(anotherEntity.equals(testEntity)).toBe(true);
});
it('should handle null or undefined comparisons', () => {
const entity = new TestEntity(createTestId('test-123'), 'Test', 42);
// @ts-expect-error - Testing null comparison
expect(entity.equals(null)).toBe(false);
// @ts-expect-error - Testing undefined comparison
expect(entity.equals(undefined)).toBe(false);
});
});
describe('identity', () => {
it('should maintain identity even after state changes', () => {
const id = createTestId('test-123');
const entity = new TestEntity(id, 'Original', 42);
entity.updateName('Modified');
entity.updateValue(999);
expect(entity.id).toBe(id);
expect(entity.name).toBe('Modified');
expect(entity.value).toBe(999);
});
it('should not allow id modification', () => {
const entity = new TestEntity(createTestId('test-123'), 'Test', 42);
// The id property should be readonly
// @ts-expect-error - Testing readonly property
expect(() => {
entity.id = createTestId('new-id');
}).toThrow();
});
});
describe('type safety', () => {
it('should work with different id types', () => {
// Number ID type
type NumberId = number & { readonly __brand: unique symbol };
class NumberIdEntity extends Entity<NumberId> {}
const numId = 123 as NumberId;
const numEntity = new NumberIdEntity(numId);
expect(numEntity.id).toBe(123);
});
it('should work with complex branded types', () => {
// Complex ID type
type ComplexId = { projectKey: string; runId: string } & { readonly __brand: unique symbol };
class ComplexIdEntity extends Entity<ComplexId> {}
const complexId = { projectKey: 'proj-1', runId: 'run-1' } as ComplexId;
const complexEntity = new ComplexIdEntity(complexId);
expect(complexEntity.id).toEqual({ projectKey: 'proj-1', runId: 'run-1' });
});
});
});