@aifabrix/builder 2.44.0 → 2.44.1
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/.cursor/rules/cli-layout.mdc +75 -0
- package/.cursor/rules/project-rules.mdc +8 -0
- package/.npmrc.token +1 -0
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/jest.projects.js +15 -2
- package/lib/api/certificates.api.js +62 -0
- package/lib/api/index.js +11 -2
- package/lib/api/types/certificates.types.js +48 -0
- package/lib/api/validation-run.api.js +16 -4
- package/lib/api/validation-runner.js +13 -3
- package/lib/app/certification-show-enrich.js +129 -0
- package/lib/app/certification-verify-rows.js +60 -0
- package/lib/app/show-display.js +43 -0
- package/lib/app/show.js +92 -8
- package/lib/certification/cli-cert-sync-skip.js +21 -0
- package/lib/certification/merge-certification-from-artifact.js +185 -0
- package/lib/certification/post-unified-cert-sync.js +33 -0
- package/lib/certification/sync-after-external-command.js +52 -0
- package/lib/certification/sync-system-certification.js +197 -0
- package/lib/cli/setup-app.js +4 -0
- package/lib/cli/setup-app.test-commands.js +24 -8
- package/lib/cli/setup-external-system.js +22 -1
- package/lib/cli/setup-secrets.js +34 -13
- package/lib/cli/setup-utility.js +18 -2
- package/lib/commands/app.js +10 -1
- package/lib/commands/datasource-unified-test-cli.js +50 -117
- package/lib/commands/datasource-unified-test-cli.options.js +44 -2
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
- package/lib/commands/datasource-validation-cli.js +15 -1
- package/lib/commands/datasource.js +25 -2
- package/lib/commands/upload.js +17 -6
- package/lib/datasource/log-viewer.js +105 -14
- package/lib/datasource/test-e2e.js +35 -17
- package/lib/datasource/unified-validation-run-body.js +3 -0
- package/lib/datasource/unified-validation-run.js +2 -1
- package/lib/external-system/deploy.js +53 -18
- package/lib/infrastructure/compose.js +12 -3
- package/lib/infrastructure/helpers-docker-check.js +67 -0
- package/lib/infrastructure/helpers.js +47 -58
- package/lib/infrastructure/index.js +3 -1
- package/lib/infrastructure/services.js +4 -56
- package/lib/schema/external-system.schema.json +25 -3
- package/lib/schema/type/document-storage.json +15 -2
- package/lib/utils/api.js +28 -3
- package/lib/utils/configuration-env-resolver.js +11 -8
- package/lib/utils/credential-secrets-env.js +5 -5
- package/lib/utils/datasource-test-run-certificate-tty.js +82 -0
- package/lib/utils/datasource-test-run-display.js +19 -2
- package/lib/utils/datasource-test-run-exit.js +25 -0
- package/lib/utils/external-system-display.js +8 -0
- package/lib/utils/external-system-system-test-tty-overview.js +120 -0
- package/lib/utils/external-system-system-test-tty.js +417 -0
- package/lib/utils/paths.js +14 -0
- package/lib/utils/validation-run-poll.js +28 -5
- package/lib/utils/validation-run-post-retry.js +20 -8
- package/lib/utils/validation-run-request.js +18 -0
- package/lib/validation/validate-external-cert-sync.js +23 -0
- package/lib/validation/validate.js +4 -1
- package/package.json +4 -3
- package/scripts/install-local.js +4 -1
- package/scripts/pnpm-global-remove.js +48 -0
- package/templates/applications/dataplane/env.template +4 -0
- package/templates/infra/compose.yaml.hbs +15 -14
- package/templates/infra/servers.json.hbs +3 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const logger = require('../utils/logger');
|
|
9
9
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
10
|
+
const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
|
|
10
11
|
const { TEST_HELP_AFTER, TEST_E2E_HELP_AFTER } = require('./setup-app.help');
|
|
11
12
|
|
|
12
13
|
function setupTestCommand(program) {
|
|
@@ -72,22 +73,31 @@ async function runTestE2ECommand(appName, options) {
|
|
|
72
73
|
sync: options.sync === true
|
|
73
74
|
});
|
|
74
75
|
const { displayIntegrationTestResults } = require('../utils/external-system-display');
|
|
76
|
+
const datasourceResults = results.map(r => ({
|
|
77
|
+
key: r.key,
|
|
78
|
+
success: r.success,
|
|
79
|
+
error: r.error,
|
|
80
|
+
skipped: false,
|
|
81
|
+
datasourceTestRun: r.datasourceTestRun
|
|
82
|
+
}));
|
|
75
83
|
displayIntegrationTestResults(
|
|
76
84
|
{
|
|
77
85
|
systemKey: appName,
|
|
78
86
|
success,
|
|
79
|
-
datasourceResults
|
|
80
|
-
key: r.key,
|
|
81
|
-
success: r.success,
|
|
82
|
-
error: r.error,
|
|
83
|
-
skipped: false,
|
|
84
|
-
datasourceTestRun: r.datasourceTestRun
|
|
85
|
-
}))
|
|
87
|
+
datasourceResults
|
|
86
88
|
},
|
|
87
89
|
options.verbose,
|
|
88
90
|
{ debug: options.debug, runType: 'e2e' }
|
|
89
91
|
);
|
|
90
|
-
|
|
92
|
+
const { computeSystemExitCodeFromDatasourceRows } = require('../utils/datasource-test-run-exit');
|
|
93
|
+
const exitCode = computeSystemExitCodeFromDatasourceRows(datasourceResults, {
|
|
94
|
+
warningsAsErrors: options.warningsAsErrors === true,
|
|
95
|
+
requireCert: options.requireCert === true
|
|
96
|
+
});
|
|
97
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
98
|
+
if (cliOptsSkipCertSync(options)) return;
|
|
99
|
+
const { trySyncCertificationFromDataplaneForExternalApp } = require('../certification/sync-after-external-command');
|
|
100
|
+
await trySyncCertificationFromDataplaneForExternalApp(appName, 'test-e2e');
|
|
91
101
|
return;
|
|
92
102
|
}
|
|
93
103
|
const { runAppTestE2e } = require('../commands/app-test');
|
|
@@ -127,6 +137,12 @@ function setupTestE2eCommand(program) {
|
|
|
127
137
|
'--sync',
|
|
128
138
|
'Publish local system and datasource files to the dataplane before running E2E (same as aifabrix upload <systemKey>; external integration only)'
|
|
129
139
|
)
|
|
140
|
+
.option('--warnings-as-errors', 'Treat aggregate warn as failure (exit 1)')
|
|
141
|
+
.option('--require-cert', 'Require certification passed on every datasource (exit 2 if not)')
|
|
142
|
+
.option(
|
|
143
|
+
'--no-cert-sync',
|
|
144
|
+
'Skip updating the system file certification block from the dataplane after a successful run'
|
|
145
|
+
)
|
|
130
146
|
.addHelpText('after', TEST_E2E_HELP_AFTER)
|
|
131
147
|
.action(async(appName, options, cmd) => {
|
|
132
148
|
try {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
18
|
+
const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
|
|
18
19
|
const { TEST_INTEGRATION_HELP_AFTER } = require('./setup-app.help');
|
|
19
20
|
|
|
20
21
|
function setupDownloadCommand(program) {
|
|
@@ -47,6 +48,10 @@ function setupUploadCommand(program) {
|
|
|
47
48
|
.option('--probe', 'After publish, run dataplane runtime checks (validation/run); slower')
|
|
48
49
|
.option('--minimal', 'Print only a short readiness summary after upload')
|
|
49
50
|
.option('--probe-timeout <ms>', 'Timeout for --probe (default: 120000)', '120000')
|
|
51
|
+
.option(
|
|
52
|
+
'--no-cert-sync',
|
|
53
|
+
'Skip updating the system file certification block from the dataplane active certificate after publish'
|
|
54
|
+
)
|
|
50
55
|
.action(async(systemKey, options) => {
|
|
51
56
|
try {
|
|
52
57
|
const upload = require('../commands/upload');
|
|
@@ -56,6 +61,7 @@ function setupUploadCommand(program) {
|
|
|
56
61
|
: Number(options.probeTimeout);
|
|
57
62
|
await upload.uploadExternalSystem(systemKey, {
|
|
58
63
|
...options,
|
|
64
|
+
noCertSync: cliOptsSkipCertSync(options),
|
|
59
65
|
probeTimeout: Number.isFinite(probeTimeout) ? probeTimeout : 120000
|
|
60
66
|
});
|
|
61
67
|
} catch (error) {
|
|
@@ -132,7 +138,16 @@ async function runExternalSystemTestIntegration(appName, options) {
|
|
|
132
138
|
};
|
|
133
139
|
const results = await test.testExternalSystemIntegration(appName, opts);
|
|
134
140
|
test.displayIntegrationTestResults(results, options.verbose, { debug: options.debug, runType: 'integration' });
|
|
135
|
-
|
|
141
|
+
const { computeSystemExitCodeFromDatasourceRows } = require('../utils/datasource-test-run-exit');
|
|
142
|
+
const rows = Array.isArray(results.datasourceResults) ? results.datasourceResults : [];
|
|
143
|
+
const exitCode = computeSystemExitCodeFromDatasourceRows(rows, {
|
|
144
|
+
warningsAsErrors: options.warningsAsErrors === true,
|
|
145
|
+
requireCert: options.requireCert === true
|
|
146
|
+
});
|
|
147
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
148
|
+
if (cliOptsSkipCertSync(options)) return;
|
|
149
|
+
const { trySyncCertificationFromDataplaneForExternalApp } = require('../certification/sync-after-external-command');
|
|
150
|
+
await trySyncCertificationFromDataplaneForExternalApp(appName, 'test-integration');
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
/**
|
|
@@ -180,6 +195,12 @@ function setupExternalSystemTestCommands(program) {
|
|
|
180
195
|
'--sync',
|
|
181
196
|
'Publish local system and datasource files to the dataplane before running tests (same as aifabrix upload <systemKey>)'
|
|
182
197
|
)
|
|
198
|
+
.option('--warnings-as-errors', 'Treat aggregate warn as failure (exit 1)')
|
|
199
|
+
.option('--require-cert', 'Require certification passed on every datasource (exit 2 if not)')
|
|
200
|
+
.option(
|
|
201
|
+
'--no-cert-sync',
|
|
202
|
+
'Skip updating the system file certification block from the dataplane after a successful run'
|
|
203
|
+
)
|
|
183
204
|
.addHelpText('after', TEST_INTEGRATION_HELP_AFTER)
|
|
184
205
|
.action(async(appName, options, cmd) => {
|
|
185
206
|
try {
|
package/lib/cli/setup-secrets.js
CHANGED
|
@@ -24,6 +24,10 @@ Subcommands:
|
|
|
24
24
|
|
|
25
25
|
Also: aifabrix secure Encrypt secrets.local.yaml (ISO 27001)
|
|
26
26
|
|
|
27
|
+
Default "secret set" (no --shared): writes only to your user secrets.local.yaml next to
|
|
28
|
+
config.yaml (typically ~/.aifabrix/secrets.local.yaml). Use --shared to write the
|
|
29
|
+
shared store from config aifabrix-secrets (another file or https), not that user file.
|
|
30
|
+
|
|
27
31
|
Examples:
|
|
28
32
|
$ aifabrix secret list
|
|
29
33
|
$ aifabrix secret set myapp/clientSecret "your-value"
|
|
@@ -41,6 +45,14 @@ Examples:
|
|
|
41
45
|
`;
|
|
42
46
|
|
|
43
47
|
const SECRET_SET_HELP_AFTER = `
|
|
48
|
+
Where the value is stored:
|
|
49
|
+
No --shared User file secrets.local.yaml in your aifabrix config directory (same
|
|
50
|
+
folder as config.yaml; often ~/.aifabrix/). Used for app/integration
|
|
51
|
+
secrets and kv:// resolution on your machine.
|
|
52
|
+
--shared The store set in config.yaml as aifabrix-secrets: a YAML file path or
|
|
53
|
+
an https secrets API (never the user-only file above unless you point
|
|
54
|
+
aifabrix-secrets at that path deliberately).
|
|
55
|
+
|
|
44
56
|
Examples:
|
|
45
57
|
$ aifabrix secret set myapp/clientSecret "your-secret"
|
|
46
58
|
$ aifabrix secret set hubspot/apiKey "$HUBSPOT_KEY"
|
|
@@ -144,22 +156,13 @@ function setupSecureCommand(program) {
|
|
|
144
156
|
});
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
|
|
148
|
-
* Sets up secrets and security commands
|
|
149
|
-
* @param {Command} program - Commander program instance
|
|
150
|
-
*/
|
|
151
|
-
function setupSecretsCommands(program) {
|
|
152
|
-
const secretCmd = program
|
|
153
|
-
.command('secret')
|
|
154
|
-
.description('User and shared secrets (list, set, remove, remove-all, validate)')
|
|
155
|
-
.addHelpText('after', SECRET_GROUP_HELP_AFTER);
|
|
156
|
-
|
|
159
|
+
function setupSecretListCommand(secretCmd) {
|
|
157
160
|
secretCmd
|
|
158
161
|
.command('list')
|
|
159
162
|
.description('List secret keys (--shared for shared/remote)')
|
|
160
163
|
.addHelpText('after', SECRET_LIST_HELP_AFTER)
|
|
161
164
|
.option('--shared', 'List shared secrets (from config aifabrix-secrets or remote API)')
|
|
162
|
-
.action(async
|
|
165
|
+
.action(async options => {
|
|
163
166
|
try {
|
|
164
167
|
await config.ensureSecretsEncryptionKey();
|
|
165
168
|
await handleSecretsList(options);
|
|
@@ -168,12 +171,30 @@ function setupSecretsCommands(program) {
|
|
|
168
171
|
process.exit(1);
|
|
169
172
|
}
|
|
170
173
|
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Sets up secrets and security commands
|
|
178
|
+
* @param {Command} program - Commander program instance
|
|
179
|
+
*/
|
|
180
|
+
function setupSecretsCommands(program) {
|
|
181
|
+
const secretCmd = program
|
|
182
|
+
.command('secret')
|
|
183
|
+
.description('User and shared secrets (list, set, remove, remove-all, validate)')
|
|
184
|
+
.addHelpText('after', SECRET_GROUP_HELP_AFTER);
|
|
185
|
+
|
|
186
|
+
setupSecretListCommand(secretCmd);
|
|
171
187
|
|
|
172
188
|
secretCmd
|
|
173
189
|
.command('set <key> <value>')
|
|
174
|
-
.description(
|
|
190
|
+
.description(
|
|
191
|
+
'Set a secret (default: user secrets.local.yaml beside config; --shared → aifabrix-secrets store)'
|
|
192
|
+
)
|
|
175
193
|
.addHelpText('after', SECRET_SET_HELP_AFTER)
|
|
176
|
-
.option(
|
|
194
|
+
.option(
|
|
195
|
+
'--shared',
|
|
196
|
+
'Write to shared secrets (config aifabrix-secrets: YAML path or https), not user secrets.local.yaml'
|
|
197
|
+
)
|
|
177
198
|
.action(async(key, value, options) => {
|
|
178
199
|
try {
|
|
179
200
|
await config.ensureSecretsEncryptionKey();
|
package/lib/cli/setup-utility.js
CHANGED
|
@@ -25,6 +25,7 @@ Generates *-deploy.json (or application-schema.json) for commit before deploy.
|
|
|
25
25
|
const VALIDATE_HELP_AFTER = `
|
|
26
26
|
Examples:
|
|
27
27
|
$ aifabrix validate myapp
|
|
28
|
+
$ aifabrix validate myapp --cert-sync
|
|
28
29
|
$ aifabrix validate --integration
|
|
29
30
|
$ aifabrix validate --builder
|
|
30
31
|
`;
|
|
@@ -262,10 +263,18 @@ function setupShowCommand(program) {
|
|
|
262
263
|
.description('Show app from local tree (default) or controller (--online)')
|
|
263
264
|
.option('--online', 'Fetch application data from the controller')
|
|
264
265
|
.option('--json', 'Output as JSON')
|
|
266
|
+
.option(
|
|
267
|
+
'--verify-cert',
|
|
268
|
+
'For external integrations, verify trust state on the dataplane when logged in (with --online uses current session; offline attempts the same if a controller URL is configured)'
|
|
269
|
+
)
|
|
265
270
|
.action(async(appKey, options) => {
|
|
266
271
|
try {
|
|
267
272
|
const { showApp } = require('../app/show');
|
|
268
|
-
await showApp(appKey, {
|
|
273
|
+
await showApp(appKey, {
|
|
274
|
+
online: options.online,
|
|
275
|
+
json: options.json,
|
|
276
|
+
verifyCert: options.verifyCert === true
|
|
277
|
+
});
|
|
269
278
|
} catch (error) {
|
|
270
279
|
logger.error(chalk.red(`Error: ${error.message}`));
|
|
271
280
|
process.exit(1);
|
|
@@ -307,7 +316,10 @@ async function runValidateCommand(appOrFile, options) {
|
|
|
307
316
|
process.exit(1);
|
|
308
317
|
}
|
|
309
318
|
|
|
310
|
-
const result = await validate.validateAppOrFile(appOrFile,
|
|
319
|
+
const result = await validate.validateAppOrFile(appOrFile, {
|
|
320
|
+
...opts,
|
|
321
|
+
certSync: opts.certSync === true
|
|
322
|
+
});
|
|
311
323
|
if (outFormat === 'json') {
|
|
312
324
|
logger.log(JSON.stringify(result, null, 2));
|
|
313
325
|
} else {
|
|
@@ -323,6 +335,10 @@ function setupValidateDiffCommands(program) {
|
|
|
323
335
|
.option('--format <format>', 'Output format: json | default (human-readable)')
|
|
324
336
|
.option('--integration', 'Validate all applications under integration/')
|
|
325
337
|
.option('--builder', 'Validate all applications under builder/')
|
|
338
|
+
.option(
|
|
339
|
+
'--cert-sync',
|
|
340
|
+
'After successful validation of an external integration, refresh the system file certification block from the dataplane (requires login)'
|
|
341
|
+
)
|
|
326
342
|
.action((appOrFile, options) => {
|
|
327
343
|
runValidateCommand(appOrFile, options).catch((error) => {
|
|
328
344
|
handleCommandError(error, 'validate');
|
package/lib/commands/app.js
CHANGED
|
@@ -78,9 +78,18 @@ function setupAppShowCommand(app) {
|
|
|
78
78
|
.option('--online', 'Fetch from controller (default for this command)')
|
|
79
79
|
.option('--json', 'Output as JSON')
|
|
80
80
|
.option('--permissions', 'Show only list of permissions')
|
|
81
|
+
.option(
|
|
82
|
+
'--verify-cert',
|
|
83
|
+
'For external integrations, verify trust state on the dataplane (requires login)'
|
|
84
|
+
)
|
|
81
85
|
.action(async(appKey, options) => {
|
|
82
86
|
try {
|
|
83
|
-
await showApp(appKey, {
|
|
87
|
+
await showApp(appKey, {
|
|
88
|
+
online: true,
|
|
89
|
+
json: !!options.json,
|
|
90
|
+
permissions: !!options.permissions,
|
|
91
|
+
verifyCert: options.verifyCert === true
|
|
92
|
+
});
|
|
84
93
|
} catch (error) {
|
|
85
94
|
logger.error(chalk.red(`Error: ${error.message}`));
|
|
86
95
|
process.exit(1);
|
|
@@ -10,34 +10,31 @@ const logger = require('../utils/logger');
|
|
|
10
10
|
const { runDatasourceTestIntegration } = require('../datasource/test-integration');
|
|
11
11
|
const { runDatasourceTestE2E } = require('../datasource/test-e2e');
|
|
12
12
|
const { runUnifiedDatasourceValidation } = require('../datasource/unified-validation-run');
|
|
13
|
-
const { displayIntegrationTestResults
|
|
13
|
+
const { displayIntegrationTestResults } = require('../utils/external-system-display');
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const { getIntegrationPath } = require('../utils/paths');
|
|
16
16
|
const { writeTestLog } = require('../utils/test-log-writer');
|
|
17
17
|
const { includeDebugForRequest } = require('../utils/validation-run-request');
|
|
18
18
|
const {
|
|
19
|
-
exitFromUnifiedValidationResult,
|
|
20
19
|
finalizeUnifiedValidationResult,
|
|
21
20
|
unifiedCliResultFromIntegrationReturn,
|
|
22
|
-
|
|
23
|
-
finalizeAfterIntegrationDisplay,
|
|
24
|
-
emitCapabilityScopeDiagnostics
|
|
21
|
+
finalizeAfterIntegrationDisplay
|
|
25
22
|
} = require('./datasource-validation-cli');
|
|
23
|
+
const { afterUnifiedValidationCertSync } = require('../certification/post-unified-cert-sync');
|
|
26
24
|
const { resolveAppKeyForDatasource } = require('../datasource/resolve-app');
|
|
27
25
|
const { runDatasourceValidationWatchLoop } = require('../utils/datasource-validation-watch');
|
|
28
|
-
const { computeExitCodeFromDatasourceTestRun } = require('../utils/datasource-test-run-exit');
|
|
29
|
-
const { analyzeCapabilityScope } = require('../utils/datasource-test-run-capability-scope');
|
|
30
26
|
const {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
exitCodeFromDatasourceTestRunEnvelope,
|
|
28
|
+
finalizeDatasourceTestE2ELegacyPath,
|
|
29
|
+
displayDatasourceTestE2EEnvelopeResults
|
|
30
|
+
} = require('./datasource-unified-test-e2e-cli-helpers');
|
|
35
31
|
const {
|
|
36
32
|
datasourceTestHelpAfter,
|
|
37
33
|
datasourceTestIntegrationHelpAfter,
|
|
38
34
|
datasourceTestE2eHelpAfter,
|
|
39
35
|
attachDatasourceWatchOptions,
|
|
40
|
-
attachDatasourceTestCommonOptions
|
|
36
|
+
attachDatasourceTestCommonOptions,
|
|
37
|
+
attachDatasourceTestE2eExclusiveOptions
|
|
41
38
|
} = require('./datasource-unified-test-cli.options');
|
|
42
39
|
|
|
43
40
|
function integrationBaseDirForLogs(appKey) {
|
|
@@ -68,88 +65,6 @@ async function writeDatasourceTestDebugLogIfRequested(appKey, datasourceKey, res
|
|
|
68
65
|
logger.log(chalk.gray(` Debug log: ${logPath}`));
|
|
69
66
|
}
|
|
70
67
|
|
|
71
|
-
function logDatasourceTestRunDebugAppendix(envelope, debugOpt) {
|
|
72
|
-
const mode = resolveDebugDisplayMode(debugOpt);
|
|
73
|
-
if (!mode || !envelope) return;
|
|
74
|
-
const block = formatDatasourceTestRunDebugBlock(envelope, mode, process.stdout.isTTY);
|
|
75
|
-
if (block) logger.log(block);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function logE2eCapabilityFocusFromEnvelope(env, capabilityOpt) {
|
|
79
|
-
if (!env) return;
|
|
80
|
-
const capKey =
|
|
81
|
-
capabilityOpt !== undefined && capabilityOpt !== null
|
|
82
|
-
? String(capabilityOpt).trim()
|
|
83
|
-
: '';
|
|
84
|
-
if (!capKey) return;
|
|
85
|
-
const sec = formatCapabilityFocusSection(env, capKey);
|
|
86
|
-
if (sec) logger.log(sec);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Exit code from envelope only (no stderr diagnostics; use when TTY output already ran emit via logEnvelope).
|
|
91
|
-
* @param {Object|null|undefined} env
|
|
92
|
-
* @param {Object} options
|
|
93
|
-
* @returns {number|null} null if no envelope
|
|
94
|
-
*/
|
|
95
|
-
function exitCodeFromDatasourceTestRunEnvelope(env, options) {
|
|
96
|
-
if (!env || typeof env !== 'object') return null;
|
|
97
|
-
let code = computeExitCodeFromDatasourceTestRun(env, {
|
|
98
|
-
warningsAsErrors: false,
|
|
99
|
-
requireCert: false
|
|
100
|
-
});
|
|
101
|
-
const scope = analyzeCapabilityScope(env, options.capability);
|
|
102
|
-
if (options.strictCapabilityScope === true && scope.violated) {
|
|
103
|
-
code = Math.max(code, 1);
|
|
104
|
-
}
|
|
105
|
-
return code;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Legacy E2E display + exit code (no process.exit; watch mode).
|
|
110
|
-
* @param {Object} data
|
|
111
|
-
* @param {Object} options
|
|
112
|
-
* @returns {number}
|
|
113
|
-
*/
|
|
114
|
-
function finalizeDatasourceTestE2ELegacyPath(data, options) {
|
|
115
|
-
displayE2EResults(data, options.verbose);
|
|
116
|
-
logDatasourceTestRunDebugAppendix(data.datasourceTestRun, options.debug);
|
|
117
|
-
logE2eCapabilityFocusFromEnvelope(data.datasourceTestRun, options.capability);
|
|
118
|
-
const env = data.datasourceTestRun;
|
|
119
|
-
if (env) {
|
|
120
|
-
emitCapabilityScopeDiagnostics(env, { requestedCapabilityKey: options.capability });
|
|
121
|
-
const code = exitCodeFromDatasourceTestRunEnvelope(env, options);
|
|
122
|
-
return code === null ? 1 : code;
|
|
123
|
-
}
|
|
124
|
-
const steps = data.steps || data.completedActions || [];
|
|
125
|
-
const failed = data.success === false || steps.some(s => s.success === false || s.error);
|
|
126
|
-
return failed ? 1 : 0;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Human TTY for single-datasource E2E when DatasourceTestRun envelope is present.
|
|
131
|
-
* @param {string} datasourceKey
|
|
132
|
-
* @param {Object} env
|
|
133
|
-
* @param {Object} options
|
|
134
|
-
*/
|
|
135
|
-
function displayDatasourceTestE2EEnvelopeResults(datasourceKey, env, options) {
|
|
136
|
-
const success = env.status !== 'fail';
|
|
137
|
-
displayIntegrationTestResults(
|
|
138
|
-
{
|
|
139
|
-
systemKey: env.systemKey || 'unknown',
|
|
140
|
-
success,
|
|
141
|
-
datasourceResults: [{ key: datasourceKey, success, datasourceTestRun: env }]
|
|
142
|
-
},
|
|
143
|
-
options.verbose,
|
|
144
|
-
{
|
|
145
|
-
debug: options.debug,
|
|
146
|
-
runType: 'e2e',
|
|
147
|
-
requestedCapabilityKey: options.capability
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
logE2eCapabilityFocusFromEnvelope(env, options.capability);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
68
|
function buildDatasourceTestRunOpts(options) {
|
|
154
69
|
return {
|
|
155
70
|
app: options.app,
|
|
@@ -175,11 +90,13 @@ function buildDatasourceTestDisplayOpts(options) {
|
|
|
175
90
|
};
|
|
176
91
|
}
|
|
177
92
|
|
|
178
|
-
async function runDatasourceUnifiedTestOnceForWatch(datasourceKey, runOpts, displayOpts) {
|
|
93
|
+
async function runDatasourceUnifiedTestOnceForWatch(datasourceKey, runOpts, displayOpts, certCliOptions = {}) {
|
|
179
94
|
try {
|
|
180
95
|
const result = await runUnifiedDatasourceValidation(datasourceKey, runOpts);
|
|
96
|
+
const exitCode = finalizeUnifiedValidationResult(result, displayOpts);
|
|
97
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, certCliOptions, 'datasource test');
|
|
181
98
|
return {
|
|
182
|
-
exitCode
|
|
99
|
+
exitCode,
|
|
183
100
|
envelope: result.envelope
|
|
184
101
|
};
|
|
185
102
|
} catch (err) {
|
|
@@ -203,8 +120,10 @@ async function datasourceTestCommandAction(datasourceKey, options) {
|
|
|
203
120
|
runOnce: async() => {
|
|
204
121
|
const result = await runUnifiedDatasourceValidation(datasourceKey, runOpts);
|
|
205
122
|
await writeDatasourceTestDebugLogIfRequested(appKey, datasourceKey, result, options);
|
|
123
|
+
const exitCode = finalizeUnifiedValidationResult(result, displayOpts);
|
|
124
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test (watch)');
|
|
206
125
|
return {
|
|
207
|
-
exitCode
|
|
126
|
+
exitCode,
|
|
208
127
|
envelope: result.envelope
|
|
209
128
|
};
|
|
210
129
|
}
|
|
@@ -220,7 +139,9 @@ async function datasourceTestCommandAction(datasourceKey, options) {
|
|
|
220
139
|
logger.warn(chalk.yellow(`⚠ Could not write debug log: ${e.message}`));
|
|
221
140
|
}
|
|
222
141
|
}
|
|
223
|
-
|
|
142
|
+
const exitCode = finalizeUnifiedValidationResult(result, displayOpts);
|
|
143
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test');
|
|
144
|
+
process.exit(exitCode);
|
|
224
145
|
} catch (error) {
|
|
225
146
|
logger.error(formatBlockingError('Datasource test failed:'), error.message);
|
|
226
147
|
process.exit(4);
|
|
@@ -274,6 +195,7 @@ async function runIntegrationOnceForWatch(datasourceKey, integOpts, options, uni
|
|
|
274
195
|
if (unifiedModes) {
|
|
275
196
|
const uni = unifiedCliResultFromIntegrationReturn(result);
|
|
276
197
|
const exitCode = finalizeUnifiedValidationResult(uni, unifiedDisplayOpts);
|
|
198
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-integration (watch)');
|
|
277
199
|
return { exitCode, envelope: uni.envelope };
|
|
278
200
|
}
|
|
279
201
|
displayIntegrationTestResults(
|
|
@@ -285,7 +207,11 @@ async function runIntegrationOnceForWatch(datasourceKey, integOpts, options, uni
|
|
|
285
207
|
options.verbose,
|
|
286
208
|
{ debug: options.debug, runType: 'integration' }
|
|
287
209
|
);
|
|
288
|
-
const exitCode = finalizeAfterIntegrationDisplay(result, {
|
|
210
|
+
const exitCode = finalizeAfterIntegrationDisplay(result, {
|
|
211
|
+
warningsAsErrors: unifiedDisplayOpts.warningsAsErrors === true,
|
|
212
|
+
requireCert: unifiedDisplayOpts.requireCert === true
|
|
213
|
+
});
|
|
214
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-integration (watch)');
|
|
289
215
|
return { exitCode, envelope: result.datasourceTestRun || null };
|
|
290
216
|
} catch (err) {
|
|
291
217
|
logger.error(formatBlockingError('Integration test failed:'), err.message);
|
|
@@ -313,7 +239,12 @@ async function integrationTestCommandAction(datasourceKey, options) {
|
|
|
313
239
|
const unifiedModes =
|
|
314
240
|
options.json || options.summary || options.warningsAsErrors || options.requireCert;
|
|
315
241
|
if (unifiedModes) {
|
|
316
|
-
|
|
242
|
+
const exitCode = finalizeUnifiedValidationResult(
|
|
243
|
+
unifiedCliResultFromIntegrationReturn(result),
|
|
244
|
+
unifiedDisplayOpts
|
|
245
|
+
);
|
|
246
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-integration');
|
|
247
|
+
process.exit(exitCode);
|
|
317
248
|
return;
|
|
318
249
|
}
|
|
319
250
|
displayIntegrationTestResults(
|
|
@@ -325,7 +256,12 @@ async function integrationTestCommandAction(datasourceKey, options) {
|
|
|
325
256
|
options.verbose,
|
|
326
257
|
{ debug: options.debug, runType: 'integration' }
|
|
327
258
|
);
|
|
328
|
-
|
|
259
|
+
const integExit = finalizeAfterIntegrationDisplay(result, {
|
|
260
|
+
warningsAsErrors: unifiedDisplayOpts.warningsAsErrors === true,
|
|
261
|
+
requireCert: unifiedDisplayOpts.requireCert === true
|
|
262
|
+
});
|
|
263
|
+
await afterUnifiedValidationCertSync(integExit, datasourceKey, options, 'datasource test-integration');
|
|
264
|
+
process.exit(integExit);
|
|
329
265
|
} catch (error) {
|
|
330
266
|
logger.error(formatBlockingError('Integration test failed:'), error.message);
|
|
331
267
|
process.exit(4);
|
|
@@ -364,6 +300,9 @@ async function runDatasourceTestE2ECliOnce(datasourceKey, options) {
|
|
|
364
300
|
recordId: options.recordId,
|
|
365
301
|
cleanup: options.cleanup,
|
|
366
302
|
primaryKeyValue: options.primaryKeyValue,
|
|
303
|
+
minVectorHits: options.minVectorHits,
|
|
304
|
+
minProcessed: options.minProcessed,
|
|
305
|
+
minRecordCount: options.minRecordCount,
|
|
367
306
|
timeout: options.timeout,
|
|
368
307
|
capabilityKey: options.capability,
|
|
369
308
|
sync: options.sync === true
|
|
@@ -402,6 +341,7 @@ async function runDatasourceTestE2ECliOnce(datasourceKey, options) {
|
|
|
402
341
|
|
|
403
342
|
async function runDatasourceTestE2ECliAction(datasourceKey, options) {
|
|
404
343
|
const { exitCode } = await runDatasourceTestE2ECliOnce(datasourceKey, options);
|
|
344
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-e2e');
|
|
405
345
|
process.exit(exitCode);
|
|
406
346
|
}
|
|
407
347
|
|
|
@@ -430,7 +370,9 @@ async function e2eTestCommandAction(datasourceKey, capabilityKey, options) {
|
|
|
430
370
|
watchFullDiff: mergedOptions.watchFullDiff === true,
|
|
431
371
|
runOnce: async() => {
|
|
432
372
|
try {
|
|
433
|
-
|
|
373
|
+
const r = await runDatasourceTestE2ECliOnce(datasourceKey, mergedOptions);
|
|
374
|
+
await afterUnifiedValidationCertSync(r.exitCode, datasourceKey, mergedOptions, 'datasource test-e2e (watch)');
|
|
375
|
+
return r;
|
|
434
376
|
} catch (err) {
|
|
435
377
|
logger.error(formatBlockingError('E2E test failed:'), err.message);
|
|
436
378
|
return { exitCode: 3, envelope: null };
|
|
@@ -456,23 +398,14 @@ function chainDatasourceTestE2ECommand(datasource) {
|
|
|
456
398
|
appHelp: 'Integration folder name (default: resolve from cwd if inside integration/<systemKey>/)',
|
|
457
399
|
verboseHelp: 'Audit / explain-oriented request flags where applicable',
|
|
458
400
|
debugHelp: 'includeDebug + log under integration/<systemKey>/logs/; TTY appendix: summary, full, or raw',
|
|
459
|
-
timeoutHelp:
|
|
401
|
+
timeoutHelp:
|
|
402
|
+
'Wall-clock budget for validation (ms); also raises per-request HTTP wait for slow E2E POST/polls (default 15m)',
|
|
460
403
|
timeoutDefault: String(15 * 60 * 1000)
|
|
461
404
|
});
|
|
462
|
-
return cmd
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
.option(
|
|
467
|
-
'--primary-key-value <value|@path>',
|
|
468
|
-
'Primary key value or path to JSON file (e.g. @pk.json) for e2eOptions.primaryKeyValue'
|
|
469
|
-
)
|
|
470
|
-
.option('--capability <key>', 'Capability drill-down (deprecated; use positional [capabilityKey])')
|
|
471
|
-
.option(
|
|
472
|
-
'--strict-capability-scope',
|
|
473
|
-
'Exit 1 if a capability drill-down is requested but the report lists more than one capabilities[] row (plan §2.3)'
|
|
474
|
-
)
|
|
475
|
-
.addHelpText('after', datasourceTestE2eHelpAfter());
|
|
405
|
+
return attachDatasourceTestE2eExclusiveOptions(cmd).addHelpText(
|
|
406
|
+
'after',
|
|
407
|
+
datasourceTestE2eHelpAfter()
|
|
408
|
+
);
|
|
476
409
|
}
|
|
477
410
|
|
|
478
411
|
function setupDatasourceTestE2ECommand(datasource) {
|
|
@@ -42,6 +42,7 @@ function datasourceTestE2eHelpAfter() {
|
|
|
42
42
|
Examples:
|
|
43
43
|
$ aifabrix datasource test-e2e hubspot-users
|
|
44
44
|
$ aifabrix datasource test-e2e hubspot-users --app test-e2e-hubspot -v --debug
|
|
45
|
+
$ aifabrix datasource test-e2e my-documents-ds --min-vector-hits 7 -v --debug
|
|
45
46
|
$ aifabrix datasource test-e2e hubspot-users -a test-e2e-hubspot --no-async
|
|
46
47
|
$ aifabrix datasource test-e2e hubspot-users read
|
|
47
48
|
|
|
@@ -129,7 +130,11 @@ function attachDatasourceTestCommonOptions(cmd, opts) {
|
|
|
129
130
|
.option('--json', 'Print raw DatasourceTestRun JSON to stdout')
|
|
130
131
|
.option('--summary', 'Print compact summary line')
|
|
131
132
|
.option('--warnings-as-errors', 'Exit 1 when root status is warn')
|
|
132
|
-
.option('--require-cert', 'Exit 2 when certificate missing or not_passed')
|
|
133
|
+
.option('--require-cert', 'Exit 2 when certificate missing or not_passed')
|
|
134
|
+
.option(
|
|
135
|
+
'--no-cert-sync',
|
|
136
|
+
'Skip updating system file certification from the dataplane after a successful run'
|
|
137
|
+
);
|
|
133
138
|
|
|
134
139
|
if (includeNoAsync) {
|
|
135
140
|
cmd.option('--no-async', 'Do not poll; fail if report is not complete in first response');
|
|
@@ -139,11 +144,48 @@ function attachDatasourceTestCommonOptions(cmd, opts) {
|
|
|
139
144
|
return cmd;
|
|
140
145
|
}
|
|
141
146
|
|
|
147
|
+
/**
|
|
148
|
+
* E2E-only Commander flags (kept out of datasource-unified-test-cli.js for file size limits).
|
|
149
|
+
* @param {object} cmd
|
|
150
|
+
* @returns {object}
|
|
151
|
+
*/
|
|
152
|
+
function attachDatasourceTestE2eExclusiveOptions(cmd) {
|
|
153
|
+
return cmd
|
|
154
|
+
.option(
|
|
155
|
+
'--min-vector-hits <n>',
|
|
156
|
+
'Assert at least N vector search hits after sync (e2eOptions.minVectorHits)',
|
|
157
|
+
(v) => parseInt(String(v), 10)
|
|
158
|
+
)
|
|
159
|
+
.option(
|
|
160
|
+
'--min-processed <n>',
|
|
161
|
+
'Minimum records processed in sync step (e2eOptions.minProcessed)',
|
|
162
|
+
(v) => parseInt(String(v), 10)
|
|
163
|
+
)
|
|
164
|
+
.option(
|
|
165
|
+
'--min-record-count <n>',
|
|
166
|
+
'Minimum record count assertion (e2eOptions.minRecordCount)',
|
|
167
|
+
(v) => parseInt(String(v), 10)
|
|
168
|
+
)
|
|
169
|
+
.option('--test-crud', 'Enable CRUD lifecycle test (e2eOptions.testCrud)')
|
|
170
|
+
.option('--record-id <id>', 'Record ID for test (e2eOptions.recordId)')
|
|
171
|
+
.option('--no-cleanup', 'Disable cleanup after test (e2eOptions.cleanup: false)')
|
|
172
|
+
.option(
|
|
173
|
+
'--primary-key-value <value|@path>',
|
|
174
|
+
'Primary key value or path to JSON file (e.g. @pk.json) for e2eOptions.primaryKeyValue'
|
|
175
|
+
)
|
|
176
|
+
.option('--capability <key>', 'Capability drill-down (deprecated; use positional [capabilityKey])')
|
|
177
|
+
.option(
|
|
178
|
+
'--strict-capability-scope',
|
|
179
|
+
'Exit 1 if a capability drill-down is requested but the report lists more than one capabilities[] row (plan §2.3)'
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
142
183
|
module.exports = {
|
|
143
184
|
datasourceTestHelpAfter,
|
|
144
185
|
datasourceTestIntegrationHelpAfter,
|
|
145
186
|
datasourceTestE2eHelpAfter,
|
|
146
187
|
attachDatasourceWatchOptions,
|
|
147
|
-
attachDatasourceTestCommonOptions
|
|
188
|
+
attachDatasourceTestCommonOptions,
|
|
189
|
+
attachDatasourceTestE2eExclusiveOptions
|
|
148
190
|
};
|
|
149
191
|
|