PSA/packages/scheduling/tests/bucketUsageService.periods.test.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

141 lines
4.1 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
vi.mock('@alga-psa/db', () => ({
createTenantKnex: vi.fn(async () => ({ tenant: 'test-tenant' })),
}));
import { findOrCreateCurrentBucketUsageRecord } from '../src/services/bucketUsageService';
describe('scheduling bucketUsageService period selection', () => {
it('uses the previous client billing cycle when rollover periods follow canonical client cadence', async () => {
const state = {
clientBillingCycleCalls: 0,
bucketUsageFirstCalls: 0,
insertedRecord: null as Record<string, unknown> | null,
};
const trx: any = ((tableName: string) => {
const builder: any = {};
builder.where = vi.fn().mockImplementation(() => builder);
builder.andWhere = vi.fn().mockImplementation(() => builder);
builder.whereNotNull = vi.fn().mockImplementation(() => builder);
builder.join = vi.fn().mockImplementation(() => builder);
builder.leftJoin = vi.fn().mockImplementation(() => builder);
builder.andOn = vi.fn().mockImplementation(() => builder);
builder.andOnVal = vi.fn().mockImplementation(() => builder);
builder.orderBy = vi.fn().mockImplementation(() => builder);
builder.select = vi.fn().mockImplementation(() => builder);
builder.first = vi.fn().mockImplementation(async () => {
if (tableName === 'client_contract_lines as ccl') {
return {
contract_line_id: 'plan-1',
start_date: '2024-12-15',
billing_frequency: 'monthly',
};
}
if (tableName === 'client_billing_cycles') {
state.clientBillingCycleCalls += 1;
if (state.clientBillingCycleCalls === 1) {
return {
period_start_date: '2025-02-01',
period_end_date: '2025-03-01',
};
}
return {
period_start_date: '2025-01-01',
period_end_date: '2025-02-01',
};
}
if (tableName === 'bucket_usage') {
state.bucketUsageFirstCalls += 1;
if (state.bucketUsageFirstCalls === 1) {
return undefined;
}
return {
usage_id: 'usage-prev',
tenant: 'test-tenant',
client_id: 'client-1',
contract_line_id: 'plan-1',
service_catalog_id: 'service-1',
period_start: '2025-01-01',
period_end: '2025-01-31',
minutes_used: 1800,
overage_minutes: 0,
rolled_over_minutes: 0,
};
}
if (tableName === 'contract_line_service_configuration') {
return {
config_id: 'bucket-config-1',
};
}
if (tableName === 'contract_line_service_bucket_config') {
return {
config_id: 'bucket-config-1',
contract_line_id: 'plan-1',
service_catalog_id: 'service-1',
total_minutes: 2400,
allow_rollover: true,
tenant: 'test-tenant',
};
}
return undefined;
});
builder.insert = vi.fn().mockImplementation((payload: Record<string, unknown>) => {
state.insertedRecord = payload;
return {
returning: vi.fn().mockResolvedValue([
{
usage_id: 'usage-new',
...payload,
},
]),
};
});
return builder;
}) as any;
trx.client = {
config: {
tenant: 'test-tenant',
},
};
trx.fn = {
now: () => 'NOW',
};
const record = await findOrCreateCurrentBucketUsageRecord(
trx,
'client-1',
'service-1',
'2025-02-10T00:00:00Z',
);
expect(state.insertedRecord).toMatchObject({
tenant: 'test-tenant',
client_id: 'client-1',
contract_line_id: 'plan-1',
service_catalog_id: 'service-1',
period_start: '2025-02-01',
period_end: '2025-02-28',
rolled_over_minutes: 600,
});
expect(record).toMatchObject({
usage_id: 'usage-new',
period_start: '2025-02-01',
period_end: '2025-02-28',
rolled_over_minutes: 600,
});
});
});