fase(1): shared domain building blocks

This commit is contained in:
debian
2026-03-04 16:22:42 -05:00
parent 2a93f1f5b7
commit 0e6c0c3655
28 changed files with 413 additions and 19 deletions

105
tests/shared/domain.test.ts Normal file
View File

@@ -0,0 +1,105 @@
import { Ok, Err, isOk, isErr } from '../../src/shared/domain/Result';
import { UniqueId } from '../../src/shared/domain/UniqueId';
import { Entity } from '../../src/shared/domain/Entity';
import { ValueObject } from '../../src/shared/domain/ValueObject';
// --- Result ---
describe('Result', () => {
it('Ok creates accessible value', () => {
const r = Ok(42);
expect(r.ok).toBe(true);
if (r.ok) expect(r.value).toBe(42);
});
it('Err creates accessible error', () => {
const e = new Error('fail');
const r = Err(e);
expect(r.ok).toBe(false);
if (!r.ok) expect(r.error).toBe(e);
});
it('isOk discriminates correctly', () => {
expect(isOk(Ok('x'))).toBe(true);
expect(isOk(Err('e'))).toBe(false);
});
it('isErr discriminates correctly', () => {
expect(isErr(Err('e'))).toBe(true);
expect(isErr(Ok('x'))).toBe(false);
});
});
// --- UniqueId ---
describe('UniqueId', () => {
it('create generates a valid UUID string', () => {
const id = UniqueId.create();
expect(id.toString()).toMatch(/^[0-9a-f-]{36}$/);
});
it('from preserves the value', () => {
const id = UniqueId.from('abc-123');
expect(id.toString()).toBe('abc-123');
});
it('equals returns true for same value', () => {
const a = UniqueId.from('same');
const b = UniqueId.from('same');
expect(a.equals(b)).toBe(true);
});
it('equals returns false for different value', () => {
const a = UniqueId.create();
const b = UniqueId.create();
expect(a.equals(b)).toBe(false);
});
it('equals returns false for undefined', () => {
const a = UniqueId.create();
expect(a.equals(undefined)).toBe(false);
});
});
// --- Entity ---
class TestEntity extends Entity<{ name: string }> {}
describe('Entity', () => {
it('equals compares by id, not by props', () => {
const id = UniqueId.from('id-1');
const a = new TestEntity({ name: 'Alice' }, id);
const b = new TestEntity({ name: 'Bob' }, id);
expect(a.equals(b)).toBe(true);
});
it('equals returns false for different ids', () => {
const a = new TestEntity({ name: 'Alice' });
const b = new TestEntity({ name: 'Alice' });
expect(a.equals(b)).toBe(false);
});
it('equals returns false for undefined', () => {
const a = new TestEntity({ name: 'Alice' });
expect(a.equals(undefined)).toBe(false);
});
});
// --- ValueObject ---
class TestVO extends ValueObject<{ amount: number; currency: string }> {}
describe('ValueObject', () => {
it('equals compares by props', () => {
const a = new TestVO({ amount: 100, currency: 'USD' });
const b = new TestVO({ amount: 100, currency: 'USD' });
expect(a.equals(b)).toBe(true);
});
it('equals returns false when props differ', () => {
const a = new TestVO({ amount: 100, currency: 'USD' });
const b = new TestVO({ amount: 200, currency: 'USD' });
expect(a.equals(b)).toBe(false);
});
it('props are frozen (immutable)', () => {
const vo = new TestVO({ amount: 100, currency: 'USD' });
expect(Object.isFrozen((vo as unknown as { props: unknown }).props)).toBe(true);
});
});