core tests
This commit is contained in:
174
core/shared/domain/Entity.test.ts
Normal file
174
core/shared/domain/Entity.test.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Entity, EntityProps, EntityAlias } from './Entity';
|
||||
|
||||
// Concrete implementation for testing
|
||||
class TestEntity extends Entity<string> {
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Entity', () => {
|
||||
describe('EntityProps interface', () => {
|
||||
it('should have readonly id property', () => {
|
||||
const props: EntityProps<string> = { id: 'test-id' };
|
||||
expect(props.id).toBe('test-id');
|
||||
});
|
||||
|
||||
it('should support different id types', () => {
|
||||
const stringProps: EntityProps<string> = { id: 'test-id' };
|
||||
const numberProps: EntityProps<number> = { id: 123 };
|
||||
const uuidProps: EntityProps<string> = { id: '550e8400-e29b-41d4-a716-446655440000' };
|
||||
|
||||
expect(stringProps.id).toBe('test-id');
|
||||
expect(numberProps.id).toBe(123);
|
||||
expect(uuidProps.id).toBe('550e8400-e29b-41d4-a716-446655440000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entity class', () => {
|
||||
it('should have id property', () => {
|
||||
const entity = new TestEntity('entity-123');
|
||||
expect(entity.id).toBe('entity-123');
|
||||
});
|
||||
|
||||
it('should have equals method', () => {
|
||||
const entity1 = new TestEntity('entity-123');
|
||||
const entity2 = new TestEntity('entity-123');
|
||||
const entity3 = new TestEntity('entity-456');
|
||||
|
||||
expect(entity1.equals(entity2)).toBe(true);
|
||||
expect(entity1.equals(entity3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when comparing with undefined', () => {
|
||||
const entity = new TestEntity('entity-123');
|
||||
expect(entity.equals(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when comparing with null', () => {
|
||||
const entity = new TestEntity('entity-123');
|
||||
expect(entity.equals(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when comparing with entity of different type', () => {
|
||||
const entity1 = new TestEntity('entity-123');
|
||||
const entity2 = new TestEntity('entity-123');
|
||||
|
||||
// Even with same ID, if they're different entity types, equals should work
|
||||
// since it only compares IDs
|
||||
expect(entity1.equals(entity2)).toBe(true);
|
||||
});
|
||||
|
||||
it('should support numeric IDs', () => {
|
||||
class NumericEntity extends Entity<number> {
|
||||
constructor(id: number) {
|
||||
super(id);
|
||||
}
|
||||
}
|
||||
|
||||
const entity1 = new NumericEntity(123);
|
||||
const entity2 = new NumericEntity(123);
|
||||
const entity3 = new NumericEntity(456);
|
||||
|
||||
expect(entity1.id).toBe(123);
|
||||
expect(entity1.equals(entity2)).toBe(true);
|
||||
expect(entity1.equals(entity3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should support UUID IDs', () => {
|
||||
const uuid1 = '550e8400-e29b-41d4-a716-446655440000';
|
||||
const uuid2 = '550e8400-e29b-41d4-a716-446655440000';
|
||||
const uuid3 = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
||||
|
||||
const entity1 = new TestEntity(uuid1);
|
||||
const entity2 = new TestEntity(uuid2);
|
||||
const entity3 = new TestEntity(uuid3);
|
||||
|
||||
expect(entity1.equals(entity2)).toBe(true);
|
||||
expect(entity1.equals(entity3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should be immutable - id cannot be changed', () => {
|
||||
const entity = new TestEntity('entity-123');
|
||||
|
||||
// Try to change id (should not work in TypeScript, but testing runtime)
|
||||
// @ts-expect-error - Testing immutability
|
||||
entity.id = 'new-id';
|
||||
|
||||
// ID should remain unchanged
|
||||
expect(entity.id).toBe('entity-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('EntityAlias type', () => {
|
||||
it('should be assignable to EntityProps', () => {
|
||||
const alias: EntityAlias<string> = { id: 'test-id' };
|
||||
expect(alias.id).toBe('test-id');
|
||||
});
|
||||
|
||||
it('should work with different ID types', () => {
|
||||
const stringAlias: EntityAlias<string> = { id: 'test' };
|
||||
const numberAlias: EntityAlias<number> = { id: 42 };
|
||||
const uuidAlias: EntityAlias<string> = { id: '550e8400-e29b-41d4-a716-446655440000' };
|
||||
|
||||
expect(stringAlias.id).toBe('test');
|
||||
expect(numberAlias.id).toBe(42);
|
||||
expect(uuidAlias.id).toBe('550e8400-e29b-41d4-a716-446655440000');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entity behavior', () => {
|
||||
it('should support entity identity', () => {
|
||||
// Entities are identified by their ID
|
||||
const entity1 = new TestEntity('same-id');
|
||||
const entity2 = new TestEntity('same-id');
|
||||
const entity3 = new TestEntity('different-id');
|
||||
|
||||
// Same ID = same identity
|
||||
expect(entity1.equals(entity2)).toBe(true);
|
||||
|
||||
// Different ID = different identity
|
||||
expect(entity1.equals(entity3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should support entity reference equality', () => {
|
||||
const entity = new TestEntity('entity-123');
|
||||
|
||||
// Same instance should equal itself
|
||||
expect(entity.equals(entity)).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with complex ID types', () => {
|
||||
interface ComplexId {
|
||||
tenant: string;
|
||||
sequence: number;
|
||||
}
|
||||
|
||||
class ComplexEntity extends Entity<ComplexId> {
|
||||
constructor(id: ComplexId) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
equals(other?: Entity<ComplexId>): boolean {
|
||||
if (!other) return false;
|
||||
return (
|
||||
this.id.tenant === other.id.tenant &&
|
||||
this.id.sequence === other.id.sequence
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const id1: ComplexId = { tenant: 'org-a', sequence: 1 };
|
||||
const id2: ComplexId = { tenant: 'org-a', sequence: 1 };
|
||||
const id3: ComplexId = { tenant: 'org-b', sequence: 1 };
|
||||
|
||||
const entity1 = new ComplexEntity(id1);
|
||||
const entity2 = new ComplexEntity(id2);
|
||||
const entity3 = new ComplexEntity(id3);
|
||||
|
||||
expect(entity1.equals(entity2)).toBe(true);
|
||||
expect(entity1.equals(entity3)).toBe(false);
|
||||
});
|
||||
});
|
||||
118
core/shared/domain/ValueObject.test.ts
Normal file
118
core/shared/domain/ValueObject.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ValueObject, ValueObjectAlias } from './ValueObject';
|
||||
|
||||
// Concrete implementation for testing
|
||||
class TestValueObject implements ValueObject<{ name: string; value: number }> {
|
||||
readonly props: { name: string; value: number };
|
||||
|
||||
constructor(name: string, value: number) {
|
||||
this.props = { name, value };
|
||||
}
|
||||
|
||||
equals(other: ValueObject<{ name: string; value: number }>): boolean {
|
||||
return (
|
||||
this.props.name === other.props.name && this.props.value === other.props.value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ValueObject', () => {
|
||||
describe('ValueObject interface', () => {
|
||||
it('should have readonly props property', () => {
|
||||
const vo = new TestValueObject('test', 42);
|
||||
expect(vo.props).toEqual({ name: 'test', value: 42 });
|
||||
});
|
||||
|
||||
it('should have equals method', () => {
|
||||
const vo1 = new TestValueObject('test', 42);
|
||||
const vo2 = new TestValueObject('test', 42);
|
||||
const vo3 = new TestValueObject('different', 42);
|
||||
|
||||
expect(vo1.equals(vo2)).toBe(true);
|
||||
expect(vo1.equals(vo3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when comparing with undefined', () => {
|
||||
const vo = new TestValueObject('test', 42);
|
||||
// Testing that equals method handles undefined gracefully
|
||||
const result = vo.equals as any;
|
||||
expect(result(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when comparing with null', () => {
|
||||
const vo = new TestValueObject('test', 42);
|
||||
// Testing that equals method handles null gracefully
|
||||
const result = vo.equals as any;
|
||||
expect(result(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ValueObjectAlias type', () => {
|
||||
it('should be assignable to ValueObject', () => {
|
||||
const vo: ValueObjectAlias<{ name: string }> = {
|
||||
props: { name: 'test' },
|
||||
equals: (other) => other.props.name === 'test',
|
||||
};
|
||||
|
||||
expect(vo.props.name).toBe('test');
|
||||
expect(vo.equals(vo)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ValueObject behavior', () => {
|
||||
it('should support complex value objects', () => {
|
||||
interface AddressProps {
|
||||
street: string;
|
||||
city: string;
|
||||
zipCode: string;
|
||||
}
|
||||
|
||||
class Address implements ValueObject<AddressProps> {
|
||||
readonly props: AddressProps;
|
||||
|
||||
constructor(street: string, city: string, zipCode: string) {
|
||||
this.props = { street, city, zipCode };
|
||||
}
|
||||
|
||||
equals(other: ValueObject<AddressProps>): boolean {
|
||||
return (
|
||||
this.props.street === other.props.street &&
|
||||
this.props.city === other.props.city &&
|
||||
this.props.zipCode === other.props.zipCode
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const address1 = new Address('123 Main St', 'New York', '10001');
|
||||
const address2 = new Address('123 Main St', 'New York', '10001');
|
||||
const address3 = new Address('456 Oak Ave', 'Boston', '02101');
|
||||
|
||||
expect(address1.equals(address2)).toBe(true);
|
||||
expect(address1.equals(address3)).toBe(false);
|
||||
});
|
||||
|
||||
it('should support immutable value objects', () => {
|
||||
class ImmutableValueObject implements ValueObject<{ readonly data: string[] }> {
|
||||
readonly props: { readonly data: string[] };
|
||||
|
||||
constructor(data: string[]) {
|
||||
this.props = { data: [...data] }; // Create a copy to ensure immutability
|
||||
}
|
||||
|
||||
equals(other: ValueObject<{ readonly data: string[] }>): boolean {
|
||||
return (
|
||||
this.props.data.length === other.props.data.length &&
|
||||
this.props.data.every((item, index) => item === other.props.data[index])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const vo1 = new ImmutableValueObject(['a', 'b', 'c']);
|
||||
const vo2 = new ImmutableValueObject(['a', 'b', 'c']);
|
||||
const vo3 = new ImmutableValueObject(['a', 'b', 'd']);
|
||||
|
||||
expect(vo1.equals(vo2)).toBe(true);
|
||||
expect(vo1.equals(vo3)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user