diff --git a/services/api/scripts/seed-local.mjs b/services/api/scripts/seed-local.mjs index 066a774a..71d803ee 100644 --- a/services/api/scripts/seed-local.mjs +++ b/services/api/scripts/seed-local.mjs @@ -235,6 +235,27 @@ function minutesAfter(minutes) { return new Date(NOW.getTime() + minutes * 60 * 1000); } +// IAM-026/BUA-024: keep the synthetic customer-growth story bounded to the +// June-August 2026 pilot window. July carries the majority of registrations, +// while only four customer identities land after August 14. +function platformAnalyticsRegistrationDate(index) { + let monthIndex; + let day; + if (index < 8) { + monthIndex = 5; + day = 2 + index * 4; + } else if (index < 47) { + monthIndex = 6; + day = 1 + Math.floor(((index - 8) * 31) / 39); + } else { + monthIndex = 7; + const augustIndex = index - 47; + day = augustIndex < 12 ? augustIndex + 1 : 15 + (augustIndex - 12) * 2; + } + + return new Date(Date.UTC(2026, monthIndex, day, 8 + (index % 9), (index * 11) % 60)); +} + export function buildPlatformAnalyticsRows() { const organizationNames = [ 'An PhĂș Retail', @@ -268,7 +289,7 @@ export function buildPlatformAnalyticsRows() { updatedAt: minutesBefore((3 + index * 7) * 1_440), })); const users = LOCAL_PLATFORM_ANALYTICS_IDENTITIES.map(([email, displayName], index) => { - const daysAgo = 4 + index * 4; + const createdAt = platformAnalyticsRegistrationDate(index); return { id: ids(8_100 + index), email, @@ -277,8 +298,8 @@ export function buildPlatformAnalyticsRows() { status: 'ACTIVE', securityEpoch: 1, mfaReenrollmentRequired: false, - createdAt: minutesBefore(daysAgo * 1_440), - updatedAt: minutesBefore(Math.max(1, daysAgo - 1) * 1_440), + createdAt, + updatedAt: new Date(createdAt.getTime() + 6 * 60 * 60 * 1000), organizationId: organizations[index % organizations.length].id, }; }); diff --git a/services/api/test/seed-local.test.mjs b/services/api/test/seed-local.test.mjs index 04d17ae3..67c6dd53 100644 --- a/services/api/test/seed-local.test.mjs +++ b/services/api/test/seed-local.test.mjs @@ -217,6 +217,27 @@ test('[IAM-026][BUA-024] platform overview seed uses 63 valid, unique supplied c assert.equal(new Set(analytics.users.map((user) => user.email)).size, 63); }); +test('[IAM-026][BUA-024] platform overview registrations start in June, peak in July, and leave only four customers after August 14', () => { + const { users } = buildPlatformAnalyticsRows(); + const registrationsByMonth = Object.fromEntries( + ['2026-06', '2026-07', '2026-08'].map((month) => [ + month, + users.filter((user) => user.createdAt.toISOString().startsWith(month)).length, + ]), + ); + + assert.deepEqual(registrationsByMonth, { + '2026-06': 8, + '2026-07': 39, + '2026-08': 16, + }); + assert.ok(users.every((user) => user.createdAt >= new Date('2026-06-01T00:00:00.000Z'))); + assert.equal( + users.filter((user) => user.createdAt >= new Date('2026-08-15T00:00:00.000Z')).length, + 4, + ); +}); + test('[IAM-026][BUA-024] platform overview seed models 21 distinct paid actors at 3,129,000 VND', () => { const analytics = buildPlatformAnalyticsRows(); const paidOrders = analytics.paymentOrders.filter((order) => order.status === 'PAID');