Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Billing/Shared/Domain/ValueObject/Uuid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,15 @@ export default abstract class Uuid {
public toIdentity(): Domain.Identity {
return this.identity;
}

/**
* Value Object equality — two UUID-based identities are equal when
* they represent the same UUID string value.
*
* Source: Evans (Blue Book), p. 98 — "Value objects should be
* testable for equality."
*/
public equals(other: Uuid): boolean {
return this.toString() === other.toString();
}
}
11 changes: 11 additions & 0 deletions src/Billing/Transaction/Domain/ValueObject/Price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,15 @@ export default class Price {
this.amount = amount;
this.currency = currency;
}

/**
* Value Object equality — two Price instances are equal when they
* represent the same amount in the same currency.
*
* Source: Evans (Blue Book), p. 98 — "Value objects should be
* testable for equality."
*/
public equals(other: Price): boolean {
return this.amount === other.amount && this.currency === other.currency;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,28 @@ describe("Price", () => {
const todo = () => new Price("12.5", "GBP");
expect(todo).toThrow(InvalidArgumentException);
});

test("Negative amount is rejected", () => {
const todo = () => new Price("-1", "EUR");
expect(todo).toThrow(InvalidArgumentException);
});

test("Non-numeric amount is rejected", () => {
const todo = () => new Price("abc", "EUR");
expect(todo).toThrow(InvalidArgumentException);
});

describe("equals", () => {
test("two prices with the same amount and currency are equal", () => {
expect(new Price("10.00", "EUR").equals(new Price("10.00", "EUR"))).toBe(true);
});

test("prices with different amounts are not equal", () => {
expect(new Price("10.00", "EUR").equals(new Price("9.99", "EUR"))).toBe(false);
});

test("prices with different currencies are not equal", () => {
expect(new Price("10.00", "EUR").equals(new Price("10.00", "USD"))).toBe(false);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import TransactionId from "@Transaction/Domain/ValueObject/TransactionId";

const UUID_A = "ae081e7a-ec8c-4ff1-9de5-f70383fe03a7";
const UUID_B = "f47ac10b-58cc-4372-a567-0e02b2c3d479";

describe("TransactionId", () => {
test("two ids with the same UUID are equal", () => {
expect(new TransactionId(UUID_A).equals(new TransactionId(UUID_A))).toBe(true);
});

test("two ids with different UUIDs are not equal", () => {
expect(new TransactionId(UUID_A).equals(new TransactionId(UUID_B))).toBe(false);
});

test("generates a unique id when constructed without value", () => {
const a = new TransactionId();
const b = new TransactionId();
expect(a.equals(b)).toBe(false);
});

test("Invalid UUID is rejected", () => {
expect(() => new TransactionId("not-a-uuid")).toThrow();
});
});