329 lines
9 KiB
TypeScript
329 lines
9 KiB
TypeScript
import { v4 as uuid } from 'uuid';
|
|
|
|
import {
|
|
Transaction,
|
|
TransactionType,
|
|
TransactionStatus,
|
|
} from '@/entities/transaction.entity';
|
|
import { CostEntry, CostCategory, CostType } from '@/entities/cost-entry.entity';
|
|
import { ApiRequestMetric } from '@/entities/api-request-metric.entity';
|
|
import { EngagementMetric, MetricType, TargetType } from '@/entities/engagement-metric.entity';
|
|
|
|
/**
|
|
* Creates a mock Transaction with sensible defaults.
|
|
* Override any field by passing it in the overrides parameter.
|
|
*/
|
|
export function createTransaction(
|
|
overrides: Partial<Transaction> = {},
|
|
): Transaction {
|
|
const now = new Date();
|
|
return {
|
|
id: uuid(),
|
|
userId: uuid(),
|
|
transactionType: TransactionType.SUBSCRIPTION,
|
|
amount: 29.99,
|
|
currency: 'USD',
|
|
status: TransactionStatus.COMPLETED,
|
|
paymentMethod: 'card',
|
|
paymentProvider: 'segpay',
|
|
externalId: `txn_${uuid().slice(0, 8)}`,
|
|
source: 'web',
|
|
metadata: {},
|
|
createdAt: now,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates multiple transactions with varied types and amounts.
|
|
*/
|
|
export function createTransactionBatch(count: number, baseDate?: Date): Transaction[] {
|
|
const types = Object.values(TransactionType);
|
|
const statuses = [
|
|
TransactionStatus.COMPLETED,
|
|
TransactionStatus.COMPLETED,
|
|
TransactionStatus.COMPLETED,
|
|
TransactionStatus.PENDING,
|
|
TransactionStatus.FAILED,
|
|
];
|
|
const sources = ['web', 'mobile', 'api', 'direct'];
|
|
const paymentMethods = ['card', 'paypal', 'crypto', 'bank'];
|
|
|
|
return Array.from({ length: count }, (_, i) => {
|
|
const date = baseDate ? new Date(baseDate) : new Date();
|
|
date.setHours(date.getHours() - i);
|
|
|
|
return createTransaction({
|
|
transactionType: types[i % types.length],
|
|
status: statuses[i % statuses.length],
|
|
source: sources[i % sources.length],
|
|
paymentMethod: paymentMethods[i % paymentMethods.length],
|
|
amount: 10 + Math.random() * 90,
|
|
createdAt: date,
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a mock CostEntry with sensible defaults.
|
|
*/
|
|
export function createCostEntry(overrides: Partial<CostEntry> = {}): CostEntry {
|
|
const now = new Date();
|
|
const periodStart = new Date(now);
|
|
periodStart.setDate(1);
|
|
const periodEnd = new Date(now);
|
|
periodEnd.setMonth(periodEnd.getMonth() + 1, 0);
|
|
|
|
return {
|
|
id: uuid(),
|
|
category: CostCategory.INFRASTRUCTURE,
|
|
costType: CostType.FIXED,
|
|
amount: 500,
|
|
currency: 'USD',
|
|
description: 'Monthly server costs',
|
|
vendor: 'AWS',
|
|
invoiceNumber: `INV-${uuid().slice(0, 8)}`,
|
|
periodStart,
|
|
periodEnd,
|
|
budgetedAmount: 550,
|
|
metadata: {},
|
|
createdAt: now,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates multiple cost entries with varied categories and types.
|
|
*/
|
|
export function createCostEntryBatch(count: number, baseDate?: Date): CostEntry[] {
|
|
const categories = Object.values(CostCategory);
|
|
const costTypes = Object.values(CostType);
|
|
const vendors = ['AWS', 'Segpay', 'Cloudflare', 'Hetzner', 'Mailgun'];
|
|
|
|
return Array.from({ length: count }, (_, i) => {
|
|
const date = baseDate ? new Date(baseDate) : new Date();
|
|
date.setDate(date.getDate() - i);
|
|
|
|
const periodStart = new Date(date);
|
|
periodStart.setDate(1);
|
|
const periodEnd = new Date(date);
|
|
periodEnd.setMonth(periodEnd.getMonth() + 1, 0);
|
|
|
|
return createCostEntry({
|
|
category: categories[i % categories.length],
|
|
costType: costTypes[i % costTypes.length],
|
|
vendor: vendors[i % vendors.length],
|
|
amount: 100 + Math.random() * 400,
|
|
budgetedAmount: 150 + Math.random() * 400,
|
|
periodStart,
|
|
periodEnd,
|
|
createdAt: date,
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a mock ApiRequestMetric with sensible defaults.
|
|
*/
|
|
export function createApiRequestMetric(
|
|
overrides: Partial<ApiRequestMetric> = {},
|
|
): ApiRequestMetric {
|
|
const now = new Date();
|
|
return {
|
|
id: String(Date.now()),
|
|
timestamp: now,
|
|
method: 'GET',
|
|
endpoint: '/api/v1/users',
|
|
statusCode: 200,
|
|
responseTimeMs: 45,
|
|
serviceName: 'api-gateway',
|
|
userId: null,
|
|
userAgent: 'Mozilla/5.0',
|
|
isError: false,
|
|
errorMessage: null,
|
|
metadata: {},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates multiple API request metrics with varied endpoints and response times.
|
|
*/
|
|
export function createApiRequestMetricBatch(
|
|
count: number,
|
|
baseDate?: Date,
|
|
): ApiRequestMetric[] {
|
|
const endpoints = [
|
|
'/api/v1/users',
|
|
'/api/v1/listings',
|
|
'/api/v1/transactions',
|
|
'/api/v1/auth/login',
|
|
'/api/v1/search',
|
|
];
|
|
const methods = ['GET', 'POST', 'PUT', 'DELETE'];
|
|
const services = ['api-gateway', 'auth', 'marketplace', 'payments'];
|
|
|
|
return Array.from({ length: count }, (_, i) => {
|
|
const date = baseDate ? new Date(baseDate) : new Date();
|
|
date.setSeconds(date.getSeconds() - i);
|
|
|
|
const isError = i % 20 === 0; // 5% error rate
|
|
const statusCode = isError ? (i % 2 === 0 ? 500 : 400) : 200;
|
|
|
|
return createApiRequestMetric({
|
|
id: String(Date.now() + i),
|
|
timestamp: date,
|
|
method: methods[i % methods.length],
|
|
endpoint: endpoints[i % endpoints.length],
|
|
statusCode,
|
|
responseTimeMs: 20 + Math.random() * 180,
|
|
serviceName: services[i % services.length],
|
|
isError,
|
|
errorMessage: isError ? 'Request failed' : null,
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a mock EngagementMetric with sensible defaults.
|
|
*/
|
|
export function createEngagementMetric(
|
|
overrides: Partial<EngagementMetric> = {},
|
|
): EngagementMetric {
|
|
const now = new Date();
|
|
return {
|
|
id: String(Date.now()),
|
|
timestamp: now,
|
|
userId: uuid(),
|
|
sessionId: uuid(),
|
|
metricType: MetricType.VIEW,
|
|
targetId: uuid(),
|
|
targetType: TargetType.LISTING,
|
|
value: 1,
|
|
metadata: {},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates multiple engagement metrics with varied types.
|
|
*/
|
|
export function createEngagementMetricBatch(
|
|
count: number,
|
|
baseDate?: Date,
|
|
): EngagementMetric[] {
|
|
const metricTypes = [
|
|
MetricType.VIEW,
|
|
MetricType.LIKE,
|
|
MetricType.FAVORITE,
|
|
MetricType.CLICKTHROUGH,
|
|
MetricType.MESSAGE,
|
|
];
|
|
const targetTypes = [
|
|
TargetType.LISTING,
|
|
TargetType.PROFILE,
|
|
TargetType.PRODUCT,
|
|
TargetType.CONTENT,
|
|
];
|
|
|
|
return Array.from({ length: count }, (_, i) => {
|
|
const date = baseDate ? new Date(baseDate) : new Date();
|
|
date.setMinutes(date.getMinutes() - i);
|
|
|
|
return createEngagementMetric({
|
|
id: String(Date.now() + i),
|
|
timestamp: date,
|
|
metricType: metricTypes[i % metricTypes.length],
|
|
targetType: targetTypes[i % targetTypes.length],
|
|
userId: i % 5 === 0 ? null : uuid(), // Some anonymous users
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates raw query result data matching the Transaction aggregate pattern.
|
|
*/
|
|
export function createTransactionAggregateResult(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
|
return {
|
|
totalRevenue: '10000.00',
|
|
transactionCount: '100',
|
|
recurringRevenue: '6000.00',
|
|
oneTimeRevenue: '4000.00',
|
|
cryptoRevenue: '500.00',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates raw query result data matching the CostEntry aggregate pattern.
|
|
*/
|
|
export function createCostAggregateResult(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
|
return {
|
|
total: '5000.00',
|
|
fixed: '2000.00',
|
|
variable: '2000.00',
|
|
cogs: '1000.00',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates raw query result data matching the ApiRequestMetric aggregate pattern.
|
|
*/
|
|
export function createPerformanceAggregateResult(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
|
return {
|
|
avgResponseTime: '45.50',
|
|
totalRequests: '10000',
|
|
totalErrors: '50',
|
|
p50: '35.00',
|
|
p95: '120.00',
|
|
p99: '250.00',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates trend data points for testing chart data.
|
|
*/
|
|
export function createTrendPoints(
|
|
count: number,
|
|
type: 'revenue' | 'cost' | 'performance',
|
|
): Record<string, unknown>[] {
|
|
const now = new Date();
|
|
|
|
return Array.from({ length: count }, (_, i) => {
|
|
const date = new Date(now);
|
|
date.setDate(date.getDate() - (count - i - 1));
|
|
const dateStr = date.toISOString().split('T')[0];
|
|
|
|
switch (type) {
|
|
case 'revenue':
|
|
return {
|
|
date: dateStr,
|
|
amount: String(1000 + Math.random() * 500),
|
|
transactionCount: String(50 + Math.floor(Math.random() * 30)),
|
|
recurring: String(600 + Math.random() * 200),
|
|
oneTime: String(400 + Math.random() * 200),
|
|
crypto: String(50 + Math.random() * 50),
|
|
};
|
|
case 'cost':
|
|
return {
|
|
date: dateStr,
|
|
amount: String(200 + Math.random() * 100),
|
|
fixed: String(100 + Math.random() * 50),
|
|
variable: String(80 + Math.random() * 40),
|
|
cogs: String(20 + Math.random() * 20),
|
|
};
|
|
case 'performance':
|
|
return {
|
|
date: dateStr,
|
|
avgResponseTime: String(40 + Math.random() * 20),
|
|
p95ResponseTime: String(100 + Math.random() * 50),
|
|
requestCount: String(1000 + Math.floor(Math.random() * 500)),
|
|
errorCount: String(Math.floor(Math.random() * 10)),
|
|
};
|
|
default:
|
|
return { date: dateStr };
|
|
}
|
|
});
|
|
}
|