@aifabrix/builder 2.52.0 → 2.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/deploy-config.js +3 -2
- package/lib/app/down.js +16 -1
- package/lib/channels/add-channel.js +134 -57
- package/lib/channels/approval-guides/chatgpt.js +1 -1
- package/lib/channels/approval-guides/index.js +3 -0
- package/lib/channels/approval-guides/microsoft-copilot.js +27 -14
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-add-existing.js +72 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +107 -14
- package/lib/channels/channel-status-display.js +36 -3
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +251 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +285 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +103 -0
- package/lib/channels/microsoft-copilot-package.js +178 -52
- package/lib/channels/microsoft-copilot-validate.js +227 -0
- package/lib/cli/attach-default-help-examples.js +102 -0
- package/lib/cli/index.js +4 -0
- package/lib/cli/infra-guided.js +13 -6
- package/lib/cli/setup-credential-deployment.js +7 -5
- package/lib/cli/setup-infra.js +31 -4
- package/lib/cli/setup-utility-validate.js +1 -0
- package/lib/commands/channel-add-setup.js +28 -9
- package/lib/commands/channel.js +14 -10
- package/lib/commands/credential-push.js +37 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/package-score.js +72 -0
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +163 -166
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/deployment/deployer-poll-run.js +2 -2
- package/lib/deployment/deployer.js +1 -1
- package/lib/deployment/environment.js +2 -2
- package/lib/external-system/deploy.js +11 -21
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +476 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +433 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/lab/package-parity-score.js +417 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +20 -0
- package/lib/programmatic/help-content/channel-onboarding.md +35 -0
- package/lib/programmatic/help-content/cip-overview.md +27 -0
- package/lib/programmatic/help-content/connected-system-ui.md +70 -0
- package/lib/programmatic/help-content/dimensions-guide.md +23 -0
- package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
- package/lib/programmatic/help-content/overview.md +25 -0
- package/lib/programmatic/help-content/subscription-guide.md +27 -0
- package/lib/programmatic/help-content/workflow.md +50 -0
- package/lib/programmatic/openapi-component-schemas.js +7 -1
- package/lib/programmatic/openapi-descriptions.js +46 -0
- package/lib/programmatic/openapi-envelope-examples.js +280 -0
- package/lib/programmatic/openapi-operation-bindings.js +50 -0
- package/lib/programmatic/openapi-request-schemas.js +40 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +76 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +25 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +43 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/persist-supplied-openapi.js +180 -0
- package/lib/programmatic/route-handler-map.js +35 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +77 -0
- package/lib/role-assistant/evidence-sync.js +55 -7
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +88 -93
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-attachments.js +118 -0
- package/lib/role-assistant/test-runner-bid-apply.js +201 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-start-payload.js +96 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +249 -20
- package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
- package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
- package/lib/role-assistant/test-runner-workhub.js +152 -107
- package/lib/role-assistant/test-runner.js +5 -63
- package/lib/role-assistant/test-suite.js +112 -0
- package/lib/schema/application-schema.json +56 -6
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/compose-generator.js +10 -1
- package/lib/utils/compose-traefik-extra-routes.js +107 -0
- package/lib/utils/docker-build.js +2 -2
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/promote-dev-docker-image.js +102 -18
- package/lib/utils/transient-developer-id.js +28 -0
- package/package.json +14 -16
- package/templates/README.md +12 -1
- package/templates/applications/dataplane/application.yaml +10 -2
- package/templates/applications/dataplane/env.template +7 -6
- package/templates/applications/miso-controller/application.yaml +23 -8
- package/templates/applications/miso-controller/env.template +8 -2
- package/templates/applications/miso-controller/rbac.yaml +1 -0
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +6 -4
- package/templates/channels/chatgpt/integration/README.md +4 -5
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +14 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +30 -0
- package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +93 -22
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -19
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +15 -0
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- package/templates/python/docker-compose.hbs +36 -0
- package/templates/typescript/docker-compose.hbs +36 -0
- package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +0 -374
- package/.cursor/process/README.md +0 -9
- package/.cursor/rules/anchor-docs.mdc +0 -15
- package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
- package/.cursor/rules/cli-layout.mdc +0 -81
- package/.cursor/rules/cli-product-neutral.mdc +0 -43
- package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
- package/.cursor/rules/docs-rules.mdc +0 -32
- package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
- package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
- package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
- package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
- package/.cursor/rules/project-rules.mdc +0 -913
- package/.cursor/skills/README.md +0 -15
- package/.dockerignore +0 -52
- package/.eslintignore +0 -22
- package/.markdownlint.json +0 -14
- package/anchor-docs/README.md +0 -10
- package/anchor-docs/_TEMPLATE +0 -24
- package/babel.config.js +0 -6
- package/bin/builder-api-cleanup.js +0 -26
- package/bin/builder-api-nest.js +0 -10
- package/bin/builder-api-spike.js +0 -19
- package/enterprise-knowledge/README.md +0 -50
- package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
- package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
- package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
- package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
- package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
- package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
- package/integration/README.md +0 -81
- package/integration/github/README.md +0 -53
- package/integration/github/application.json +0 -21
- package/integration/github/deploy.js +0 -72
- package/integration/github/env.template +0 -11
- package/integration/github/github-datasource-issue-comments.json +0 -239
- package/integration/github/github-datasource-issues.json +0 -399
- package/integration/github/github-datasource-repository.json +0 -354
- package/integration/github/github-deploy.json +0 -1202
- package/integration/github/github-system.json +0 -82
- package/integration/hubspot-test/README.md +0 -159
- package/integration/hubspot-test/application.json +0 -54
- package/integration/hubspot-test/companies.json +0 -2048
- package/integration/hubspot-test/create-hubspot.js +0 -523
- package/integration/hubspot-test/env.template +0 -4
- package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
- package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
- package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
- package/integration/hubspot-test/hubspot-test-system.json +0 -74
- package/integration/hubspot-test/rbac.json +0 -166
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
- package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
- package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
- package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
- package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
- package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
- package/integration/hubspot-test/test-dataplane-down.js +0 -209
- package/integration/hubspot-test/test.js +0 -1588
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
- package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
- package/integration/roundtrip-test-local/README.md +0 -143
- package/integration/roundtrip-test-local/application.yaml +0 -13
- package/integration/roundtrip-test-local/env.template +0 -15
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
- package/integration/roundtrip-test-local2/README.md +0 -143
- package/integration/roundtrip-test-local2/application.yaml +0 -13
- package/integration/roundtrip-test-local2/env.template +0 -15
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
- package/integration/test/wizard.yaml +0 -8
- package/jest.config.coverage.js +0 -37
- package/jest.config.default.js +0 -11
- package/jest.config.integration.fixtures.js +0 -22
- package/jest.config.integration.js +0 -33
- package/jest.config.isolated.js +0 -11
- package/jest.config.manual.js +0 -30
- package/jest.global-live-fabrix-hooks.js +0 -36
- package/jest.isolated-projects.js +0 -260
- package/jest.projects.js +0 -76
- package/packages/builder-api/README.md +0 -120
- package/packages/builder-api/package.json +0 -30
- package/packages/builder-api/src/app.module.ts +0 -7
- package/packages/builder-api/src/auth/auth.module.ts +0 -5
- package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
- package/packages/builder-api/src/main.ts +0 -16
- package/packages/builder-api/tsconfig.json +0 -15
- package/scripts/check-builder-api-architecture-grep.js +0 -79
- package/scripts/check-datasource-test-run-schema-sync.js +0 -34
- package/scripts/ci-fix.sh +0 -19
- package/scripts/ci-simulate.sh +0 -19
- package/scripts/diagnose-cli.js +0 -150
- package/scripts/docker_image_content_gate.js +0 -47
- package/scripts/docker_image_content_gate.sh +0 -5
- package/scripts/export-builder-api-evidence.js +0 -262
- package/scripts/install-local.js +0 -490
- package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
- package/scripts/lib/builder-api-docker-staging-core.js +0 -183
- package/scripts/lib/docker-image-content-gate-core.js +0 -166
- package/scripts/lib/plan-actor.js +0 -109
- package/scripts/lib/plan-config.js +0 -20
- package/scripts/lib/plan-gates.js +0 -154
- package/scripts/lib/plan-meta.js +0 -104
- package/scripts/lib/plan-parse.js +0 -162
- package/scripts/lib/plan-prevalidate-gates.js +0 -337
- package/scripts/lib/plan-run-core.js +0 -125
- package/scripts/lib/plan-run-report.js +0 -119
- package/scripts/plan-approve.js +0 -24
- package/scripts/plan-archive.js +0 -49
- package/scripts/plan-run.js +0 -13
- package/scripts/plan-set-actor.js +0 -21
- package/scripts/plan-validate.js +0 -80
- package/scripts/pnpm/af.mjs +0 -110
- package/scripts/pnpm/check-quiet.mjs +0 -32
- package/scripts/pnpm/dev-install.mjs +0 -29
- package/scripts/pnpm/dev-reload.mjs +0 -18
- package/scripts/pnpm/help.mjs +0 -55
- package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
- package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
- package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
- package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
- package/scripts/pnpm/lib/spinner.mjs +0 -42
- package/scripts/pnpm-global-remove.js +0 -48
- package/scripts/stage_builder_api_for_docker.js +0 -65
- package/scripts/sync-builder-api-template.js +0 -44
- package/scripts/test-dataplane-bootstrap.js +0 -331
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant test CLI envelope + human summary output.
|
|
3
|
+
* @fileoverview Split from role-assistant.js for max-lines / complexity
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const logger = require('../utils/logger');
|
|
9
|
+
const { VERDICT } = require('../lifecycle/product-model');
|
|
10
|
+
const {
|
|
11
|
+
sectionTitle,
|
|
12
|
+
headerKeyValue,
|
|
13
|
+
formatSuccessLine,
|
|
14
|
+
formatSuccessParagraph,
|
|
15
|
+
formatWarningLine,
|
|
16
|
+
formatBlockingError,
|
|
17
|
+
metadata
|
|
18
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Bucket a lifecycle step status into ok | failed | skipped | other.
|
|
22
|
+
* @param {string} status
|
|
23
|
+
* @returns {'ok'|'failed'|'skipped'|'other'}
|
|
24
|
+
*/
|
|
25
|
+
function bucketStepStatus(status) {
|
|
26
|
+
const normalized = String(status || 'ok').toLowerCase();
|
|
27
|
+
if (normalized === 'ok' || normalized === 'verified' || normalized === 'passed') {
|
|
28
|
+
return 'ok';
|
|
29
|
+
}
|
|
30
|
+
if (normalized === 'failed' || normalized === 'error') {
|
|
31
|
+
return 'failed';
|
|
32
|
+
}
|
|
33
|
+
if (normalized === 'skipped' || normalized === 'n/a' || normalized === 'not_applicable') {
|
|
34
|
+
return 'skipped';
|
|
35
|
+
}
|
|
36
|
+
return 'other';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Summarize lifecycle stepsRun rows (API uses step/status/datasourceKey, not pillar).
|
|
41
|
+
* @param {Array<Object|string>} steps
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function formatStepsRunSummary(steps) {
|
|
45
|
+
const byStep = Object.create(null);
|
|
46
|
+
for (const row of steps) {
|
|
47
|
+
if (!row || typeof row !== 'object') {
|
|
48
|
+
const label = String(row);
|
|
49
|
+
byStep[label] = byStep[label] || { ok: 0, failed: 0, skipped: 0, other: 0 };
|
|
50
|
+
byStep[label].other += 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const stepName = String(row.step || row.pillar || row.name || 'step').trim() || 'step';
|
|
54
|
+
const status = row.status || (row.success === false ? 'failed' : 'ok');
|
|
55
|
+
byStep[stepName] = byStep[stepName] || { ok: 0, failed: 0, skipped: 0, other: 0 };
|
|
56
|
+
byStep[stepName][bucketStepStatus(status)] += 1;
|
|
57
|
+
}
|
|
58
|
+
return Object.keys(byStep)
|
|
59
|
+
.map((stepName) => {
|
|
60
|
+
const counts = byStep[stepName];
|
|
61
|
+
const parts = [];
|
|
62
|
+
if (counts.ok) parts.push(`${counts.ok} ok`);
|
|
63
|
+
if (counts.failed) parts.push(`${counts.failed} failed`);
|
|
64
|
+
if (counts.skipped) parts.push(`${counts.skipped} skipped`);
|
|
65
|
+
if (counts.other) parts.push(`${counts.other} other`);
|
|
66
|
+
return parts.length > 0 ? `${stepName} (${parts.join(', ')})` : stepName;
|
|
67
|
+
})
|
|
68
|
+
.join('; ');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function mapCaseEnvelopeRow(row) {
|
|
72
|
+
return {
|
|
73
|
+
caseId: row.caseId,
|
|
74
|
+
sourceFile: row.sourceFile,
|
|
75
|
+
passed: row.passed,
|
|
76
|
+
lifecycleState: row.lifecycleState,
|
|
77
|
+
assertions: row.assertions,
|
|
78
|
+
failedAssertions: row.failedAssertions,
|
|
79
|
+
warnings: Array.isArray(row.warnings) ? row.warnings : [],
|
|
80
|
+
execution: row.execution || null
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function attachEnvelopeCases(envelope, caseResult) {
|
|
85
|
+
if (Array.isArray(caseResult)) {
|
|
86
|
+
envelope.cases = caseResult.map(mapCaseEnvelopeRow);
|
|
87
|
+
const passed = envelope.cases.filter((c) => c.passed).length;
|
|
88
|
+
envelope.summary = {
|
|
89
|
+
total: envelope.cases.length,
|
|
90
|
+
passed,
|
|
91
|
+
failed: envelope.cases.length - passed
|
|
92
|
+
};
|
|
93
|
+
envelope.case = envelope.cases.length === 1 ? envelope.cases[0] : null;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (caseResult) {
|
|
97
|
+
envelope.case = mapCaseEnvelopeRow(caseResult);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildRoleAssistantTestEnvelope(roleKey, verdict, portalUrl, result, opts, caseResult = null) {
|
|
102
|
+
const report = result.report || {};
|
|
103
|
+
const multiCase = Array.isArray(caseResult) && caseResult.length > 1;
|
|
104
|
+
const envelope = {
|
|
105
|
+
roleKey,
|
|
106
|
+
command: 'role-assistant test',
|
|
107
|
+
mode: opts.suite || multiCase ? 'suite' : 'single',
|
|
108
|
+
suite: opts.suite || null,
|
|
109
|
+
verdict,
|
|
110
|
+
portalUrl,
|
|
111
|
+
openApiPublished: result.openApiPublished === true,
|
|
112
|
+
certification: report.certification || null,
|
|
113
|
+
recommendations: Array.isArray(report.recommendations) ? report.recommendations : [],
|
|
114
|
+
warnings: Array.isArray(report.warnings) ? report.warnings : [],
|
|
115
|
+
stepsRun: result.stepsRun || [],
|
|
116
|
+
case: null,
|
|
117
|
+
cases: null,
|
|
118
|
+
summary: null
|
|
119
|
+
};
|
|
120
|
+
attachEnvelopeCases(envelope, caseResult);
|
|
121
|
+
if (opts.verbose || opts.debug) {
|
|
122
|
+
envelope.report = report;
|
|
123
|
+
}
|
|
124
|
+
if (opts.debug) {
|
|
125
|
+
envelope.debug = {
|
|
126
|
+
observation: caseResult && !Array.isArray(caseResult) ? caseResult.observation : null,
|
|
127
|
+
lifecycleResult: result
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return envelope;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function displayCaseResultSummary(caseResult) {
|
|
134
|
+
if (!caseResult) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
logger.log(headerKeyValue('Case:', caseResult.caseId));
|
|
138
|
+
logger.log(headerKeyValue('Case file:', caseResult.sourceFile));
|
|
139
|
+
logger.log(headerKeyValue('Observed state:', caseResult.lifecycleState));
|
|
140
|
+
if (caseResult.execution) {
|
|
141
|
+
logger.log(headerKeyValue('Execution id:', caseResult.execution.processExecutionId || 'n/a'));
|
|
142
|
+
logger.log(headerKeyValue('Final runtime status:', caseResult.execution.finalStatus || 'n/a'));
|
|
143
|
+
}
|
|
144
|
+
if (caseResult.passed) {
|
|
145
|
+
logger.log(formatSuccessLine('Case assertions passed.'));
|
|
146
|
+
const warnings = Array.isArray(caseResult.warnings) ? caseResult.warnings : [];
|
|
147
|
+
for (const row of warnings) {
|
|
148
|
+
logger.log(formatWarningLine(row.message || String(row)));
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
logger.log(formatBlockingError('Case assertions failed.'));
|
|
153
|
+
for (const row of caseResult.failedAssertions) {
|
|
154
|
+
logger.log(formatWarningLine(row.message));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function displayPortalAndVerdict(portalUrl, verdict) {
|
|
159
|
+
if (portalUrl) {
|
|
160
|
+
logger.log(headerKeyValue('Portal:', portalUrl));
|
|
161
|
+
} else {
|
|
162
|
+
logger.log(formatWarningLine('Portal link unavailable. Configure dataplane public URL and retry.'));
|
|
163
|
+
}
|
|
164
|
+
const verdictMessage =
|
|
165
|
+
verdict === VERDICT.VERIFIED
|
|
166
|
+
? 'Lifecycle checks passed. Open portal for full execution steps and logs.'
|
|
167
|
+
: 'Lifecycle checks are not fully verified. Open portal to inspect blocking steps.';
|
|
168
|
+
if (verdict === VERDICT.VERIFIED) {
|
|
169
|
+
logger.log(formatSuccessParagraph(verdictMessage));
|
|
170
|
+
} else {
|
|
171
|
+
logger.log(formatWarningLine(verdictMessage));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function displayRoleAssistantTestSummary(roleKey, verdict, portalUrl, result, caseResult = null) {
|
|
176
|
+
logger.log('');
|
|
177
|
+
logger.log(sectionTitle(`${String(roleKey).toUpperCase()} ROLE ASSISTANT TEST`));
|
|
178
|
+
logger.log('');
|
|
179
|
+
logger.log(headerKeyValue('Role key:', roleKey));
|
|
180
|
+
displayPortalAndVerdict(portalUrl, verdict);
|
|
181
|
+
const steps = Array.isArray(result.stepsRun) ? result.stepsRun : [];
|
|
182
|
+
if (steps.length > 0) {
|
|
183
|
+
logger.log(metadata(`Steps run: ${formatStepsRunSummary(steps)}`));
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(caseResult)) {
|
|
186
|
+
for (const row of caseResult) {
|
|
187
|
+
displayCaseResultSummary(row);
|
|
188
|
+
}
|
|
189
|
+
const passed = caseResult.filter((c) => c.passed).length;
|
|
190
|
+
logger.log(headerKeyValue('Suite summary:', `${passed}/${caseResult.length} passed`));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
displayCaseResultSummary(caseResult);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
formatStepsRunSummary,
|
|
198
|
+
buildRoleAssistantTestEnvelope,
|
|
199
|
+
displayRoleAssistantTestSummary
|
|
200
|
+
};
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
+
const path = require('path');
|
|
11
12
|
const logger = require('../utils/logger');
|
|
12
13
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
13
14
|
const {
|
|
@@ -22,6 +23,11 @@ const {
|
|
|
22
23
|
loadRoleAssistantTestCase,
|
|
23
24
|
evaluateRoleAssistantTestCase
|
|
24
25
|
} = require('../role-assistant/test-cases');
|
|
26
|
+
const {
|
|
27
|
+
resolveCasesRoot,
|
|
28
|
+
discoverSuiteCaseFiles,
|
|
29
|
+
filterDiscoveredCases
|
|
30
|
+
} = require('../role-assistant/test-suite');
|
|
25
31
|
const {
|
|
26
32
|
isExecutableRoleAssistantCase,
|
|
27
33
|
runRoleAssistantCaseLifecycle,
|
|
@@ -32,66 +38,39 @@ const { collectString } = require('./governance');
|
|
|
32
38
|
const {
|
|
33
39
|
sectionTitle,
|
|
34
40
|
headerKeyValue,
|
|
35
|
-
formatSuccessLine,
|
|
36
|
-
formatSuccessParagraph,
|
|
37
|
-
formatWarningLine,
|
|
38
|
-
formatBlockingError,
|
|
39
41
|
metadata
|
|
40
42
|
} = require('../utils/cli-test-layout-chalk');
|
|
43
|
+
const {
|
|
44
|
+
formatStepsRunSummary,
|
|
45
|
+
buildRoleAssistantTestEnvelope,
|
|
46
|
+
displayRoleAssistantTestSummary
|
|
47
|
+
} = require('./role-assistant-test-output');
|
|
41
48
|
const {
|
|
42
49
|
bareRoleKey,
|
|
43
50
|
roleAssistantSystemKey
|
|
44
51
|
} = require('../utils/role-assistant-paths');
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*/
|
|
51
|
-
function bucketStepStatus(status) {
|
|
52
|
-
const normalized = String(status || 'ok').toLowerCase();
|
|
53
|
-
if (normalized === 'ok' || normalized === 'verified' || normalized === 'passed') {
|
|
54
|
-
return 'ok';
|
|
55
|
-
}
|
|
56
|
-
if (normalized === 'failed' || normalized === 'error') {
|
|
57
|
-
return 'failed';
|
|
58
|
-
}
|
|
59
|
-
if (normalized === 'skipped' || normalized === 'n/a' || normalized === 'not_applicable') {
|
|
60
|
-
return 'skipped';
|
|
61
|
-
}
|
|
62
|
-
return 'other';
|
|
63
|
-
}
|
|
53
|
+
function roleAssistantGroupHelpAfter() {
|
|
54
|
+
return `
|
|
55
|
+
Subcommands:
|
|
56
|
+
test <roleKey> Run lifecycle checks, optional YAML cases/suites, and print a portal deep link
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
byStep[stepName] = byStep[stepName] || { ok: 0, failed: 0, skipped: 0, other: 0 };
|
|
82
|
-
byStep[stepName][bucketStepStatus(status)] += 1;
|
|
83
|
-
}
|
|
84
|
-
return Object.keys(byStep)
|
|
85
|
-
.map((stepName) => {
|
|
86
|
-
const counts = byStep[stepName];
|
|
87
|
-
const parts = [];
|
|
88
|
-
if (counts.ok) parts.push(`${counts.ok} ok`);
|
|
89
|
-
if (counts.failed) parts.push(`${counts.failed} failed`);
|
|
90
|
-
if (counts.skipped) parts.push(`${counts.skipped} skipped`);
|
|
91
|
-
if (counts.other) parts.push(`${counts.other} other`);
|
|
92
|
-
return parts.length > 0 ? `${stepName} (${parts.join(', ')})` : stepName;
|
|
93
|
-
})
|
|
94
|
-
.join('; ');
|
|
58
|
+
Package workspace (top-level commands — same digital-worker package):
|
|
59
|
+
download / validate / upload / deploy <roleKey> Manage role-assistant/ra-<roleKey>/
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
$ aifabrix role-assistant test customer-support-bot
|
|
63
|
+
$ aifabrix role-assistant test customer-support-bot --suite 001
|
|
64
|
+
$ aifabrix role-assistant test customer-support-bot --list-cases
|
|
65
|
+
$ aifabrix role-assistant test customer-support-bot --env tst --json
|
|
66
|
+
$ aifabrix upload customer-support-bot --env dev
|
|
67
|
+
$ aifabrix test ra-customer-support-bot
|
|
68
|
+
|
|
69
|
+
Notes:
|
|
70
|
+
- Local packages live under role-assistant/ra-<roleKey>/ (bare <roleKey> in CLI args).
|
|
71
|
+
- test uses the public/running worker by default; pass --publish (or upload) to refresh from local files.
|
|
72
|
+
- See: aifabrix role-assistant test -h
|
|
73
|
+
`;
|
|
95
74
|
}
|
|
96
75
|
|
|
97
76
|
function roleAssistantHelpAfter() {
|
|
@@ -101,15 +80,19 @@ Examples:
|
|
|
101
80
|
$ aifabrix role-assistant test customer-support-bot --case smoke
|
|
102
81
|
$ aifabrix role-assistant test customer-support-bot --case tests/customer-address-change.yaml
|
|
103
82
|
$ aifabrix role-assistant test customer-support-bot --subject-email ops@example.com
|
|
104
|
-
$ aifabrix role-assistant test customer-support-bot --
|
|
105
|
-
$ aifabrix role-assistant test customer-support-bot --
|
|
83
|
+
$ aifabrix role-assistant test customer-support-bot --suite 001
|
|
84
|
+
$ aifabrix role-assistant test customer-support-bot --list-cases
|
|
85
|
+
$ aifabrix role-assistant test customer-support-bot --suite 001 --fail-fast --json
|
|
86
|
+
$ aifabrix test ra-customer-support-bot
|
|
106
87
|
|
|
107
88
|
Notes:
|
|
89
|
+
- <roleKey> is bare (e.g. customer-support-bot); package folder is role-assistant/ra-<roleKey>/.
|
|
108
90
|
- Uses the public/running Role Assistant on the dataplane by default (does not publish local datasources).
|
|
109
91
|
- Use \`aifabrix upload <roleKey> --env …\` (or \`--publish\`) when you intentionally refresh the package.
|
|
110
92
|
- Runs lifecycle checks, then prints a portal deep link for full step-by-step status.
|
|
111
93
|
- Case files with input.message/input.content execute Work Hub plan→accept (or work.start when executionPath: intent).
|
|
112
94
|
- answers[] and approvals[] are matched to runtime wait payloads (questionKey/requestKey/waitReason).
|
|
95
|
+
- --suite runs YAML under role-assistant/ra-<roleKey>/tests/<suite>/ (preferred), or tests/ra-<roleKey>/<roleKey>/<suite>/, or legacy tests/role-assistant/ra-<roleKey>/<suite>/ (override with --cases-root).
|
|
113
96
|
- Use the portal URL for detailed human review while automation reads CLI/JSON output.
|
|
114
97
|
`;
|
|
115
98
|
}
|
|
@@ -146,96 +129,129 @@ function normalizeRoleAssistantTestOptions(options, cmd) {
|
|
|
146
129
|
certificationProfile: options.certificationProfile || 'bronze',
|
|
147
130
|
case: trimOptionalString(options.case),
|
|
148
131
|
testsDir: trimOptionalString(options.testsDir),
|
|
132
|
+
suite: trimOptionalString(options.suite),
|
|
133
|
+
casesRoot: trimOptionalString(options.casesRoot),
|
|
134
|
+
listCases: optionOrFlag(options, 'listCases', cmd, '--list-cases'),
|
|
135
|
+
failFast: optionOrFlag(options, 'failFast', cmd, '--fail-fast'),
|
|
149
136
|
run: true
|
|
150
137
|
};
|
|
151
138
|
}
|
|
152
139
|
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
portalUrl,
|
|
160
|
-
openApiPublished: result.openApiPublished === true,
|
|
161
|
-
certification: report.certification || null,
|
|
162
|
-
recommendations: Array.isArray(report.recommendations) ? report.recommendations : [],
|
|
163
|
-
warnings: Array.isArray(report.warnings) ? report.warnings : [],
|
|
164
|
-
stepsRun: result.stepsRun || [],
|
|
165
|
-
case: caseResult
|
|
166
|
-
? {
|
|
167
|
-
caseId: caseResult.caseId,
|
|
168
|
-
sourceFile: caseResult.sourceFile,
|
|
169
|
-
passed: caseResult.passed,
|
|
170
|
-
lifecycleState: caseResult.lifecycleState,
|
|
171
|
-
assertions: caseResult.assertions,
|
|
172
|
-
failedAssertions: caseResult.failedAssertions,
|
|
173
|
-
warnings: Array.isArray(caseResult.warnings) ? caseResult.warnings : [],
|
|
174
|
-
execution: caseResult.execution || null
|
|
175
|
-
}
|
|
176
|
-
: null
|
|
177
|
-
};
|
|
178
|
-
if (opts.verbose || opts.debug) {
|
|
179
|
-
envelope.report = report;
|
|
140
|
+
function computeRoleAssistantExitCode(verdict, caseResult) {
|
|
141
|
+
if (Array.isArray(caseResult)) {
|
|
142
|
+
if (caseResult.some((row) => row && row.passed === false)) {
|
|
143
|
+
return 2;
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
180
146
|
}
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
147
|
+
if (caseResult) {
|
|
148
|
+
return caseResult.passed ? 0 : 2;
|
|
149
|
+
}
|
|
150
|
+
if (verdict === VERDICT.VERIFIED) {
|
|
151
|
+
return 0;
|
|
186
152
|
}
|
|
187
|
-
return
|
|
153
|
+
return exitCodeFromVerdict(verdict, { partial: verdict === VERDICT.NOT_VERIFIED });
|
|
188
154
|
}
|
|
189
155
|
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
156
|
+
function listRoleAssistantCases(roleKey, opts) {
|
|
157
|
+
const root = resolveCasesRoot(roleKey, { casesRoot: opts.casesRoot || undefined });
|
|
158
|
+
const rows = discoverSuiteCaseFiles(root, { suite: opts.suite || undefined });
|
|
159
|
+
const filtered = filterDiscoveredCases(rows, opts.case);
|
|
160
|
+
if (opts.json) {
|
|
161
|
+
logger.log(
|
|
162
|
+
JSON.stringify(
|
|
163
|
+
{
|
|
164
|
+
roleKey: bareRoleKey(roleKey),
|
|
165
|
+
command: 'role-assistant test',
|
|
166
|
+
mode: 'list-cases',
|
|
167
|
+
casesRoot: root,
|
|
168
|
+
cases: filtered.map((r) => ({
|
|
169
|
+
suite: r.suite,
|
|
170
|
+
file: r.file,
|
|
171
|
+
path: r.absPath,
|
|
172
|
+
caseIdHint: r.caseIdHint
|
|
173
|
+
}))
|
|
174
|
+
},
|
|
175
|
+
null,
|
|
176
|
+
2
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
} else {
|
|
180
|
+
logger.log(sectionTitle('ROLE ASSISTANT CASES'));
|
|
181
|
+
logger.log(headerKeyValue('Cases root:', root));
|
|
182
|
+
for (const row of filtered) {
|
|
183
|
+
logger.log(`${row.suite}\t${row.file}\t${row.absPath}`);
|
|
184
|
+
}
|
|
185
|
+
logger.log(metadata(`total ${filtered.length}`));
|
|
193
186
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function resolveSuiteCaseResults(systemKey, opts, result, verdict, portalUrl) {
|
|
191
|
+
const cwd = process.cwd();
|
|
192
|
+
const root = resolveCasesRoot(systemKey, { casesRoot: opts.casesRoot || undefined, cwd });
|
|
193
|
+
const discovered = discoverSuiteCaseFiles(root, { suite: opts.suite || undefined });
|
|
194
|
+
const selected = filterDiscoveredCases(discovered, opts.case);
|
|
195
|
+
if (selected.length === 0) {
|
|
196
|
+
const { listCasesRootCandidates } = require('../role-assistant/test-suite');
|
|
197
|
+
const tried = opts.casesRoot
|
|
198
|
+
? [root]
|
|
199
|
+
: listCasesRootCandidates(systemKey, cwd);
|
|
200
|
+
throw new Error(
|
|
201
|
+
`No role-assistant cases found under ${root}` +
|
|
202
|
+
(opts.suite ? ` suite=${opts.suite}` : '') +
|
|
203
|
+
`. Looked in: ${tried.join(', ')}`
|
|
204
|
+
);
|
|
200
205
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
const outcomes = [];
|
|
207
|
+
for (const row of selected) {
|
|
208
|
+
const caseOpts = { ...opts, case: row.absPath, testsDir: path.dirname(row.absPath) };
|
|
209
|
+
const one = await resolveRoleAssistantCaseResult(systemKey, caseOpts, result, verdict, portalUrl);
|
|
210
|
+
outcomes.push(one);
|
|
211
|
+
if (opts.failFast && one && one.passed === false) {
|
|
212
|
+
break;
|
|
206
213
|
}
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
logger.log(formatBlockingError('Case assertions failed.'));
|
|
210
|
-
for (const row of caseResult.failedAssertions) {
|
|
211
|
-
logger.log(formatWarningLine(row.message));
|
|
212
214
|
}
|
|
215
|
+
return outcomes;
|
|
213
216
|
}
|
|
214
217
|
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
|
|
218
|
+
async function runRoleAssistantTestAction(roleKey, options, cmd) {
|
|
219
|
+
const opts = normalizeRoleAssistantTestOptions(options, cmd);
|
|
220
|
+
const systemKey = roleAssistantSystemKey(roleKey);
|
|
221
|
+
const displayKey = bareRoleKey(roleKey);
|
|
222
|
+
|
|
223
|
+
if (opts.listCases) {
|
|
224
|
+
return listRoleAssistantCases(systemKey, opts);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const result = await runLifecycleForExternalSystem(systemKey, opts);
|
|
228
|
+
const verdict = lifecycleVerdictFromReport(result.report || {});
|
|
229
|
+
const portalUrl = buildExternalSystemUiUrl(result.dataplaneUiBaseUrl, systemKey);
|
|
230
|
+
|
|
231
|
+
let caseResult = null;
|
|
232
|
+
if (opts.suite) {
|
|
233
|
+
caseResult = await resolveSuiteCaseResults(systemKey, opts, result, verdict, portalUrl);
|
|
222
234
|
} else {
|
|
223
|
-
|
|
235
|
+
caseResult = await resolveRoleAssistantCaseResult(systemKey, opts, result, verdict, portalUrl);
|
|
224
236
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
237
|
+
|
|
238
|
+
if (opts.json) {
|
|
239
|
+
logger.log(
|
|
240
|
+
JSON.stringify(
|
|
241
|
+
buildRoleAssistantTestEnvelope(displayKey, verdict, portalUrl, result, opts, caseResult),
|
|
242
|
+
null,
|
|
243
|
+
2
|
|
244
|
+
)
|
|
245
|
+
);
|
|
231
246
|
} else {
|
|
232
|
-
|
|
247
|
+
displayRoleAssistantTestSummary(displayKey, verdict, portalUrl, result, caseResult);
|
|
233
248
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
249
|
+
|
|
250
|
+
const exitCode = computeRoleAssistantExitCode(verdict, caseResult);
|
|
251
|
+
if (exitCode !== 0) {
|
|
252
|
+
process.exit(exitCode);
|
|
237
253
|
}
|
|
238
|
-
|
|
254
|
+
return exitCode;
|
|
239
255
|
}
|
|
240
256
|
|
|
241
257
|
async function resolveRoleAssistantCaseResult(roleKey, opts, result, verdict, portalUrl) {
|
|
@@ -272,44 +288,6 @@ async function resolveRoleAssistantCaseResult(roleKey, opts, result, verdict, po
|
|
|
272
288
|
return evaluation;
|
|
273
289
|
}
|
|
274
290
|
|
|
275
|
-
function computeRoleAssistantExitCode(verdict, caseResult) {
|
|
276
|
-
if (caseResult) {
|
|
277
|
-
return caseResult.passed ? 0 : 2;
|
|
278
|
-
}
|
|
279
|
-
if (verdict === VERDICT.VERIFIED) {
|
|
280
|
-
return 0;
|
|
281
|
-
}
|
|
282
|
-
return exitCodeFromVerdict(verdict, { partial: verdict === VERDICT.NOT_VERIFIED });
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async function runRoleAssistantTestAction(roleKey, options, cmd) {
|
|
286
|
-
const opts = normalizeRoleAssistantTestOptions(options, cmd);
|
|
287
|
-
// Dataplane ExternalSystem key is always ra-{roleKey}; bare roleKey is display/settings only.
|
|
288
|
-
const systemKey = roleAssistantSystemKey(roleKey);
|
|
289
|
-
const displayKey = bareRoleKey(roleKey);
|
|
290
|
-
const result = await runLifecycleForExternalSystem(systemKey, opts);
|
|
291
|
-
const verdict = lifecycleVerdictFromReport(result.report || {});
|
|
292
|
-
const portalUrl = buildExternalSystemUiUrl(result.dataplaneUiBaseUrl, systemKey);
|
|
293
|
-
const caseResult = await resolveRoleAssistantCaseResult(systemKey, opts, result, verdict, portalUrl);
|
|
294
|
-
|
|
295
|
-
if (opts.json) {
|
|
296
|
-
logger.log(
|
|
297
|
-
JSON.stringify(
|
|
298
|
-
buildRoleAssistantTestEnvelope(displayKey, verdict, portalUrl, result, opts, caseResult),
|
|
299
|
-
null,
|
|
300
|
-
2
|
|
301
|
-
)
|
|
302
|
-
);
|
|
303
|
-
} else {
|
|
304
|
-
displayRoleAssistantTestSummary(displayKey, verdict, portalUrl, result, caseResult);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const exitCode = computeRoleAssistantExitCode(verdict, caseResult);
|
|
308
|
-
if (exitCode !== 0) {
|
|
309
|
-
process.exit(exitCode);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
291
|
async function roleAssistantTestHandler(roleKey, options, cmd) {
|
|
314
292
|
try {
|
|
315
293
|
await runRoleAssistantTestAction(roleKey, options, cmd);
|
|
@@ -322,14 +300,29 @@ async function roleAssistantTestHandler(roleKey, options, cmd) {
|
|
|
322
300
|
function setupRoleAssistantCommands(program) {
|
|
323
301
|
const roleAssistant = program
|
|
324
302
|
.command('role-assistant')
|
|
325
|
-
.description(
|
|
303
|
+
.description(
|
|
304
|
+
'Role Assistant packages: lifecycle checks, YAML cases/suites, and portal deep links'
|
|
305
|
+
)
|
|
306
|
+
.addHelpText('after', roleAssistantGroupHelpAfter());
|
|
326
307
|
|
|
327
308
|
roleAssistant
|
|
328
309
|
.command('test <roleKey>')
|
|
329
|
-
.description(
|
|
310
|
+
.description(
|
|
311
|
+
'Lifecycle checks + optional cases/suites; prints portal deep link (bare <roleKey>)'
|
|
312
|
+
)
|
|
330
313
|
.option('-e, --env <env>', 'Environment: dev, tst, or pro', 'dev')
|
|
331
314
|
.option('--case <caseIdOrPath>', 'Run and evaluate one role-assistant test case')
|
|
332
315
|
.option('--tests-dir <dir>', 'Override role-assistant test case directory')
|
|
316
|
+
.option(
|
|
317
|
+
'--suite <suiteId>',
|
|
318
|
+
'Run all YAML under role-assistant/ra-<roleKey>/tests/<suiteId>/ (or tests/ra-<roleKey>/<roleKey>/<suiteId>/)'
|
|
319
|
+
)
|
|
320
|
+
.option(
|
|
321
|
+
'--cases-root <dir>',
|
|
322
|
+
'Override cases root (default: package tests/, then tests/ra-<roleKey>/<roleKey>/)'
|
|
323
|
+
)
|
|
324
|
+
.option('--list-cases', 'List discoverable case files and exit')
|
|
325
|
+
.option('--fail-fast', 'Stop suite run after the first failing case')
|
|
333
326
|
.option('-v, --verbose', 'Include full lifecycle report payload in --json output')
|
|
334
327
|
.option('-d, --debug', 'Write debug logs under role-assistant/<roleKey>/logs/')
|
|
335
328
|
.option(
|
|
@@ -367,5 +360,9 @@ module.exports = {
|
|
|
367
360
|
buildRoleAssistantTestEnvelope,
|
|
368
361
|
computeRoleAssistantExitCode,
|
|
369
362
|
resolveRoleAssistantCaseResult,
|
|
370
|
-
|
|
363
|
+
resolveSuiteCaseResults,
|
|
364
|
+
listRoleAssistantCases,
|
|
365
|
+
formatStepsRunSummary,
|
|
366
|
+
roleAssistantGroupHelpAfter,
|
|
367
|
+
roleAssistantHelpAfter
|
|
371
368
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @version 2.0.0
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const { capabilitiesIncludes } = require('./capability-manifest');
|
|
9
|
+
const { capabilitiesIncludes, capabilityKeyFromEntry } = require('./capability-manifest');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @param {object|undefined} operations
|
|
@@ -44,6 +44,16 @@ function resolveSingleOpsKey(operations, logicalKey, label) {
|
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* @param {unknown} entry
|
|
49
|
+
* @param {string} userKey
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
function capabilityEntryMatches(entry, userKey) {
|
|
53
|
+
const key = capabilityKeyFromEntry(entry);
|
|
54
|
+
return Boolean(key) && key.toLowerCase() === String(userKey).toLowerCase();
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
/**
|
|
48
58
|
* For --from on copy: when capabilities[] is non-empty, the name must appear there (case-insensitive).
|
|
49
59
|
*
|
|
@@ -56,14 +66,14 @@ function resolveSourceCapabilityForCopy(doc, userKey) {
|
|
|
56
66
|
const u = String(userKey).trim();
|
|
57
67
|
const caps = doc.capabilities;
|
|
58
68
|
if (Array.isArray(caps) && caps.length > 0) {
|
|
59
|
-
const hit = caps.find((c) =>
|
|
69
|
+
const hit = caps.find((c) => capabilityEntryMatches(c, u));
|
|
60
70
|
if (hit === undefined) {
|
|
61
71
|
throw new Error(
|
|
62
72
|
`Source capability "${userKey}" not found in capabilities[]. ` +
|
|
63
73
|
'Use a name that appears in capabilities[] (case-insensitive match is allowed).'
|
|
64
74
|
);
|
|
65
75
|
}
|
|
66
|
-
return
|
|
76
|
+
return capabilityKeyFromEntry(hit) || u;
|
|
67
77
|
}
|
|
68
78
|
return u;
|
|
69
79
|
}
|
|
@@ -79,9 +89,9 @@ function resolveLogicalNameForRemove(doc, userKey) {
|
|
|
79
89
|
const u = String(userKey).trim();
|
|
80
90
|
const caps = doc.capabilities;
|
|
81
91
|
if (Array.isArray(caps) && caps.length > 0) {
|
|
82
|
-
const hit = caps.find((c) =>
|
|
92
|
+
const hit = caps.find((c) => capabilityEntryMatches(c, u));
|
|
83
93
|
if (hit !== undefined) {
|
|
84
|
-
return
|
|
94
|
+
return capabilityKeyFromEntry(hit) || u;
|
|
85
95
|
}
|
|
86
96
|
}
|
|
87
97
|
return u;
|