wsp-ms-core 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/index.cjs +44 -3
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +29 -1
  4. package/dist/index.d.ts +29 -1
  5. package/dist/index.js +41 -2
  6. package/dist/index.js.map +1 -1
  7. package/package.json +8 -1
  8. package/jest.config.cjs +0 -9
  9. package/src/application/contracts/EventBus.ts +0 -7
  10. package/src/application/contracts/EventBusRepository.ts +0 -10
  11. package/src/domain/contracts/DomainEntity.ts +0 -58
  12. package/src/domain/contracts/DomainError.ts +0 -9
  13. package/src/domain/contracts/DomainEvent.ts +0 -22
  14. package/src/domain/contracts/ValueObject.ts +0 -26
  15. package/src/domain/errors/FatalError.ts +0 -9
  16. package/src/domain/errors/InternalError.ts +0 -9
  17. package/src/domain/errors/UsageError.ts +0 -12
  18. package/src/domain/value-objects/Currency.ts +0 -68
  19. package/src/domain/value-objects/DateTime.ts +0 -132
  20. package/src/domain/value-objects/Email.ts +0 -24
  21. package/src/domain/value-objects/Language.ts +0 -94
  22. package/src/domain/value-objects/Price.ts +0 -54
  23. package/src/domain/value-objects/UUID.ts +0 -23
  24. package/src/index.ts +0 -43
  25. package/src/infrastructure/contracts/DatabaseConnection.ts +0 -11
  26. package/src/infrastructure/contracts/DatabaseConnector.ts +0 -8
  27. package/src/infrastructure/contracts/Logger.ts +0 -9
  28. package/src/infrastructure/errors/ErrorManager.ts +0 -93
  29. package/src/infrastructure/mysql/MysqlConnection.ts +0 -45
  30. package/src/infrastructure/mysql/MysqlConnector.ts +0 -51
  31. package/src/utils/StringVars.ts +0 -14
  32. package/test/domain/value-objects/Currency.test.ts +0 -48
  33. package/test/domain/value-objects/DateTime.test.ts +0 -32
  34. package/test/domain/value-objects/Email.test.ts +0 -38
  35. package/test/domain/value-objects/Language.test.ts +0 -76
  36. package/test/domain/value-objects/Price.test.ts +0 -96
  37. package/test/domain/value-objects/UUID.test.ts +0 -18
  38. package/test/infrastructure/errors/ErrorManager.test.ts +0 -125
  39. package/test/infrastructure/mysql/MysqlConnection.test.ts +0 -45
  40. package/tsconfig.json +0 -14
  41. package/tsup.config.ts +0 -18
