@aifabrix/builder 2.54.1 → 2.55.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/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +130 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +239 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +79 -0
- package/lib/commands/agent-kit.js +346 -0
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +117 -48
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +7 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +229 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +137 -30
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +291 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +50 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/schema-loader.js +37 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +1 -1
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +78 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -2
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +8 -9
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- package/templates/typescript/docker-compose.hbs +14 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bind a Work Hub execution to the Role Assistant named on copy.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Fail closed on mismatch; trust the CLI role when identity is absent
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
bareRoleKey,
|
|
11
|
+
roleAssistantSystemKey
|
|
12
|
+
} = require('../utils/role-assistant-paths');
|
|
13
|
+
|
|
14
|
+
const PLACEHOLDER = new Set(['', 'unknown', 'none', 'null']);
|
|
15
|
+
|
|
16
|
+
function throwCopyStatus(message, status) {
|
|
17
|
+
const err = new Error(message);
|
|
18
|
+
err.status = status;
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function usableIdentity(value) {
|
|
23
|
+
const token = String(value || '').trim();
|
|
24
|
+
if (!token || PLACEHOLDER.has(token.toLowerCase())) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
return token;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function pushIdentity(acc, value) {
|
|
31
|
+
const token = usableIdentity(value);
|
|
32
|
+
if (token) {
|
|
33
|
+
acc.push(token);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function boundIdentityTokens(status, result) {
|
|
38
|
+
const acc = [];
|
|
39
|
+
const task = result && result.task && typeof result.task === 'object' ? result.task : {};
|
|
40
|
+
pushIdentity(acc, status && status.assistantKey);
|
|
41
|
+
pushIdentity(acc, status && status.digitalWorkerKey);
|
|
42
|
+
pushIdentity(acc, result && result.assistantKey);
|
|
43
|
+
pushIdentity(acc, result && result.digitalWorkerKey);
|
|
44
|
+
pushIdentity(acc, task.assistantKey);
|
|
45
|
+
pushIdentity(acc, task.digitalWorkerKey);
|
|
46
|
+
pushIdentity(acc, task.activeBusinessRoleRef);
|
|
47
|
+
pushIdentity(acc, task.roleKey);
|
|
48
|
+
return [...new Set(acc)];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function identityMatchesSystem(token, systemKey) {
|
|
52
|
+
const expectedSystem = roleAssistantSystemKey(systemKey);
|
|
53
|
+
const expectedBare = bareRoleKey(systemKey).toLowerCase();
|
|
54
|
+
const lower = token.toLowerCase();
|
|
55
|
+
if (lower === expectedSystem.toLowerCase()) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return bareRoleKey(token).toLowerCase() === expectedBare;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function assertCopyBoundToSystem(systemKey, status, result) {
|
|
62
|
+
const tokens = boundIdentityTokens(status, result);
|
|
63
|
+
if (tokens.length === 0) {
|
|
64
|
+
return { bound: false };
|
|
65
|
+
}
|
|
66
|
+
const mismatch = tokens.find((token) => !identityMatchesSystem(token, systemKey));
|
|
67
|
+
if (mismatch) {
|
|
68
|
+
throwCopyStatus('Execution belongs to a different Role Assistant', 409);
|
|
69
|
+
}
|
|
70
|
+
return { bound: true };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
throwCopyStatus,
|
|
75
|
+
boundIdentityTokens,
|
|
76
|
+
assertCopyBoundToSystem
|
|
77
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive copy input.message and expected.* from Work Hub reference payloads.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview No facts dump; do not invent a start message
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { collectObservedCapabilities } = require('./test-cases');
|
|
10
|
+
const { throwCopyStatus } = require('./test-case-copy-bind');
|
|
11
|
+
|
|
12
|
+
const PLACEHOLDER = new Set(['', 'unknown', 'none', 'null']);
|
|
13
|
+
const STATUS_LIFECYCLE = {
|
|
14
|
+
completed: 'completed',
|
|
15
|
+
stopped: 'failed'
|
|
16
|
+
};
|
|
17
|
+
const OBJECTIVE_KEYS = new Set([
|
|
18
|
+
'objective',
|
|
19
|
+
'goal',
|
|
20
|
+
'planObjective',
|
|
21
|
+
'objectiveSummary',
|
|
22
|
+
'acceptedObjective'
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
function usableText(value) {
|
|
26
|
+
const text = String(value || '').trim();
|
|
27
|
+
if (!text || PLACEHOLDER.has(text.toLowerCase())) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
return text;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function collectObjectiveStrings(value, acc, depth) {
|
|
34
|
+
if (depth > 8 || !value) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
value.forEach((item) => collectObjectiveStrings(item, acc, depth + 1));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (typeof value !== 'object') {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
for (const [key, child] of Object.entries(value)) {
|
|
45
|
+
if (OBJECTIVE_KEYS.has(key) && typeof child === 'string') {
|
|
46
|
+
const text = usableText(child);
|
|
47
|
+
if (text) {
|
|
48
|
+
acc.push(text);
|
|
49
|
+
}
|
|
50
|
+
} else if (child && typeof child === 'object') {
|
|
51
|
+
collectObjectiveStrings(child, acc, depth + 1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function startMessageFromWorkReference(result) {
|
|
57
|
+
const goal = usableText(result && result.task && result.task.acceptedIntent && result.task.acceptedIntent.goal);
|
|
58
|
+
if (goal) {
|
|
59
|
+
return goal;
|
|
60
|
+
}
|
|
61
|
+
const found = [];
|
|
62
|
+
collectObjectiveStrings(result, found, 0);
|
|
63
|
+
if (found[0]) {
|
|
64
|
+
return found[0];
|
|
65
|
+
}
|
|
66
|
+
throwCopyStatus('Execution has no reconstructable start message', 422);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function lifecycleStateFromStatus(status) {
|
|
70
|
+
const token = String((status && status.status) || '').trim();
|
|
71
|
+
if (token === 'waiting') {
|
|
72
|
+
const wait = status.wait && typeof status.wait === 'object' ? status.wait : {};
|
|
73
|
+
if (String(wait.answerType || wait.waitReason || '') === 'approval') {
|
|
74
|
+
return 'approval-required';
|
|
75
|
+
}
|
|
76
|
+
return 'question';
|
|
77
|
+
}
|
|
78
|
+
return STATUS_LIFECYCLE[token] || null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resultProjectionExists(result) {
|
|
82
|
+
return Boolean(result && typeof result === 'object' && (result.completion || result.sourceCompleteness));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ranCapabilityKeys(result) {
|
|
86
|
+
if (!result || typeof result !== 'object') {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const fromSteps = [];
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
const steps = Array.isArray(result.steps) ? result.steps : [];
|
|
92
|
+
for (const step of steps) {
|
|
93
|
+
const block = step && step.capabilityExecution && typeof step.capabilityExecution === 'object'
|
|
94
|
+
? step.capabilityExecution
|
|
95
|
+
: {};
|
|
96
|
+
const key = String(block.capabilityKey || '').trim();
|
|
97
|
+
if (key && !seen.has(key)) {
|
|
98
|
+
seen.add(key);
|
|
99
|
+
fromSteps.push(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (fromSteps.length > 0) {
|
|
103
|
+
return fromSteps;
|
|
104
|
+
}
|
|
105
|
+
return collectObservedCapabilities({ steps: result.steps, workResult: result });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildCopyExpected(status, result) {
|
|
109
|
+
const expected = {};
|
|
110
|
+
const lifecycle = lifecycleStateFromStatus(status);
|
|
111
|
+
if (lifecycle) {
|
|
112
|
+
expected.lifecycleState = lifecycle;
|
|
113
|
+
}
|
|
114
|
+
if (resultProjectionExists(result)) {
|
|
115
|
+
expected.honesty = { requireWorkResult: true };
|
|
116
|
+
}
|
|
117
|
+
const required = ranCapabilityKeys(result);
|
|
118
|
+
if (required.length > 0) {
|
|
119
|
+
expected.capabilities = { required };
|
|
120
|
+
}
|
|
121
|
+
return expected;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
startMessageFromWorkReference,
|
|
126
|
+
buildCopyExpected
|
|
127
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a Role Assistant test case from Work Hub status and result.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview CLI materializes 187 YAML; dataplane is the PE reference only
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
getWorkExecutionStatus,
|
|
11
|
+
getWorkExecutionResult
|
|
12
|
+
} = require('../api/work.api');
|
|
13
|
+
const { throwCopyStatus, assertCopyBoundToSystem } = require('./test-case-copy-bind');
|
|
14
|
+
const { mapCopyInteractions } = require('./test-case-copy-interactions');
|
|
15
|
+
const {
|
|
16
|
+
startMessageFromWorkReference,
|
|
17
|
+
buildCopyExpected
|
|
18
|
+
} = require('./test-case-copy-expected');
|
|
19
|
+
|
|
20
|
+
function remapCopyReferenceError(error) {
|
|
21
|
+
const status = Number(error && error.status) || 0;
|
|
22
|
+
if (status === 404) {
|
|
23
|
+
throwCopyStatus('Execution was not found', 404);
|
|
24
|
+
}
|
|
25
|
+
if (status === 403) {
|
|
26
|
+
throwCopyStatus('Permission denied', 403);
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function readWorkStatus(dataplaneUrl, authConfig, processExecutionId) {
|
|
32
|
+
try {
|
|
33
|
+
return await getWorkExecutionStatus(dataplaneUrl, authConfig, processExecutionId);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
remapCopyReferenceError(error);
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function readWorkResult(dataplaneUrl, authConfig, processExecutionId) {
|
|
41
|
+
try {
|
|
42
|
+
return await getWorkExecutionResult(dataplaneUrl, authConfig, processExecutionId, {
|
|
43
|
+
requestedDepth: 'business',
|
|
44
|
+
format: 'json'
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (Number(error && error.status) === 404) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
remapCopyReferenceError(error);
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function fetchWorkCopyReference(dataplaneUrl, authConfig, processExecutionId) {
|
|
56
|
+
const status = await readWorkStatus(dataplaneUrl, authConfig, processExecutionId);
|
|
57
|
+
const result = await readWorkResult(dataplaneUrl, authConfig, processExecutionId);
|
|
58
|
+
return { status, result };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function omitEmptyList(doc, key, rows) {
|
|
62
|
+
if (Array.isArray(rows) && rows.length > 0) {
|
|
63
|
+
doc[key] = rows;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildCopyCaseFromWorkReference(args) {
|
|
68
|
+
const { systemKey, caseId, processExecutionId, status, result } = args;
|
|
69
|
+
const bind = assertCopyBoundToSystem(systemKey, status, result);
|
|
70
|
+
const message = startMessageFromWorkReference(result);
|
|
71
|
+
const mapped = mapCopyInteractions(result);
|
|
72
|
+
const warnings = [...mapped.warnings];
|
|
73
|
+
if (!bind.bound) {
|
|
74
|
+
warnings.push('Role Assistant identity was not on the execution reference');
|
|
75
|
+
}
|
|
76
|
+
const caseDoc = {
|
|
77
|
+
caseId,
|
|
78
|
+
input: { executionPath: 'workHub', message },
|
|
79
|
+
expected: buildCopyExpected(status, result),
|
|
80
|
+
metadata: { sourceProcessExecutionId: processExecutionId }
|
|
81
|
+
};
|
|
82
|
+
omitEmptyList(caseDoc, 'answers', mapped.answers);
|
|
83
|
+
omitEmptyList(caseDoc, 'approvals', mapped.approvals);
|
|
84
|
+
return { case: caseDoc, warnings };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
fetchWorkCopyReference,
|
|
89
|
+
buildCopyCaseFromWorkReference
|
|
90
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map work.result interactions onto 187 answers and approvals.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Completed waits only; pending waits are omitted
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { throwCopyStatus } = require('./test-case-copy-bind');
|
|
10
|
+
|
|
11
|
+
const ANSWERED = new Set(['answered', 'approved']);
|
|
12
|
+
|
|
13
|
+
function asObject(value) {
|
|
14
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function interactionValue(row) {
|
|
21
|
+
const response = asObject(row.response);
|
|
22
|
+
if (Object.prototype.hasOwnProperty.call(response, 'decision') && response.decision !== null
|
|
23
|
+
&& response.decision !== undefined) {
|
|
24
|
+
return response.decision;
|
|
25
|
+
}
|
|
26
|
+
if (Object.prototype.hasOwnProperty.call(response, 'data')) {
|
|
27
|
+
return response.data;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mapAnsweredInteraction(row, answers, approvals, warnings) {
|
|
33
|
+
const questionKey = String(row.questionKey || '').trim();
|
|
34
|
+
if (!questionKey) {
|
|
35
|
+
warnings.push('Skipped a completed interaction without a questionKey');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const entry = { when: { questionKey }, value: interactionValue(row) };
|
|
39
|
+
if (String(row.interactionKind || '') === 'approval') {
|
|
40
|
+
approvals.push(entry);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
answers.push(entry);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function mapCopyInteractions(result) {
|
|
47
|
+
const rows = Array.isArray(result && result.interactions) ? result.interactions : [];
|
|
48
|
+
const answers = [];
|
|
49
|
+
const approvals = [];
|
|
50
|
+
const warnings = [];
|
|
51
|
+
let answeredCount = 0;
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
if (!row || typeof row !== 'object' || !ANSWERED.has(String(row.status || ''))) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
answeredCount += 1;
|
|
57
|
+
mapAnsweredInteraction(row, answers, approvals, warnings);
|
|
58
|
+
}
|
|
59
|
+
if (answeredCount > 0 && answers.length === 0 && approvals.length === 0) {
|
|
60
|
+
throwCopyStatus('Completed interactions could not be bound to questionKey', 422);
|
|
61
|
+
}
|
|
62
|
+
return { answers, approvals, warnings };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
mapCopyInteractions
|
|
67
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persist a copied Role Assistant test case as local YAML.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview UUID checks and AJV-gated write of a copied case document
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const yaml = require('js-yaml');
|
|
11
|
+
const fs = require('../internal/fs-real-sync');
|
|
12
|
+
const { YAML_SPEC_KEYS } = require('./test-pack-map');
|
|
13
|
+
const { assertTestCaseDocument } = require('./test-case-validate');
|
|
14
|
+
|
|
15
|
+
const PE_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16
|
+
const WORK_HUB_REF_MSG =
|
|
17
|
+
'--reference must be a Work Hub execution id (the same id printed after a run)';
|
|
18
|
+
|
|
19
|
+
function normalizeProcessExecutionId(value) {
|
|
20
|
+
return String(value || '')
|
|
21
|
+
.trim()
|
|
22
|
+
.replace(/^\{|\}$/g, '');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function defaultCaseIdFromReference(reference) {
|
|
26
|
+
const hex = normalizeProcessExecutionId(reference).replace(/-/g, '').toLowerCase();
|
|
27
|
+
return `copy-${hex.slice(0, 8)}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function assertProcessExecutionId(value) {
|
|
31
|
+
const id = normalizeProcessExecutionId(value);
|
|
32
|
+
if (!PE_UUID_RE.test(id)) {
|
|
33
|
+
throw new Error(WORK_HUB_REF_MSG);
|
|
34
|
+
}
|
|
35
|
+
return id.toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function assertFolderToken(value, label) {
|
|
39
|
+
const token = String(value || '').trim();
|
|
40
|
+
if (!token || token.includes('..') || /[/\\]/.test(token)) {
|
|
41
|
+
throw new Error(`${label} must be a folder name (for example 001)`);
|
|
42
|
+
}
|
|
43
|
+
return token;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function copyTargetPath(testsDir, suiteId, caseId) {
|
|
47
|
+
return path.join(testsDir, suiteId, `${caseId}.yaml`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractCopyCase(payload) {
|
|
51
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (payload.case && typeof payload.case === 'object' && !Array.isArray(payload.case)) {
|
|
55
|
+
return payload.case;
|
|
56
|
+
}
|
|
57
|
+
if (typeof payload.caseId === 'string' || payload.input) {
|
|
58
|
+
return payload;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function withCopyProvenance(doc, processExecutionId) {
|
|
64
|
+
return {
|
|
65
|
+
...doc,
|
|
66
|
+
metadata: {
|
|
67
|
+
...(doc.metadata && typeof doc.metadata === 'object' ? doc.metadata : {}),
|
|
68
|
+
sourceProcessExecutionId: processExecutionId
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function pickYamlSpec(doc) {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const key of YAML_SPEC_KEYS) {
|
|
76
|
+
if (doc[key] !== undefined) {
|
|
77
|
+
out[key] = doc[key];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function prepareCopyDocument(payload, caseId, processExecutionId) {
|
|
84
|
+
const raw = extractCopyCase(payload);
|
|
85
|
+
if (!raw) {
|
|
86
|
+
throw new Error('Copy did not return a test case');
|
|
87
|
+
}
|
|
88
|
+
const doc = withCopyProvenance({ ...raw, caseId }, processExecutionId);
|
|
89
|
+
assertTestCaseDocument(doc, `${caseId}.yaml`);
|
|
90
|
+
return pickYamlSpec(doc);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function yamlFromValidatedCase(doc) {
|
|
94
|
+
return yaml.dump(doc, { lineWidth: 120, noRefs: true, sortKeys: false });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function assertCopyTargetWritable(targetPath, force) {
|
|
98
|
+
if (fs.existsSync(targetPath) && force !== true) {
|
|
99
|
+
throw new Error('Test case already exists. Pass --force to overwrite.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function persistCopyCaseYaml(targetPath, yamlText) {
|
|
104
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
105
|
+
fs.writeFileSync(targetPath, yamlText, 'utf8');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
WORK_HUB_REF_MSG,
|
|
110
|
+
defaultCaseIdFromReference,
|
|
111
|
+
assertProcessExecutionId,
|
|
112
|
+
assertFolderToken,
|
|
113
|
+
copyTargetPath,
|
|
114
|
+
extractCopyCase,
|
|
115
|
+
prepareCopyDocument,
|
|
116
|
+
yamlFromValidatedCase,
|
|
117
|
+
assertCopyTargetWritable,
|
|
118
|
+
persistCopyCaseYaml
|
|
119
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nested expected-field catalog for Role Assistant YAML cases.
|
|
3
|
+
* @fileoverview Fail-closed coverage walk after AJV nested schema
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const HONESTY_KEYS = [
|
|
9
|
+
'requireWorkResult',
|
|
10
|
+
'schemaVersion',
|
|
11
|
+
'requireCompletion',
|
|
12
|
+
'requireContributionObservation',
|
|
13
|
+
'minResourceChanges',
|
|
14
|
+
'softResourceChanges',
|
|
15
|
+
'forbidBuilderKeysInObjective'
|
|
16
|
+
];
|
|
17
|
+
const SEARCH_KEYS = [
|
|
18
|
+
'statusCode',
|
|
19
|
+
'operation',
|
|
20
|
+
'resultMode',
|
|
21
|
+
'minRows',
|
|
22
|
+
'errorContains',
|
|
23
|
+
'graphDepth',
|
|
24
|
+
'relatedTypes',
|
|
25
|
+
'related'
|
|
26
|
+
];
|
|
27
|
+
const SEARCH_RELATED_KEYS = ['minRows', 'resultMode'];
|
|
28
|
+
const SKILLS_KEYS = ['resourceType', 'operations', 'filterFields', 'operationRequired'];
|
|
29
|
+
const EVIDENCE_KEYS = ['requiredKeys', 'roleKey', 'resourceType', 'requireStatus'];
|
|
30
|
+
const BID_KEYS = [
|
|
31
|
+
'schemaName',
|
|
32
|
+
'schemaVersion',
|
|
33
|
+
'requireDefinitionHash',
|
|
34
|
+
'requireSuiteRunId',
|
|
35
|
+
'forbidGoldPaths',
|
|
36
|
+
'minDatasources',
|
|
37
|
+
'requireCapability'
|
|
38
|
+
];
|
|
39
|
+
const EXPECTED_KEYS = [
|
|
40
|
+
'soft',
|
|
41
|
+
'softNotes',
|
|
42
|
+
'honesty',
|
|
43
|
+
'lifecycleState',
|
|
44
|
+
'capabilities',
|
|
45
|
+
'search',
|
|
46
|
+
'skills',
|
|
47
|
+
'evidence',
|
|
48
|
+
'bid',
|
|
49
|
+
'facts',
|
|
50
|
+
'prohibited'
|
|
51
|
+
];
|
|
52
|
+
const CAPABILITY_KEYS = ['allowed', 'required', 'forbidden'];
|
|
53
|
+
const NESTED = {
|
|
54
|
+
'expected.honesty': HONESTY_KEYS,
|
|
55
|
+
'expected.search': SEARCH_KEYS,
|
|
56
|
+
'expected.skills': SKILLS_KEYS,
|
|
57
|
+
'expected.evidence': EVIDENCE_KEYS,
|
|
58
|
+
'expected.bid': BID_KEYS,
|
|
59
|
+
'expected.capabilities': CAPABILITY_KEYS,
|
|
60
|
+
'preflight.evidence': EVIDENCE_KEYS
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function objectKeys(value) {
|
|
64
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return Object.keys(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function unknownKeyError(filePath, pointer, key, allowed) {
|
|
71
|
+
const allowedText = allowed.join(', ');
|
|
72
|
+
return (
|
|
73
|
+
`${filePath}: unknown field ${pointer}.${key}. ` +
|
|
74
|
+
`Allowed properties: ${allowedText}. ` +
|
|
75
|
+
'Remove the field or rename it to a listed property.'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function collectUnknownInObject(filePath, pointer, value, allowed, errors) {
|
|
80
|
+
for (const key of objectKeys(value)) {
|
|
81
|
+
if (!allowed.includes(key)) {
|
|
82
|
+
errors.push(unknownKeyError(filePath, pointer, key, allowed));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function collectSearchRelatedUnknown(filePath, related, errors) {
|
|
88
|
+
for (const resourceType of objectKeys(related)) {
|
|
89
|
+
collectUnknownInObject(
|
|
90
|
+
filePath,
|
|
91
|
+
`expected.search.related.${resourceType}`,
|
|
92
|
+
related[resourceType],
|
|
93
|
+
SEARCH_RELATED_KEYS,
|
|
94
|
+
errors
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function collectNestedUnknown(filePath, parsed, errors) {
|
|
100
|
+
const expected = parsed.expected && typeof parsed.expected === 'object' ? parsed.expected : {};
|
|
101
|
+
collectUnknownInObject(filePath, 'expected', expected, EXPECTED_KEYS, errors);
|
|
102
|
+
collectUnknownInObject(filePath, 'expected.honesty', expected.honesty, HONESTY_KEYS, errors);
|
|
103
|
+
collectUnknownInObject(filePath, 'expected.search', expected.search, SEARCH_KEYS, errors);
|
|
104
|
+
collectSearchRelatedUnknown(filePath, expected.search && expected.search.related, errors);
|
|
105
|
+
collectUnknownInObject(filePath, 'expected.skills', expected.skills, SKILLS_KEYS, errors);
|
|
106
|
+
collectUnknownInObject(filePath, 'expected.evidence', expected.evidence, EVIDENCE_KEYS, errors);
|
|
107
|
+
collectUnknownInObject(filePath, 'expected.bid', expected.bid, BID_KEYS, errors);
|
|
108
|
+
collectUnknownInObject(
|
|
109
|
+
filePath,
|
|
110
|
+
'expected.capabilities',
|
|
111
|
+
expected.capabilities,
|
|
112
|
+
CAPABILITY_KEYS,
|
|
113
|
+
errors
|
|
114
|
+
);
|
|
115
|
+
const preflight = parsed.preflight && typeof parsed.preflight === 'object' ? parsed.preflight : {};
|
|
116
|
+
collectUnknownInObject(filePath, 'preflight.evidence', preflight.evidence, EVIDENCE_KEYS, errors);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function assertExpectedCoverage(parsed, filePath) {
|
|
120
|
+
const errors = [];
|
|
121
|
+
collectNestedUnknown(filePath, parsed || {}, errors);
|
|
122
|
+
return errors;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
HONESTY_KEYS,
|
|
127
|
+
SEARCH_KEYS,
|
|
128
|
+
SEARCH_RELATED_KEYS,
|
|
129
|
+
SKILLS_KEYS,
|
|
130
|
+
EVIDENCE_KEYS,
|
|
131
|
+
BID_KEYS,
|
|
132
|
+
EXPECTED_KEYS,
|
|
133
|
+
NESTED,
|
|
134
|
+
assertExpectedCoverage
|
|
135
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline Role Assistant test-case schema validation.
|
|
3
|
+
* @fileoverview AJV check for YAML cases before auth or package publish
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { parseJsonFileContent } = require('../validation/validate-external-file');
|
|
10
|
+
const { loadRoleAssistantTestCaseSchema } = require('../utils/schema-loader');
|
|
11
|
+
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
12
|
+
const { assertExpectedCoverage } = require('./test-case-expected-catalog');
|
|
13
|
+
|
|
14
|
+
const TEST_CASE_TYPE = 'role-assistant-test-case';
|
|
15
|
+
|
|
16
|
+
function formatCaseErrors(filePath, ajvErrors) {
|
|
17
|
+
return formatValidationErrors(ajvErrors, { dedupe: true }).map((msg) => {
|
|
18
|
+
const fix = /additional properties/i.test(msg)
|
|
19
|
+
? ' Remove the field or rename it to a listed property.'
|
|
20
|
+
: '';
|
|
21
|
+
return `${filePath}: ${msg}${fix}`;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function validateTestCaseDocument(parsed, filePath) {
|
|
26
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
27
|
+
return {
|
|
28
|
+
valid: false,
|
|
29
|
+
errors: [`${filePath}: Test case must be an object`],
|
|
30
|
+
warnings: []
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const validate = loadRoleAssistantTestCaseSchema();
|
|
34
|
+
const ok = validate(parsed);
|
|
35
|
+
const errors = ok
|
|
36
|
+
? assertExpectedCoverage(parsed, filePath)
|
|
37
|
+
: formatCaseErrors(filePath, validate.errors);
|
|
38
|
+
return { valid: errors.length === 0, errors, warnings: [] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateTestCaseFile(filePath) {
|
|
42
|
+
const parseResult = parseJsonFileContent(filePath);
|
|
43
|
+
if (parseResult.error) {
|
|
44
|
+
return { ...parseResult.error, file: filePath, type: TEST_CASE_TYPE };
|
|
45
|
+
}
|
|
46
|
+
const result = validateTestCaseDocument(parseResult.parsed, filePath);
|
|
47
|
+
return { ...result, file: filePath, type: TEST_CASE_TYPE };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function assertTestCaseDocument(parsed, filePath) {
|
|
51
|
+
const result = validateTestCaseDocument(parsed, filePath);
|
|
52
|
+
if (!result.valid) {
|
|
53
|
+
throw new Error(result.errors.join('\n'));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function throwIfCaseFileInvalid(filePath) {
|
|
58
|
+
const result = validateTestCaseFile(filePath);
|
|
59
|
+
if (!result.valid) {
|
|
60
|
+
throw new Error(result.errors.join('\n'));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function assertLocalCasesBeforeAuth(systemKey, opts) {
|
|
65
|
+
if (!opts || opts.fromDataplane || opts.listCases) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { listTestCaseFiles, loadRoleAssistantTestCase } = require('./test-cases');
|
|
69
|
+
if (opts.case) {
|
|
70
|
+
loadRoleAssistantTestCase(systemKey, opts.case, { testsDir: opts.testsDir || null });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!opts.suite) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const testsDir = opts.testsDir || path.join(process.cwd(), 'role-assistant', systemKey, 'tests');
|
|
77
|
+
for (const filePath of listTestCaseFiles(testsDir)) {
|
|
78
|
+
throwIfCaseFileInvalid(filePath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
TEST_CASE_TYPE,
|
|
84
|
+
validateTestCaseDocument,
|
|
85
|
+
validateTestCaseFile,
|
|
86
|
+
assertTestCaseDocument,
|
|
87
|
+
assertLocalCasesBeforeAuth
|
|
88
|
+
};
|