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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
160 lines
5.0 KiB
TypeScript
160 lines
5.0 KiB
TypeScript
import { beforeAll, describe, expect, it } from 'vitest';
|
|
|
|
import type { Envelope } from '../../types';
|
|
import { getNodeTypeRegistry } from '../../registries/nodeTypeRegistry';
|
|
import { getActionRegistryV2 } from '../../registries/actionRegistry';
|
|
import { registerDefaultNodes } from '../registerDefaultNodes';
|
|
import { registerCrmActions } from '../../actions/businessOperations/crm';
|
|
|
|
describe('workflow runtime node smoke for crm actions', () => {
|
|
beforeAll(() => {
|
|
const nodeRegistry = getNodeTypeRegistry();
|
|
if (!nodeRegistry.get('action.call')) {
|
|
registerDefaultNodes();
|
|
}
|
|
|
|
const actionRegistry = getActionRegistryV2();
|
|
if (!actionRegistry.get('crm.find_quotes', 1)) {
|
|
registerCrmActions();
|
|
}
|
|
});
|
|
|
|
it('T013: action.call can execute a follow-up crm action, saveAs output, and use it in a downstream expression', async () => {
|
|
const nodeRegistry = getNodeTypeRegistry();
|
|
const actionRegistry = getActionRegistryV2();
|
|
|
|
const actionCallNode = nodeRegistry.get('action.call');
|
|
const assignNode = nodeRegistry.get('transform.assign');
|
|
const findQuotes = actionRegistry.get('crm.find_quotes', 1);
|
|
|
|
if (!actionCallNode || !assignNode || !findQuotes) {
|
|
throw new Error('Required runtime registrations are missing');
|
|
}
|
|
|
|
const originalHandler = findQuotes.handler;
|
|
|
|
try {
|
|
findQuotes.handler = async () => ({
|
|
quotes: [
|
|
{
|
|
activity_id: '00000000-0000-0000-0000-000000000001',
|
|
quote_id: '00000000-0000-0000-0000-000000000001',
|
|
quote_number: 'Q-1001',
|
|
status: 'draft',
|
|
client_id: '00000000-0000-0000-0000-000000000111',
|
|
contact_id: null,
|
|
title: 'Follow-up Quote',
|
|
quote_date: '2026-04-26T12:00:00.000Z',
|
|
valid_until: '2026-05-26T12:00:00.000Z',
|
|
currency_code: 'USD',
|
|
subtotal: 5000,
|
|
discount_total: 0,
|
|
tax: 0,
|
|
total_amount: 5000,
|
|
sent_at: null,
|
|
converted_contract_id: null,
|
|
converted_invoice_id: null,
|
|
is_template: false,
|
|
},
|
|
],
|
|
first_quote: {
|
|
quote_id: '00000000-0000-0000-0000-000000000001',
|
|
quote_number: 'Q-1001',
|
|
status: 'draft',
|
|
client_id: '00000000-0000-0000-0000-000000000111',
|
|
contact_id: null,
|
|
title: 'Follow-up Quote',
|
|
quote_date: '2026-04-26T12:00:00.000Z',
|
|
valid_until: '2026-05-26T12:00:00.000Z',
|
|
currency_code: 'USD',
|
|
subtotal: 5000,
|
|
discount_total: 0,
|
|
tax: 0,
|
|
total_amount: 5000,
|
|
sent_at: null,
|
|
converted_contract_id: null,
|
|
converted_invoice_id: null,
|
|
is_template: false,
|
|
},
|
|
count: 1,
|
|
pagination: {
|
|
page: 1,
|
|
page_size: 10,
|
|
},
|
|
});
|
|
|
|
const nowIso = () => new Date().toISOString();
|
|
|
|
let env: Envelope = {
|
|
v: 1,
|
|
run: {
|
|
id: 'run-1',
|
|
workflowId: 'workflow-1',
|
|
workflowVersion: 1,
|
|
startedAt: nowIso(),
|
|
},
|
|
payload: {},
|
|
meta: {},
|
|
vars: {},
|
|
};
|
|
|
|
const nodeCtx = {
|
|
runId: 'run-1',
|
|
stepPath: 'steps.crm-find',
|
|
tenantId: 'tenant-1',
|
|
nowIso,
|
|
publishWait: async () => {},
|
|
actions: {
|
|
call: async (actionId: string, version: number, args: unknown) => {
|
|
const action = actionRegistry.get(actionId, version);
|
|
if (!action) throw new Error(`Unknown action ${actionId}@${version}`);
|
|
|
|
const parsedInput = action.inputSchema.parse(args);
|
|
const output = await action.handler(parsedInput, {
|
|
runId: 'run-1',
|
|
stepPath: 'steps.crm-find',
|
|
idempotencyKey: 'idem-1',
|
|
attempt: 1,
|
|
nowIso,
|
|
env,
|
|
tenantId: 'tenant-1',
|
|
} as any);
|
|
|
|
return action.outputSchema.parse(output);
|
|
},
|
|
},
|
|
};
|
|
|
|
const actionConfig = actionCallNode.configSchema.parse({
|
|
actionId: 'crm.find_quotes',
|
|
version: 1,
|
|
inputMapping: {
|
|
client_id: '00000000-0000-0000-0000-000000000111',
|
|
pageSize: 10,
|
|
on_empty: 'return_empty',
|
|
},
|
|
saveAs: 'crmQuotes',
|
|
});
|
|
|
|
env = await actionCallNode.handler(env, actionConfig, nodeCtx as any) as Envelope;
|
|
|
|
const assignConfig = assignNode.configSchema.parse({
|
|
assign: {
|
|
'payload.crmQuoteCount': { $expr: 'vars.crmQuotes.count' },
|
|
'payload.crmFirstQuoteTitle': { $expr: 'vars.crmQuotes.quotes[0].title' },
|
|
},
|
|
});
|
|
|
|
env = await assignNode.handler(env, assignConfig, {
|
|
...nodeCtx,
|
|
stepPath: 'steps.assign-output',
|
|
} as any) as Envelope;
|
|
|
|
expect((env.payload as any).crmQuoteCount).toBe(1);
|
|
expect((env.payload as any).crmFirstQuoteTitle).toBe('Follow-up Quote');
|
|
} finally {
|
|
findQuotes.handler = originalHandler;
|
|
}
|
|
});
|
|
});
|