@@ -1,94 +0,0 @@
1
- import { ValueObject } from '@domain/contracts/ValueObject';
2
-
3
- export class Language extends ValueObject<string> {
4
-
5
- public static readonly SUPPORTED: readonly string[] = [
6
- 'es',
7
- 'en',
8
- 'en-us',
9
- 'en-gb',
10
- 'en-au',
11
- 'en-ca',
12
- 'en-nz',
13
- 'en-ie',
14
- 'en-za',
15
- 'en-jm',
16
- 'en-bz',
17
- 'en-tt',
18
- 'pt-br',
19
- 'pt',
20
- 'es',
21
- 'es-ar',
22
- 'es-gt',
23
- 'es-cr',
24
- 'es-pa',
25
- 'es-do',
26
- 'es-mx',
27
- 'es-ve',
28
- 'es-co',
29
- 'es-pe',
30
- 'es-ec',
31
- 'es-cl',
32
- 'es-uy',
33
- 'es-py',
34
- 'es-bo',
35
- 'es-sv',
36
- 'es-hn',
37
- 'es-ni',
38
- 'es-pr',
39
- ] as const;
40
-
41
- public static readonly DEFAULT: Language = new Language('es');
42
- public static readonly ENGLISH: Language = new Language('en');
43
- public static readonly ENGLISH_UNITED_STATES: Language = new Language('en-us');
44
- public static readonly ENGLISH_UNITED_KINGDOM: Language = new Language('en-gb');
45
- public static readonly ENGLISH_AUSTRALIA: Language = new Language('en-au');
46
- public static readonly ENGLISH_CANADA: Language = new Language('en-ca');
47
- public static readonly ENGLISH_NEW_ZEALAND: Language = new Language('en-nz');
48
- public static readonly ENGLISH_IRELAND: Language = new Language('en-ie');
49
- public static readonly ENGLISH_SOUTH_AFRICA: Language = new Language('en-za');
50
- public static readonly ENGLISH_JAMAICA: Language = new Language('en-jm');
51
- public static readonly ENGLISH_BELIZE: Language = new Language('en-bz');
52
- public static readonly ENGLISH_TRINIDAD: Language = new Language('en-tt');
53
- public static readonly PORTUGUESE_BRAZIL: Language = new Language('pt-br');
54
- public static readonly PORTUGUESE_PORTUGAL: Language = new Language('pt');
55
- public static readonly SPANISH: Language = new Language('es');
56
- public static readonly SPANISH_ARGENTINA: Language = new Language('es-ar');
57
- public static readonly SPANISH_GUATEMALA: Language = new Language('es-gt');
58
- public static readonly SPANISH_COSTA_RICA: Language = new Language('es-cr');
59
- public static readonly SPANISH_PANAMA: Language = new Language('es-pa');
60
- public static readonly SPANISH_REPUBLICA_DOMINICANA: Language = new Language('es-do');
61
- public static readonly SPANISH_MEXICO: Language = new Language('es-mx');
62
- public static readonly SPANISH_VENEZUELA: Language = new Language('es-ve');
63
- public static readonly SPANISH_COLOMBIA: Language = new Language('es-co');
64
- public static readonly SPANISH_PERU: Language = new Language('es-pe');
65
- public static readonly SPANISH_ECUADOR: Language = new Language('es-ec');
66
- public static readonly SPANISH_CHILE: Language = new Language('es-cl');
67
- public static readonly SPANISH_URUGUAY: Language = new Language('es-uy');
68
- public static readonly SPANISH_PARAGUAY: Language = new Language('es-py');
69
- public static readonly SPANISH_BOLIVIA: Language = new Language('es-bo');
70
- public static readonly SPANISH_EL_SALVADOR: Language = new Language('es-sv');
71
- public static readonly SPANISH_HONDURAS: Language = new Language('es-hn');
72
- public static readonly SPANISH_NICARAGUA: Language = new Language('es-ni');
73
- public static readonly SPANISH_PUERTO_RICO: Language = new Language('es-pr');
74
-
75
- private constructor(code: string) {
76
- super(code.trim().toLowerCase());
77
- }
78
-
79
- protected validate(value: string): void {
80
- if (!Language.SUPPORTED.includes(value)) {
81
- throw new Error(`Language <${value}> is not supported`);
82
- }
83
- }
84
-
85
- public base(): string {
86
- return this.value.split('-')[0];
87
- }
88
-
89
- public static create(raw: string): Language {
90
- const normalized = raw.trim().toLowerCase().replace('_', '-');
91
- return new Language(normalized);
92
- }
93
-
94
- }
@@ -1,54 +0,0 @@
1
- import { ValueObject } from '@domain/contracts/ValueObject';
2
- import { Currency } from '@domain/value-objects/Currency';
3
-
4
- export class Price extends ValueObject<{ amount: number; currency: Currency }> {
5
-
6
- public static readonly MIN_AMOUNT: number = -1000000;
7
-
8
- public readonly amount: number;
9
- public readonly currency: Currency;
10
-
11
- private constructor(amount: number, currency: Currency) {
12
- super({ amount, currency });
13
- this.amount = amount;
14
- this.currency = currency;
15
- }
16
-
17
- protected validate(props: { amount: number; currency: Currency }): void {
18
- const { amount, currency } = props;
19
-
20
- if (typeof amount !== 'number' || Number.isNaN(amount) || !Number.isFinite(amount)) {
21
- throw new Error(`Price amount <${amount}> is not a valid number`);
22
- }
23
- if (amount < Price.MIN_AMOUNT) {
24
- throw new Error(`Price amount <${amount}> must be ≥ ${Price.MIN_AMOUNT}`);
25
- }
26
- }
27
-
28
- public equals(other?: Price | null): boolean {
29
- if (!other) return false;
30
- return this.amount === other.amount && this.currency.equals(other.currency);
31
- }
32
-
33
- private assertSameCurrency(other: Price): void {
34
- if (!this.currency.equals(other.currency)) {
35
- throw new Error('Cannot operate on Price objects with different currencies');
36
- }
37
- }
38
-
39
- public add(other: Price): Price {
40
- this.assertSameCurrency(other);
41
- return Price.create(this.amount + other.amount, this.currency);
42
- }
43
-
44
- public subtract(other: Price): Price {
45
- this.assertSameCurrency(other);
46
- return Price.create(this.amount - other.amount, this.currency);
47
- }
48
-
49
- public static create(amount: number, currency: Currency | string | number): Price {
50
- const cur = currency instanceof Currency ? currency : Currency.create(currency);
51
- return new Price(amount, cur);
52
- }
53
-
54
- }
@@ -1,23 +0,0 @@
1
- import {ValueObject} from "@domain/contracts/ValueObject";
2
-
3
- export class UUID extends ValueObject<string> {
4
-
5
- private constructor(value: string) {
6
- super(value);
7
- }
8
-
9
- protected validate(uuid: string) {
10
- if (!UUID.isValid(uuid)) {
11
- throw new Error(`Invalid uuid ${uuid}`);
12
- }
13
- }
14
-
15
- public static create(uuid?: string): UUID {
16
- return new UUID(uuid ?? crypto.randomUUID());
17
- }
18
-
19
- public static isValid(uuid: string): boolean {
20
- return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);
21
- }
22
-
23
- }
package/src/index.ts DELETED
@@ -1,43 +0,0 @@
1
- /* ──────────────────────────────────────────────── *
2
- * DOMAIN *
3
- * ──────────────────────────────────────────────── */
4
- // Contracts
5
- export {DomainEntity} from './domain/contracts/DomainEntity';
6
- export {DomainError} from './domain/contracts/DomainError';
7
- export {DomainEvent} from './domain/contracts/DomainEvent';
8
- export {ValueObject} from './domain/contracts/ValueObject';
9
- // Errors
10
- export {FatalError} from './domain/errors/FatalError';
11
- export {InternalError} from './domain/errors/InternalError';
12
- export {UsageError} from './domain/errors/UsageError';
13
- // Value objects
14
- export {Currency} from './domain/value-objects/Currency';
15
- export {DateTime} from './domain/value-objects/DateTime';
16
- export {Email} from './domain/value-objects/Email';
17
- export {Language} from './domain/value-objects/Language';
18
- export {Price} from './domain/value-objects/Price';
19
- export {UUID} from './domain/value-objects/UUID';
20
-
21
-
22
- /* ──────────────────────────────────────────────── *
23
- * APPLICATION *
24
- * ──────────────────────────────────────────────── */
25
- // Contracts
26
- export {EventBus} from './application/contracts/EventBus';
27
- export {EventBusRepository} from './application/contracts/EventBusRepository';
28
-
29
-
30
- /* ──────────────────────────────────────────────── *
31
- * INFRASTRUCTURE *
32
- * ──────────────────────────────────────────────── */
33
- // Contracts
34
- export {DatabaseConnector} from './infrastructure/contracts/DatabaseConnector';
35
- export {DatabaseConnection} from './infrastructure/contracts/DatabaseConnection';
36
- export {Logger} from './infrastructure/contracts/Logger';
37
- // Errors
38
- export {ErrorManager, ErrorManagerHandleResult, ErrorTemplate} from './infrastructure/errors/ErrorManager';
39
- // Mysql
40
- export {MysqlConnector} from './infrastructure/mysql/MysqlConnector';
41
- export {MysqlConnection} from './infrastructure/mysql/MysqlConnection';
42
-
43
-
@@ -1,11 +0,0 @@
1
-
2
- export interface DatabaseConnection<Q = string, Params = unknown[], Row = Record<string, unknown>> {
3
-
4
- query<T = Row>(statement: Q, params?: Params): Promise<T[]>;
5
- begin(): Promise<void>;
6
- commit(): Promise<void>;
7
- rollback(toSavepoint?: string): Promise<void>;
8
- transaction<T>(fn: (conn: this) => Promise<T>): Promise<T>;
9
- close(): Promise<void>;
10
-
11
- }
@@ -1,8 +0,0 @@
1
- import {DatabaseConnection} from "@infrastructure/contracts/DatabaseConnection";
2
-
3
- export interface DatabaseConnector<C extends DatabaseConnection = DatabaseConnection> {
4
-
5
- getConnection(options?: { readonly?: boolean }): Promise<C>;
6
- closePool(): Promise<void>;
7
-
8
- }
@@ -1,9 +0,0 @@
1
- export interface Logger {
2
-
3
- debug(type: string, message: string, meta?: Record<string, any>): void;
4
- info(type: string, message: string, meta?: Record<string, any>): void;
5
- warn(type: string, message: string, meta?: Record<string, any>): void;
6
- error(type: string, message: string, meta?: Record<string, any>): void;
7
- fatal(type: string, message: string, meta?: Record<string, any>): void;
8
-
9
- }
@@ -1,93 +0,0 @@
1
- import {Language} from "@domain/value-objects/Language";
2
- import {FatalError} from "@domain/errors/FatalError";
3
- import {InternalError} from "@domain/errors/InternalError";
4
- import {UsageError} from "@domain/errors/UsageError";
5
- import {StringVars} from "@utils/StringVars";
6
- import {Logger} from "@infrastructure/contracts/Logger";
7
-
8
- export interface ErrorTemplate {
9
- type: string;
10
- languages: Record<string, string>;
11
- }
12
-
13
- export type ErrorManagerHandleResult = {
14
- status: number | string;
15
- message: string;
16
- };
17
-
18
- export class ErrorManager {
19
-
20
- private static readonly DEFAULT_MESSAGES: Record<string, string> = {
21
- 'es': 'Ups, hemos encontrado un error. Nuestro equipo ya está trabajando para solucionarlo',
22
- 'en': 'Ups, we found an error. Our team is working on it.',
23
- 'pt': 'Ops, encontramos um bug. Nossa equipe já está trabalhando para resolver isso.',
24
- };
25
-
26
- public static readonly APP_ERRORS = {
27
- UNDEFINED: 'UNDEFINED_ERROR',
28
- PROCESS: 'PROCESS_ERROR',
29
- DATABASE: 'DATABASE_ERROR'
30
- } as const;
31
-
32
- private static readonly TEMPLATES = new Map<string, ErrorTemplate>();
33
-
34
- public constructor(private readonly logger: Logger | null = null) {}
35
-
36
- private getDefaultMessage(lang: Language): string {
37
- return (ErrorManager.DEFAULT_MESSAGES[lang.value] || ErrorManager.DEFAULT_MESSAGES[lang.base()] || 'error');
38
- }
39
-
40
- private onFatal(err: FatalError, lang: Language): ErrorManagerHandleResult {
41
- this.logger?.fatal(err.type, err.message);
42
- return { status: 'ERROR', message: this.getDefaultMessage(lang) };
43
- }
44
-
45
- private onInternal(err: InternalError, lang: Language): ErrorManagerHandleResult {
46
- this.logger?.error(err.type, err.message);
47
- return { status: 'ERROR', message: this.getDefaultMessage(lang) };
48
- }
49
-
50
- private onUsage(err: UsageError, lang: Language): ErrorManagerHandleResult {
51
- const tmpl = ErrorManager.TEMPLATES.get(err.type);
52
- if (!tmpl) {
53
- this.logger?.error('TEMPLATE_NOT_FOUND', `${err.type}`);
54
- return { status: 'ERROR', message: this.getDefaultMessage(lang) };
55
- }
56
- const code = lang.value;
57
- const base = lang.base();
58
- const rawMsg =
59
- tmpl.languages[code] ??
60
- tmpl.languages[base] ??
61
- this.getDefaultMessage(lang);
62
- return {
63
- status: 'ERROR',
64
- message: StringVars.parse(rawMsg, err.vars),
65
- };
66
- }
67
-
68
- private onUnknown(err: Error, lang: Language): ErrorManagerHandleResult {
69
- this.logger?.error('UNKNOWN_ERROR', err.message);
70
- return { status: 'ERROR', message: this.getDefaultMessage(lang) };
71
- }
72
-
73
- public handle(err: Error, lang: Language): ErrorManagerHandleResult {
74
- if (['local','dev'].includes(process.env.ENVIRONMENT ?? '')) {
75
- console.log(err);
76
- }
77
- if (err instanceof FatalError) {
78
- return this.onFatal(err, lang);
79
- }
80
- if (err instanceof InternalError) {
81
- return this.onInternal(err, lang);
82
- }
83
- if (err instanceof UsageError) {
84
- return this.onUsage(err, lang);
85
- }
86
- return this.onUnknown(err, lang);
87
- }
88
-
89
- public static addTemplate(template: ErrorTemplate): void {
90
- ErrorManager.TEMPLATES.set(template.type, template);
91
- }
92
-
93
- }
@@ -1,45 +0,0 @@
1
- import {FieldPacket, PoolConnection, RowDataPacket} from "mysql2/promise";
2
- import {DatabaseConnection} from "@infrastructure/contracts/DatabaseConnection";
3
-
4
- export class MysqlConnection implements DatabaseConnection<string, any[], RowDataPacket> {
5
-
6
- private readonly _conn: PoolConnection;
7
-
8
- public constructor(conn: PoolConnection) {
9
- this._conn = conn;
10
- }
11
-
12
- public async query<R = RowDataPacket>(statement: string, params: any[] = [],): Promise<R[]> {
13
- const [rows] = await this._conn.query<R[] & RowDataPacket[] & FieldPacket[]>(statement, params);
14
- return rows as R[];
15
- }
16
-
17
- public async begin(): Promise<void> {
18
- await this._conn.beginTransaction();
19
- }
20
-
21
- public async commit(): Promise<void> {
22
- await this._conn.commit();
23
- }
24
-
25
- public async rollback(): Promise<void> {
26
- await this._conn.rollback();
27
- }
28
-
29
- public async transaction<T>(fn: (conn: this) => Promise<T>): Promise<T> {
30
- await this.begin();
31
- try {
32
- const result: T = await fn(this);
33
- await this.commit();
34
- return result;
35
- } catch (err) {
36
- await this.rollback();
37
- throw err;
38
- }
39
- }
40
-
41
- public async close(): Promise<void> {
42
- this._conn.release();
43
- }
44
-
45
- }
@@ -1,51 +0,0 @@
1
- import { createPool, Pool, PoolConnection } from 'mysql2/promise';
2
- import {DatabaseConnector} from "@infrastructure/contracts/DatabaseConnector";
3
- import {MysqlConnection} from "@infrastructure/mysql/MysqlConnection";
4
-
5
- export class MysqlConnector implements DatabaseConnector<MysqlConnection> {
6
- public static readonly DEFAULT_POOL_SIZE: number = 10;
7
-
8
- private readonly _pool: Pool;
9
-
10
- public constructor(pool?: Pool) {
11
- this._pool =
12
- pool ??
13
- createPool({
14
- host: process.env.DB_HOST,
15
- port: Number(process.env.DB_PORT ?? 3306),
16
- user: process.env.DB_USER,
17
- password: process.env.DB_PASSWORD,
18
- database: process.env.DB_NAME,
19
- connectionLimit:
20
- Number(process.env.DB_POOL_SIZE) || MysqlConnector.DEFAULT_POOL_SIZE,
21
- decimalNumbers: true,
22
- });
23
- }
24
-
25
- private async wrap(conn: PoolConnection): Promise<MysqlConnection> {
26
- return new MysqlConnection(conn);
27
- }
28
-
29
- public async getConnection(): Promise<MysqlConnection> {
30
- const conn: PoolConnection = await this._pool.getConnection();
31
- return this.wrap(conn);
32
- }
33
-
34
- public async closePool(): Promise<void> {
35
- await this._pool.end();
36
- }
37
-
38
- public static async ping(): Promise<boolean> {
39
- const connector: MysqlConnector = new MysqlConnector();
40
- try {
41
- const conn = await connector._pool.getConnection();
42
- await conn.ping();
43
- conn.release();
44
- return true;
45
- } catch {
46
- return false;
47
- } finally {
48
- await connector.closePool();
49
- }
50
- }
51
- }
@@ -1,14 +0,0 @@
1
- export class StringVars {
2
-
3
- public static parse(str: string, ob: { [key: string]: any }): string {
4
- const regex = /{{(.*?)}}/g;
5
- return str.replace(regex, (match, variable) => {
6
- if (ob.hasOwnProperty(variable.trim())) {
7
- return ob[variable.trim()];
8
- } else {
9
- return match;
10
- }
11
- });
12
- }
13
-
14
- }
@@ -1,48 +0,0 @@
1
- import { Currency } from '@domain/value-objects/Currency';
2
-
3
- describe('Currency ValueObject', () => {
4
-
5
- it('creates from alpha code (case‑insensitive) and exposes numeric', () => {
6
- const uyu = Currency.create('uyu');
7
- expect(uyu.value).toBe('UYU');
8
- expect(uyu.numeric).toBe(858);
9
- });
10
-
11
- it('creates from numeric code (number) and maps to alpha', () => {
12
- const uyu = Currency.create(858);
13
- expect(uyu.value).toBe('UYU');
14
- expect(uyu.numeric).toBe(858);
15
- });
16
-
17
- it('creates from numeric code (string) and maps to alpha', () => {
18
- const ars = Currency.create('032');
19
- expect(ars.value).toBe('ARS');
20
- expect(ars.numeric).toBe(32);
21
- });
22
-
23
- it('equals() returns true for different representations of same currency', () => {
24
- const a = Currency.create('uyu');
25
- const b = Currency.create(858);
26
- expect(a.equals(b)).toBe(true);
27
- expect(a.equals(Currency.UYU)).toBe(true);
28
- });
29
-
30
- it('isValid() returns true for valid inputs', () => {
31
- expect(Currency.isValid('USD')).toBe(true);
32
- expect(Currency.isValid(840)).toBe(true);
33
- });
34
-
35
- it('isValid() returns false for invalid inputs', () => {
36
- expect(Currency.isValid('US')).toBe(false);
37
- expect(Currency.isValid('USDX')).toBe(false);
38
- expect(Currency.isValid(999)).toBe(false);
39
- });
40
-
41
- it('throws when alpha code is invalid', () => {
42
- expect(() => Currency.create('ABC1')).toThrow(Error);
43
- });
44
-
45
- it('throws when numeric code is unknown', () => {
46
- expect(() => Currency.create(999)).toThrow(Error);
47
- });
48
- });
@@ -1,32 +0,0 @@
1
- import { DateTime } from '@domain/value-objects/DateTime';
2
-
3
- describe('DateTime ValueObject', () => {
4
- const ISO = '2025-07-27T15:30:00Z';
5
- const TS = Date.parse(ISO);
6
-
7
- it('creates from ISO string', () => {
8
- const dt = DateTime.create(ISO);
9
- expect(dt.value).toBe('2025-07-27 15:30:00');
10
- expect(dt.year).toBe(2025);
11
- });
12
-
13
- it('creates from timestamp', () => {
14
- const dt = DateTime.create(TS);
15
- expect(dt.day).toBe(27);
16
- });
17
-
18
- it('plusDays() returns a new DateTime with expected day', () => {
19
- const dt = DateTime.create(ISO).plusDays(3);
20
- expect(dt.day).toBe(30);
21
- });
22
-
23
- it('minusHours() returns a new DateTime with expected hour', () => {
24
- const dt = DateTime.create(ISO).minusHours(2);
25
- expect(dt.hour).toBe(13);
26
- });
27
-
28
- it('getMonthName() obeys locale', () => {
29
- const dt = DateTime.create(ISO);
30
- expect(dt.getMonthName('es')).toBe('julio'); // Luxon locale
31
- });
32
- });
@@ -1,38 +0,0 @@
1
- import { Email } from '@domain/value-objects/Email';
2
-
3
- describe('Email ValueObject', () => {
4
-
5
- it('creates from a valid e‑mail string', () => {
6
- const mail = Email.create('user@example.com');
7
- expect(mail.value).toBe('user@example.com');
8
- });
9
-
10
- it('trims whitespace before validation/storage', () => {
11
- const mail = Email.create(' user@example.com \n');
12
- expect(mail.value).toBe('user@example.com');
13
- });
14
-
15
- it('equals() returns true for the exact same address string', () => {
16
- const a = Email.create('user@example.com');
17
- const b = Email.create('user@example.com');
18
- expect(a.equals(b)).toBe(true);
19
- });
20
-
21
- it('equals() is case‑sensitive (default behaviour)', () => {
22
- const a = Email.create('User@Example.com');
23
- const b = Email.create('user@example.com');
24
- expect(a.equals(b)).toBe(false);
25
- });
26
-
27
- it('isValid() returns true for a correct address', () => {
28
- expect(Email.isValid('user@example.com')).toBe(true);
29
- });
30
-
31
- it('isValid() returns false for an incorrect address', () => {
32
- expect(Email.isValid('not‑an‑email')).toBe(false);
33
- });
34
-
35
- it('throws when the address is invalid', () => {
36
- expect(() => Email.create('bad‑mail')).toThrow(Error);
37
- });
38
- });
@@ -1,76 +0,0 @@
1
- import { Language } from '@domain/value-objects/Language';
2
-
3
- describe('Language ValueObject', () => {
4
- describe('create()', () => {
5
- it('creates from lowercase code and preserves value', () => {
6
- const lang = Language.create('es');
7
- expect(lang.value).toBe('es');
8
- });
9
-
10
- it('creates from uppercase code by normalizing to lowercase', () => {
11
- const lang = Language.create('ES-AR');
12
- expect(lang.value).toBe('es-ar');
13
- });
14
-
15
- it('replaces underscore with hyphen and normalizes', () => {
16
- const lang = Language.create('EN_US');
17
- expect(lang.value).toBe('en-us');
18
- });
19
-
20
- it('trims whitespace around the code', () => {
21
- const lang = Language.create(' en-gb ');
22
- expect(lang.value).toBe('en-gb');
23
- });
24
- });
25
-
26
- describe('supported constants', () => {
27
- it('has a DEFAULT constant of "es"', () => {
28
- expect(Language.DEFAULT.value).toBe('es');
29
- });
30
-
31
- it('has an ENGLISH_UNITED_STATES constant of "en-us"', () => {
32
- expect(Language.ENGLISH_UNITED_STATES.value).toBe('en-us');
33
- });
34
- });
35
-
36
- describe('base()', () => {
37
- it('returns "es" for Spanish variants', () => {
38
- const lang = Language.create('es-mx');
39
- expect(lang.base()).toBe('es');
40
- });
41
-
42
- it('returns "en" for English variants', () => {
43
- const lang = Language.create('en-za');
44
- expect(lang.base()).toBe('en');
45
- });
46
-
47
- it('returns the same value if no region part', () => {
48
- const lang = Language.create('pt');
49
- expect(lang.base()).toBe('pt');
50
- });
51
- });
52
-
53
- describe('equals()', () => {
54
- it('returns true for two identical codes', () => {
55
- const a = Language.create('es');
56
- const b = Language.create('ES');
57
- expect(a.equals(b)).toBe(true);
58
- });
59
-
60
- it('returns false for different codes', () => {
61
- const a = Language.create('en');
62
- const b = Language.create('es');
63
- expect(a.equals(b)).toBe(false);
64
- });
65
- });
66
-
67
- describe('validation errors', () => {
68
- it('throws when creating an unsupported code', () => {
69
- expect(() => Language.create('fr')).toThrow('Language <fr> is not supported');
70
- });
71
-
72
- it('throws when creating an empty string', () => {
73
- expect(() => Language.create('')).toThrow('Language <> is not supported');
74
- });
75
- });
76
- });