PSA/server/test-utils/testDataFactory.ts
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

222 lines
6.7 KiB
TypeScript

import { Knex } from 'knex';
import { v4 as uuidv4 } from 'uuid';
import { BillingCycleType } from 'server/src/interfaces/billing.interfaces';
import { TenantEntity } from 'server/src/interfaces/index';
import { IClient, IClientLocation } from 'server/src/interfaces/client.interfaces';
import type { ProductCode } from '@alga-psa/types';
/**
* Creates a new tenant in the database
* @param db Knex database instance
* @param name Optional tenant name (defaults to "Test Tenant")
* @returns The ID of the created tenant
*/
export async function createTenant(
db: Knex,
name: string = 'Test Tenant',
options: { productCode?: ProductCode } = {}
): Promise<string> {
const tenantId = uuidv4();
const now = new Date().toISOString();
await db('tenants').insert({
tenant: tenantId,
client_name: name,
phone_number: '555-0100',
email: `test-${tenantId.substring(0, 8)}@example.com`,
created_at: now,
updated_at: now,
payment_platform_id: `test-platform-${tenantId.substring(0, 8)}`,
payment_method_id: `test-method-${tenantId.substring(0, 8)}`,
auth_service_id: `test-auth-${tenantId.substring(0, 8)}`,
plan: 'pro',
product_code: options.productCode ?? 'psa',
});
return tenantId;
}
/**
* Creates a new client in the database
* @param db Knex database instance
* @param tenantId ID of the tenant this client belongs to
* @param name Optional client name (defaults to "Test Client")
* @param options Optional additional client properties
* @returns The ID of the created client
*/
export async function createClient(
db: Knex,
tenantId: string,
name: string = 'Test Client',
options: Partial<IClient> = {}
): Promise<string> {
const clientId = uuidv4();
const now = new Date().toISOString();
const client: IClient = {
client_id: clientId,
client_name: name,
tenant: tenantId,
billing_cycle: options.billing_cycle || 'monthly' as BillingCycleType,
is_tax_exempt: options.is_tax_exempt ?? false,
url: options.url || '',
created_at: now,
updated_at: now,
is_inactive: options.is_inactive ?? false,
credit_balance: options.credit_balance ?? 0,
client_type: options.client_type,
tax_id_number: options.tax_id_number,
notes: options.notes,
notes_document_id: options.notes_document_id,
properties: options.properties || {},
payment_terms: options.payment_terms,
credit_limit: options.credit_limit,
preferred_payment_method: options.preferred_payment_method,
auto_invoice: options.auto_invoice,
invoice_delivery_method: options.invoice_delivery_method,
region_code: options.region_code,
tax_exemption_certificate: options.tax_exemption_certificate,
timezone: options.timezone,
invoice_template_id: options.invoice_template_id,
billing_contact_id: options.billing_contact_id,
billing_email: options.billing_email
};
await db('clients').insert(client);
return clientId;
}
/**
* Creates a new client location in the database
* @param db Knex database instance
* @param clientId ID of the client this location belongs to
* @param tenantId ID of the tenant this location belongs to
* @param options Optional location properties
* @returns The ID of the created location
*/
export async function createClientLocation(
db: Knex,
clientId: string,
tenantId: string,
options: Partial<IClientLocation> = {}
): Promise<string> {
const locationId = uuidv4();
const now = new Date();
const location: IClientLocation = {
location_id: locationId,
client_id: clientId,
tenant: tenantId,
address_line1: options.address_line1 || '123 Test St',
address_line2: options.address_line2,
city: options.city || 'Test City',
state_province: options.state_province,
postal_code: options.postal_code,
country_code: options.country_code || 'US',
country_name: options.country_name || 'United States',
region_code: options.region_code || 'US-NY',
created_at: now.toISOString(),
updated_at: now.toISOString()
};
await db('client_locations').insert(location);
return locationId;
}
/**
* Creates a new user in the database
* @param db Knex database instance
* @param tenantId ID of the tenant this user belongs to
* @param options Optional user properties
* @returns The ID of the created user
*/
export async function createUser(
db: Knex,
tenantId: string,
options: {
email?: string;
username?: string;
first_name?: string;
last_name?: string;
hashed_password?: string;
user_type?: 'client' | 'internal';
two_factor_enabled?: boolean;
two_factor_secret?: string;
is_google_user?: boolean;
is_inactive?: boolean;
contact_id?: string;
phone?: string;
timezone?: string;
} = {}
): Promise<string> {
const userId = uuidv4();
const now = new Date();
const user = {
user_id: userId,
tenant: tenantId,
username: options.username || `test.user.${userId}`,
first_name: options.first_name || 'Test',
last_name: options.last_name || 'User',
email: options.email || `test.user.${userId}@example.com`,
hashed_password: options.hashed_password || 'hashed_password_here',
created_at: now,
two_factor_enabled: options.two_factor_enabled ?? false,
two_factor_secret: options.two_factor_secret,
is_google_user: options.is_google_user ?? false,
is_inactive: options.is_inactive ?? false,
user_type: options.user_type || 'internal',
contact_id: options.contact_id,
phone: options.phone,
timezone: options.timezone
};
await db('users').insert(user);
return userId;
}
/**
* Creates a complete test environment with tenant, client, location, and user
* @param db Knex database instance
* @param options Optional properties for the created entities
* @returns Object containing the created IDs
*/
export async function createTestEnvironment(
db: Knex,
options: {
clientName?: string;
userName?: string;
billingCycle?: BillingCycleType;
} = {}
): Promise<{
tenantId: string;
clientId: string;
locationId: string;
userId: string;
}> {
const tenantId = await createTenant(db);
const clientId = await createClient(db, tenantId, options.clientName, {
billing_cycle: options.billingCycle || 'monthly'
});
// For now, skip location creation due to region_code foreign key constraint
// const locationId = await createClientLocation(db, clientId, tenantId);
const locationId = ''; // Empty for now
const userId = await createUser(db, tenantId, {
username: options.userName
});
return { tenantId, clientId, locationId, userId };
}
/**
* Helper function to generate a timestamp for test data
* @param date Optional date to convert (defaults to current time)
* @returns ISO string timestamp
*/
export function getTestTimestamp(date: Date = new Date()): string {
return date.toISOString();
}