import { DailyActiveStats } from '../../../../src/domain/aggregates/daily-active-stats/daily-active-stats.aggregate'; import { DauCalculatedEvent } from '../../../../src/domain/events/dau-calculated.event'; describe('DailyActiveStats Aggregate', () => { describe('create', () => { it('should create DailyActiveStats with required properties', () => { const day = new Date('2025-01-01'); const stats = DailyActiveStats.create({ day, dauCount: 1000, }); expect(stats.day).toEqual(day); expect(stats.dauCount).toBe(1000); expect(stats.dauByProvince).toBeInstanceOf(Map); expect(stats.dauByProvince.size).toBe(0); expect(stats.dauByCity).toBeInstanceOf(Map); expect(stats.dauByCity.size).toBe(0); expect(stats.calculatedAt).toBeInstanceOf(Date); expect(stats.version).toBe(1); }); it('should create DailyActiveStats with province breakdown', () => { const byProvince = new Map([ ['Shanghai', 500], ['Beijing', 300], ['Guangdong', 200], ]); const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, dauByProvince: byProvince, }); expect(stats.dauByProvince.size).toBe(3); expect(stats.dauByProvince.get('Shanghai')).toBe(500); expect(stats.dauByProvince.get('Beijing')).toBe(300); }); it('should create DailyActiveStats with city breakdown', () => { const byCity = new Map([ ['Shanghai', 500], ['Shenzhen', 200], ]); const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 700, dauByCity: byCity, }); expect(stats.dauByCity.size).toBe(2); expect(stats.dauByCity.get('Shanghai')).toBe(500); }); it('should apply DauCalculatedEvent on create', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, }); const events = stats.getUncommittedEvents(); expect(events.length).toBe(1); expect(events[0]).toBeInstanceOf(DauCalculatedEvent); }); it('should handle zero DAU count', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 0, }); expect(stats.dauCount).toBe(0); }); }); describe('recalculate', () => { it('should update DAU count', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, }); stats.commit(); // Clear initial events const oldCalculatedAt = stats.calculatedAt; // Wait a tiny bit to ensure calculatedAt changes stats.recalculate(1500); expect(stats.dauCount).toBe(1500); expect(stats.version).toBe(2); }); it('should update province breakdown', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, }); const newByProvince = new Map([ ['Shanghai', 800], ['Beijing', 700], ]); stats.recalculate(1500, newByProvince); expect(stats.dauByProvince.get('Shanghai')).toBe(800); expect(stats.dauByProvince.get('Beijing')).toBe(700); }); it('should update city breakdown', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, }); const newByCity = new Map([ ['Shenzhen', 300], ]); stats.recalculate(1500, undefined, newByCity); expect(stats.dauByCity.get('Shenzhen')).toBe(300); }); it('should increment version', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, }); expect(stats.version).toBe(1); stats.recalculate(1100); expect(stats.version).toBe(2); stats.recalculate(1200); expect(stats.version).toBe(3); }); it('should apply DauCalculatedEvent on recalculate', () => { const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 1000, }); stats.commit(); // Clear create event stats.recalculate(1500); const events = stats.getUncommittedEvents(); expect(events.length).toBe(1); expect(events[0]).toBeInstanceOf(DauCalculatedEvent); }); }); describe('reconstitute', () => { it('should reconstitute from persistence data', () => { const day = new Date('2025-01-01'); const calculatedAt = new Date('2025-01-01T23:00:00.000Z'); const byProvince = new Map([['Shanghai', 500]]); const byCity = new Map([['Shanghai', 500]]); const stats = DailyActiveStats.reconstitute({ day, dauCount: 2000, dauByProvince: byProvince, dauByCity: byCity, calculatedAt, version: 5, }); expect(stats.day).toEqual(day); expect(stats.dauCount).toBe(2000); expect(stats.dauByProvince.get('Shanghai')).toBe(500); expect(stats.dauByCity.get('Shanghai')).toBe(500); expect(stats.calculatedAt).toEqual(calculatedAt); expect(stats.version).toBe(5); }); it('should not apply events on reconstitute', () => { const stats = DailyActiveStats.reconstitute({ day: new Date('2025-01-01'), dauCount: 2000, dauByProvince: new Map(), dauByCity: new Map(), calculatedAt: new Date(), version: 1, }); const events = stats.getUncommittedEvents(); expect(events.length).toBe(0); }); }); describe('getters return copies', () => { it('should return a copy of dauByProvince', () => { const byProvince = new Map([['Shanghai', 500]]); const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 500, dauByProvince: byProvince, }); const returned = stats.dauByProvince; returned.set('NewProvince', 100); // Original should not be modified expect(stats.dauByProvince.has('NewProvince')).toBe(false); }); it('should return a copy of dauByCity', () => { const byCity = new Map([['Shenzhen', 300]]); const stats = DailyActiveStats.create({ day: new Date('2025-01-01'), dauCount: 300, dauByCity: byCity, }); const returned = stats.dauByCity; returned.set('NewCity', 50); // Original should not be modified expect(stats.dauByCity.has('NewCity')).toBe(false); }); }); });