@dataparade/cli 0.0.6 → 0.0.7
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 +3 -2
- package/dist/package.json +1 -1
- package/dist/src/cli.js +65 -36
- package/dist/src/config/env.js +7 -2
- package/dist/src/config/load-cli-env.d.ts +2 -0
- package/dist/src/config/load-cli-env.js +11 -2
- package/dist/src/config/redact.js +3 -0
- package/dist/src/config/validate-scan-ai.js +13 -7
- package/dist/src/core/pipeline/ai-orchestrator-options.js +8 -4
- package/dist/src/core/pipeline/orchestrator.js +1 -1
- package/dist/src/core/schema/scan-config.schema.d.ts +1 -0
- package/dist/src/core/schema/scan-config.schema.js +1 -0
- package/dist/src/core/types/config.d.ts +5 -0
- package/dist/src/platform-api/anonymous-ai-session-client.d.ts +14 -0
- package/dist/src/platform-api/anonymous-ai-session-client.js +40 -0
- package/dist/src/platform-api/upload-client.d.ts +10 -3
- package/dist/src/platform-api/upload-client.js +35 -16
- package/dist/src/platform-api/upload.types.d.ts +10 -5
- package/dist/src/upload/build-preview-url.d.ts +1 -0
- package/dist/src/upload/build-preview-url.js +6 -0
- package/dist/src/upload/run-upload.js +20 -4
- package/dist/src/upload/types.d.ts +2 -2
- package/dist/tests/cli/cli.spec.js +17 -3
- package/dist/tests/unit/cli/scan-command-ai-flags.spec.js +19 -0
- package/dist/tests/unit/cli/scan-command-e2e-monorepo-app-plus-terraform.spec.js +12 -8
- package/dist/tests/unit/cli/scan-command-e2e-monorepo-front-back-sections.spec.js +10 -1
- package/dist/tests/unit/cli/scan-command-e2e-python-basic.spec.js +10 -1
- package/dist/tests/unit/cli/scan-command-e2e-terraform-basic.spec.js +12 -8
- package/dist/tests/unit/cli/scan-command-e2e-ts-sample.spec.js +10 -1
- package/dist/tests/unit/cli/scan-command-e2e-ts-sentry-route-env.spec.js +1 -1
- package/dist/tests/unit/cli/scan-command-exit-code.spec.js +21 -0
- package/dist/tests/unit/cli/scan-quota-flow.spec.js +1 -1
- package/dist/tests/unit/config/load-cli-env.spec.d.ts +1 -0
- package/dist/tests/unit/config/load-cli-env.spec.js +52 -0
- package/dist/tests/unit/config/redact.spec.js +1 -1
- package/dist/tests/unit/config/resolve-config.spec.js +1 -1
- package/dist/tests/unit/config/validate-scan-ai.spec.js +25 -0
- package/dist/tests/unit/core/ai-orchestrator-options-anon.spec.d.ts +1 -0
- package/dist/tests/unit/core/ai-orchestrator-options-anon.spec.js +29 -0
- package/dist/tests/unit/core/dependency-manifest-performance-budgets.spec.js +1 -1
- package/dist/tests/unit/core/graph-mapping.spec.js +1 -1
- package/dist/tests/unit/core/invariant-tests-dedupe-determinism.spec.js +1 -1
- package/dist/tests/unit/core/no-source-code-leakage.spec.js +1 -1
- package/dist/tests/unit/core/orchestrator.spec.js +12 -12
- package/dist/tests/unit/core/output-json.spec.js +2 -2
- package/dist/tests/unit/core/parsing-error-handling-nonfatal.spec.js +1 -1
- package/dist/tests/unit/core/scan-result-language-stats.spec.js +1 -1
- package/dist/tests/unit/core/structural-scan.spec.js +1 -1
- package/dist/tests/unit/core/terraform-json-overlay.spec.js +1 -1
- package/dist/tests/unit/platform-api/anonymous-ai-session-client.spec.d.ts +1 -0
- package/dist/tests/unit/platform-api/anonymous-ai-session-client.spec.js +35 -0
- package/dist/tests/unit/upload/build-preview-url.spec.js +16 -0
- package/dist/tests/unit/upload/run-upload.spec.d.ts +1 -0
- package/dist/tests/unit/upload/run-upload.spec.js +43 -0
- package/package.json +1 -1
|
@@ -34,14 +34,33 @@ jest.mock("../../../src/core/pipeline/graph-mapping", () => ({
|
|
|
34
34
|
}));
|
|
35
35
|
jest.mock("../../../src/output/json", () => ({
|
|
36
36
|
writeDataflowJson: jest.fn(() => { }),
|
|
37
|
+
buildDataflowWrapper: jest.fn(() => ({
|
|
38
|
+
schemaVersion: "1.0",
|
|
39
|
+
graph: { nodes: [], edges: [], viewport: { x: 0, y: 0, zoom: 1 } },
|
|
40
|
+
metadata: {
|
|
41
|
+
componentsCount: 0,
|
|
42
|
+
dataFlowsCount: 0,
|
|
43
|
+
filesScanned: 0,
|
|
44
|
+
scanDurationMs: 0,
|
|
45
|
+
},
|
|
46
|
+
})),
|
|
37
47
|
}));
|
|
38
48
|
describe("cli scan command ai flags", () => {
|
|
39
49
|
let consoleLogSpy;
|
|
50
|
+
let prevSkipAutoUpload;
|
|
40
51
|
beforeEach(() => {
|
|
41
52
|
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
53
|
+
prevSkipAutoUpload = process.env.DATAPARADE_SKIP_AUTO_UPLOAD;
|
|
54
|
+
process.env.DATAPARADE_SKIP_AUTO_UPLOAD = "true";
|
|
42
55
|
});
|
|
43
56
|
afterEach(() => {
|
|
44
57
|
consoleLogSpy.mockRestore();
|
|
58
|
+
if (prevSkipAutoUpload === undefined) {
|
|
59
|
+
delete process.env.DATAPARADE_SKIP_AUTO_UPLOAD;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
process.env.DATAPARADE_SKIP_AUTO_UPLOAD = prevSkipAutoUpload;
|
|
63
|
+
}
|
|
45
64
|
});
|
|
46
65
|
it("normalizes --ai-inference-scope third-party-only to third_party_only", async () => {
|
|
47
66
|
const { run } = require("../../../src/cli");
|
|
@@ -21,12 +21,19 @@ function nodeSourcePaths(node) {
|
|
|
21
21
|
}
|
|
22
22
|
describe("cli scan command - monorepo app plus terraform", () => {
|
|
23
23
|
it("keeps app API hub, reduces TF to provider and module shells, and lays out app left of TF hub", async () => {
|
|
24
|
-
const prevAiInference = process.env.DATAPARADE_AI_INFERENCE;
|
|
25
|
-
process.env.DATAPARADE_AI_INFERENCE = "false";
|
|
26
24
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "monorepo-app-plus-terraform");
|
|
27
25
|
const outputPath = path_1.default.join(os_1.default.tmpdir(), `dataparade-scan-e2e-monorepo-app-plus-terraform-${Date.now()}.json`);
|
|
26
|
+
await (0, cli_1.run)([
|
|
27
|
+
"node",
|
|
28
|
+
"cli",
|
|
29
|
+
"scan",
|
|
30
|
+
fixturesRoot,
|
|
31
|
+
"--output",
|
|
32
|
+
outputPath,
|
|
33
|
+
"--no-ai-inference",
|
|
34
|
+
"--skip-auto-upload",
|
|
35
|
+
]);
|
|
28
36
|
try {
|
|
29
|
-
await (0, cli_1.run)(["node", "cli", "scan", fixturesRoot, "--output", outputPath]);
|
|
30
37
|
const contents = fs_1.default.readFileSync(outputPath, "utf8");
|
|
31
38
|
const parsed = JSON.parse(contents);
|
|
32
39
|
const validation = (0, dataflow_wrapper_schema_1.validateDataflowJson)(parsed);
|
|
@@ -96,11 +103,8 @@ describe("cli scan command - monorepo app plus terraform", () => {
|
|
|
96
103
|
fs_1.default.unlinkSync(outputPath);
|
|
97
104
|
}
|
|
98
105
|
finally {
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
process.env.DATAPARADE_AI_INFERENCE = prevAiInference;
|
|
106
|
+
if (fs_1.default.existsSync(outputPath)) {
|
|
107
|
+
fs_1.default.unlinkSync(outputPath);
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
}, 20000);
|
|
@@ -12,7 +12,16 @@ describe("cli scan command - monorepo sections", () => {
|
|
|
12
12
|
it("tags nodes with section_id and avoids cross-section edges", async () => {
|
|
13
13
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "monorepo-front-back-sections");
|
|
14
14
|
const outputPath = path_1.default.join(os_1.default.tmpdir(), `dataparade-scan-e2e-monorepo-front-back-sections-${Date.now()}.json`);
|
|
15
|
-
await (0, cli_1.run)([
|
|
15
|
+
await (0, cli_1.run)([
|
|
16
|
+
"node",
|
|
17
|
+
"cli",
|
|
18
|
+
"scan",
|
|
19
|
+
fixturesRoot,
|
|
20
|
+
"--output",
|
|
21
|
+
outputPath,
|
|
22
|
+
"--no-ai-inference",
|
|
23
|
+
"--skip-auto-upload",
|
|
24
|
+
]);
|
|
16
25
|
const contents = fs_1.default.readFileSync(outputPath, "utf8");
|
|
17
26
|
const parsed = JSON.parse(contents);
|
|
18
27
|
const validation = (0, dataflow_wrapper_schema_1.validateDataflowJson)(parsed);
|
|
@@ -12,7 +12,16 @@ describe("cli scan command - DP-P0-CLI-705 e2e Python basic", () => {
|
|
|
12
12
|
it("scans python-basic fixture and produces valid dataflow.json with route, database, third-party, and flows", async () => {
|
|
13
13
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "python-basic");
|
|
14
14
|
const outputPath = path_1.default.join(os_1.default.tmpdir(), `dataparade-scan-e2e-python-basic-${Date.now()}.json`);
|
|
15
|
-
await (0, cli_1.run)([
|
|
15
|
+
await (0, cli_1.run)([
|
|
16
|
+
"node",
|
|
17
|
+
"cli",
|
|
18
|
+
"scan",
|
|
19
|
+
fixturesRoot,
|
|
20
|
+
"--output",
|
|
21
|
+
outputPath,
|
|
22
|
+
"--no-ai-inference",
|
|
23
|
+
"--skip-auto-upload",
|
|
24
|
+
]);
|
|
16
25
|
const contents = fs_1.default.readFileSync(outputPath, "utf8");
|
|
17
26
|
const parsed = JSON.parse(contents);
|
|
18
27
|
const validation = (0, dataflow_wrapper_schema_1.validateDataflowJson)(parsed);
|
|
@@ -10,12 +10,19 @@ const cli_1 = require("../../../src/cli");
|
|
|
10
10
|
const dataflow_wrapper_schema_1 = require("../../../src/core/schema/dataflow-wrapper.schema");
|
|
11
11
|
describe("cli scan command - terraform-basic fixture", () => {
|
|
12
12
|
it("scans terraform-basic fixture and produces valid dataflow.json with nodes and edges", async () => {
|
|
13
|
-
const prevAiInference = process.env.DATAPARADE_AI_INFERENCE;
|
|
14
|
-
process.env.DATAPARADE_AI_INFERENCE = "false";
|
|
15
13
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "terraform-basic");
|
|
16
14
|
const outputPath = path_1.default.join(os_1.default.tmpdir(), `dataparade-scan-e2e-terraform-basic-${Date.now()}.json`);
|
|
15
|
+
await (0, cli_1.run)([
|
|
16
|
+
"node",
|
|
17
|
+
"cli",
|
|
18
|
+
"scan",
|
|
19
|
+
fixturesRoot,
|
|
20
|
+
"--output",
|
|
21
|
+
outputPath,
|
|
22
|
+
"--no-ai-inference",
|
|
23
|
+
"--skip-auto-upload",
|
|
24
|
+
]);
|
|
17
25
|
try {
|
|
18
|
-
await (0, cli_1.run)(["node", "cli", "scan", fixturesRoot, "--output", outputPath]);
|
|
19
26
|
const contents = fs_1.default.readFileSync(outputPath, "utf8");
|
|
20
27
|
const parsed = JSON.parse(contents);
|
|
21
28
|
const validation = (0, dataflow_wrapper_schema_1.validateDataflowJson)(parsed);
|
|
@@ -51,11 +58,8 @@ describe("cli scan command - terraform-basic fixture", () => {
|
|
|
51
58
|
fs_1.default.unlinkSync(outputPath);
|
|
52
59
|
}
|
|
53
60
|
finally {
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
process.env.DATAPARADE_AI_INFERENCE = prevAiInference;
|
|
61
|
+
if (fs_1.default.existsSync(outputPath)) {
|
|
62
|
+
fs_1.default.unlinkSync(outputPath);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
}, 15000);
|
|
@@ -12,7 +12,16 @@ describe("cli scan command - DP-P0-CLI-404 e2e TypeScript sample", () => {
|
|
|
12
12
|
it("scans the e2e-ts-sample fixture and produces a valid dataflow.json with database and third-party components and flows", async () => {
|
|
13
13
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "e2e-ts-sample");
|
|
14
14
|
const outputPath = path_1.default.join(os_1.default.tmpdir(), `dataparade-scan-e2e-ts-sample-${Date.now()}.json`);
|
|
15
|
-
await (0, cli_1.run)([
|
|
15
|
+
await (0, cli_1.run)([
|
|
16
|
+
"node",
|
|
17
|
+
"cli",
|
|
18
|
+
"scan",
|
|
19
|
+
fixturesRoot,
|
|
20
|
+
"--output",
|
|
21
|
+
outputPath,
|
|
22
|
+
"--no-ai-inference",
|
|
23
|
+
"--skip-auto-upload",
|
|
24
|
+
]);
|
|
16
25
|
const contents = fs_1.default.readFileSync(outputPath, "utf8");
|
|
17
26
|
const parsed = JSON.parse(contents);
|
|
18
27
|
const validation = (0, dataflow_wrapper_schema_1.validateDataflowJson)(parsed);
|
|
@@ -8,7 +8,7 @@ const orchestrator_1 = require("../../../src/core/pipeline/orchestrator");
|
|
|
8
8
|
describe("cli scan - DP-P0-CLI regression: sentry/route/env", () => {
|
|
9
9
|
it("detects Sentry third_party, Express route, and API_KEY env_variable", async () => {
|
|
10
10
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "e2e-ts-sentry-route-env");
|
|
11
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
11
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false,
|
|
12
12
|
projectName: "SentryRouteEnvApp",
|
|
13
13
|
});
|
|
14
14
|
const { scanResult, findings } = await (0, orchestrator_1.scan)(fixturesRoot, config, undefined);
|
|
@@ -44,14 +44,35 @@ jest.mock("../../../src/core/pipeline/graph-mapping", () => ({
|
|
|
44
44
|
}));
|
|
45
45
|
jest.mock("../../../src/output/json", () => ({
|
|
46
46
|
writeDataflowJson: jest.fn(async () => { }),
|
|
47
|
+
buildDataflowWrapper: jest.fn(() => ({
|
|
48
|
+
schemaVersion: "1.0",
|
|
49
|
+
graph: { nodes: [], edges: [], viewport: { x: 0, y: 0, zoom: 1 } },
|
|
50
|
+
metadata: {
|
|
51
|
+
componentsCount: 0,
|
|
52
|
+
dataFlowsCount: 0,
|
|
53
|
+
filesScanned: 0,
|
|
54
|
+
scanDurationMs: 0,
|
|
55
|
+
},
|
|
56
|
+
})),
|
|
47
57
|
}));
|
|
48
58
|
describe("cli scan command - exit codes", () => {
|
|
49
59
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
50
60
|
function tmpOutputPath() {
|
|
51
61
|
return path_1.default.join(os_1.default.tmpdir(), `dataparade-exit-${Date.now()}.json`);
|
|
52
62
|
}
|
|
63
|
+
let prevSkipAutoUpload;
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
prevSkipAutoUpload = process.env.DATAPARADE_SKIP_AUTO_UPLOAD;
|
|
66
|
+
process.env.DATAPARADE_SKIP_AUTO_UPLOAD = "true";
|
|
67
|
+
});
|
|
53
68
|
afterEach(() => {
|
|
54
69
|
process.exitCode = undefined;
|
|
70
|
+
if (prevSkipAutoUpload === undefined) {
|
|
71
|
+
delete process.env.DATAPARADE_SKIP_AUTO_UPLOAD;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
process.env.DATAPARADE_SKIP_AUTO_UPLOAD = prevSkipAutoUpload;
|
|
75
|
+
}
|
|
55
76
|
});
|
|
56
77
|
it("sets exitCode=1 when scanResult.errors is non-empty", async () => {
|
|
57
78
|
const { run } = require("../../../src/cli");
|
|
@@ -33,7 +33,7 @@ describe("scan quota flow", () => {
|
|
|
33
33
|
it("skips quota API when workspace key is present but AI is disabled", async () => {
|
|
34
34
|
process.env.DATAPARADE_WORKSPACE_API_KEY = "dp_live_test";
|
|
35
35
|
process.env.DATAPARADE_SKIP_AUTO_UPLOAD = "true";
|
|
36
|
-
await (0, cli_1.run)(["node", "cli", "scan", tempRoot]);
|
|
36
|
+
await (0, cli_1.run)(["node", "cli", "scan", tempRoot, "--no-ai-inference"]);
|
|
37
37
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
38
38
|
});
|
|
39
39
|
it("prints workspace quota message when preflight is quota-blocked", async () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
7
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const load_cli_env_1 = require("../../../src/config/load-cli-env");
|
|
10
|
+
const CLI_PACKAGE_NAME = "@dataparade/cli";
|
|
11
|
+
function writeCliPackageJson(dir) {
|
|
12
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
13
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(dir, "package.json"), JSON.stringify({ name: CLI_PACKAGE_NAME, version: "0.0.0" }));
|
|
14
|
+
}
|
|
15
|
+
describe("findCliPackageRoot", () => {
|
|
16
|
+
let tempRoot;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
tempRoot = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "dataparade-cli-root-"));
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
node_fs_1.default.rmSync(tempRoot, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
it("prefers the package root over dist/ when tsc copies package.json", () => {
|
|
24
|
+
const packageRoot = node_path_1.default.join(tempRoot, "cli");
|
|
25
|
+
writeCliPackageJson(packageRoot);
|
|
26
|
+
writeCliPackageJson(node_path_1.default.join(packageRoot, "dist"));
|
|
27
|
+
const startDir = node_path_1.default.join(packageRoot, "dist", "src", "config");
|
|
28
|
+
node_fs_1.default.mkdirSync(startDir, { recursive: true });
|
|
29
|
+
expect((0, load_cli_env_1.findCliPackageRoot)(startDir)).toBe(packageRoot);
|
|
30
|
+
});
|
|
31
|
+
it("returns the npm package root when dist/ contains a copied package.json", () => {
|
|
32
|
+
const packageRoot = node_path_1.default.join(tempRoot, "node_modules", "@dataparade", "cli");
|
|
33
|
+
writeCliPackageJson(packageRoot);
|
|
34
|
+
writeCliPackageJson(node_path_1.default.join(packageRoot, "dist"));
|
|
35
|
+
const startDir = node_path_1.default.join(packageRoot, "dist", "src", "config");
|
|
36
|
+
node_fs_1.default.mkdirSync(startDir, { recursive: true });
|
|
37
|
+
expect((0, load_cli_env_1.findCliPackageRoot)(startDir)).toBe(packageRoot);
|
|
38
|
+
});
|
|
39
|
+
it("returns dist/ when it is the only matching package root", () => {
|
|
40
|
+
const distRoot = node_path_1.default.join(tempRoot, "dist-only");
|
|
41
|
+
writeCliPackageJson(distRoot);
|
|
42
|
+
const startDir = node_path_1.default.join(distRoot, "src", "config");
|
|
43
|
+
node_fs_1.default.mkdirSync(startDir, { recursive: true });
|
|
44
|
+
expect((0, load_cli_env_1.findCliPackageRoot)(startDir)).toBe(distRoot);
|
|
45
|
+
});
|
|
46
|
+
it("returns undefined when no @dataparade/cli package.json exists", () => {
|
|
47
|
+
const startDir = node_path_1.default.join(tempRoot, "other", "src");
|
|
48
|
+
node_fs_1.default.mkdirSync(startDir, { recursive: true });
|
|
49
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(tempRoot, "other", "package.json"), JSON.stringify({ name: "other-package" }));
|
|
50
|
+
expect((0, load_cli_env_1.findCliPackageRoot)(startDir)).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -12,7 +12,7 @@ describe("redactScanConfigurationForDisplay", () => {
|
|
|
12
12
|
expect(JSON.stringify(redacted)).not.toContain("sk-secret-key-12345");
|
|
13
13
|
});
|
|
14
14
|
it("leaves config unchanged when aiApiKey is absent", () => {
|
|
15
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
15
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
16
16
|
const redacted = (0, redact_1.redactScanConfigurationForDisplay)(config);
|
|
17
17
|
expect(redacted.aiApiKey).toBeUndefined();
|
|
18
18
|
});
|
|
@@ -98,7 +98,7 @@ describe("resolveScanConfiguration", () => {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
|
-
it("does not
|
|
101
|
+
it("does not set enableAiInference from BYOK envs alone (default comes from createDefaultScanConfiguration)", () => {
|
|
102
102
|
const prevProvider = process.env.SCAN_BYOK_PROVIDER;
|
|
103
103
|
const prevModel = process.env.SCAN_BYOK_MODEL;
|
|
104
104
|
const prevKey = process.env.SCAN_BYOK_API_KEY;
|
|
@@ -39,6 +39,31 @@ describe("validate-scan-ai", () => {
|
|
|
39
39
|
expect((0, validate_scan_ai_1.validateAiInferenceCredentials)(config)).toEqual([]);
|
|
40
40
|
expect((0, validate_scan_ai_1.resolveAiMode)(config)).toBe("platform");
|
|
41
41
|
});
|
|
42
|
+
it("accepts anonymous session token with job id for platform mode", () => {
|
|
43
|
+
const config = baseConfig({
|
|
44
|
+
enableAiInference: true,
|
|
45
|
+
anonSessionToken: "dp_anon_abc",
|
|
46
|
+
cliQuotaJobId: "job-1",
|
|
47
|
+
});
|
|
48
|
+
expect((0, validate_scan_ai_1.validateAiInferenceCredentials)(config)).toEqual([]);
|
|
49
|
+
expect((0, validate_scan_ai_1.resolveAiMode)(config)).toBe("platform");
|
|
50
|
+
});
|
|
51
|
+
it("rejects workspace key and anonymous session together", () => {
|
|
52
|
+
const errors = (0, validate_scan_ai_1.validateAiInferenceCredentials)(baseConfig({
|
|
53
|
+
enableAiInference: true,
|
|
54
|
+
workspaceApiKey: "dp_live_abc",
|
|
55
|
+
anonSessionToken: "dp_anon_abc",
|
|
56
|
+
cliQuotaJobId: "job-1",
|
|
57
|
+
}));
|
|
58
|
+
expect(errors.some((e) => e.includes("not both"))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("requires job id for anonymous platform mode", () => {
|
|
61
|
+
const errors = (0, validate_scan_ai_1.validateAiInferenceCredentials)(baseConfig({
|
|
62
|
+
enableAiInference: true,
|
|
63
|
+
anonSessionToken: "dp_anon_abc",
|
|
64
|
+
}));
|
|
65
|
+
expect(errors.some((e) => e.includes("job id"))).toBe(true);
|
|
66
|
+
});
|
|
42
67
|
it("accepts hosted worker infer proxy without BYOK or workspace key", () => {
|
|
43
68
|
const config = baseConfig({
|
|
44
69
|
enableAiInference: true,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ai_orchestrator_options_1 = require("../../../src/core/pipeline/ai-orchestrator-options");
|
|
4
|
+
function baseConfig(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
enableAPIDetection: true,
|
|
7
|
+
enableDatabaseDetection: true,
|
|
8
|
+
enableDataFlowDetection: true,
|
|
9
|
+
minimumConfidence: 0.5,
|
|
10
|
+
enableAiInference: true,
|
|
11
|
+
...overrides,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe("buildAgentOrchestratorOptions platform anon", () => {
|
|
15
|
+
it("wires platformProxy with anon session token as bearer", () => {
|
|
16
|
+
const options = (0, ai_orchestrator_options_1.buildAgentOrchestratorOptions)(baseConfig({
|
|
17
|
+
anonSessionToken: "dp_anon_session_token",
|
|
18
|
+
cliQuotaJobId: "job-1",
|
|
19
|
+
platformApiBaseUrl: "http://localhost:3000",
|
|
20
|
+
}), { llmEnabled: true, skipStructuralHeuristics: false });
|
|
21
|
+
expect(options.platformProxy).toEqual({
|
|
22
|
+
apiBaseUrl: "http://localhost:3000",
|
|
23
|
+
workspaceApiKey: "dp_anon_session_token",
|
|
24
|
+
jobId: "job-1",
|
|
25
|
+
});
|
|
26
|
+
expect(options.apiKey).toBeUndefined();
|
|
27
|
+
expect(options.providerConcurrency).toBe(1);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -47,7 +47,7 @@ describe("dependency-manifest performance budgets (non-fatal)", () => {
|
|
|
47
47
|
// Create enough manifests to exceed default budgets (maxManifestFiles=500).
|
|
48
48
|
await createManyTypeScriptManifests(root, 600);
|
|
49
49
|
await createManyPythonManifests(root, 600);
|
|
50
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
50
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false,
|
|
51
51
|
languages: ["typescript", "python"],
|
|
52
52
|
enableDataFlowDetection: false,
|
|
53
53
|
deepAnalysis: false,
|
|
@@ -11,7 +11,7 @@ const scan_result_builders_1 = require("../../helpers/scan-result-builders");
|
|
|
11
11
|
describe("core/pipeline/graph-mapping - DP-P0-CLI-402", () => {
|
|
12
12
|
it("builds a DiagramGraphJson from a real ScanResult for the typescript-basic fixture", async () => {
|
|
13
13
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
14
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
14
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
15
15
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
16
16
|
const graph = (0, graph_mapping_1.buildDiagramGraphFromScanResult)(scanResult);
|
|
17
17
|
expect(graph.nodes.length).toBe(scanResult.components.length);
|
|
@@ -19,7 +19,7 @@ function shuffleDeterministic(arr, mode) {
|
|
|
19
19
|
describe("invariant-tests - determinism", () => {
|
|
20
20
|
it("scan() twice produces identical graph outputs", async () => {
|
|
21
21
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
22
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
22
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false,
|
|
23
23
|
deepAnalysis: false,
|
|
24
24
|
});
|
|
25
25
|
const { scanResult: scanResult1 } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
@@ -40,7 +40,7 @@ function graphNodesContainCodeKey(graph) {
|
|
|
40
40
|
describe("output safety - no source code leakage (DP-P0-CLI-XXX)", () => {
|
|
41
41
|
it("does not emit `code` under graph.nodes[].data", async () => {
|
|
42
42
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
43
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
43
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
44
44
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
45
45
|
const graph = (0, graph_mapping_1.buildDiagramGraphFromScanResult)(scanResult);
|
|
46
46
|
expect(graphNodesContainCodeKey(graph)).toBe(false);
|
|
@@ -10,7 +10,7 @@ const orchestrator_1 = require("../../../src/core/pipeline/orchestrator");
|
|
|
10
10
|
describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
11
11
|
it("runs the full structural pipeline for the typescript-basic fixture", async () => {
|
|
12
12
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
13
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
13
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
14
14
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
15
15
|
expect(scanResult.components.length).toBeGreaterThan(0);
|
|
16
16
|
expect(scanResult.dataFlows.length).toBeGreaterThan(0);
|
|
@@ -21,7 +21,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
21
21
|
expect(Array.isArray(scanResult.warnings)).toBe(true);
|
|
22
22
|
});
|
|
23
23
|
it("enables Terraform and monorepo package section auto-inference by default", () => {
|
|
24
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
24
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
25
25
|
expect(config.autoInferTerraformStackSectionPathDepth).toBe(true);
|
|
26
26
|
expect(config.terraformStackSectionPathDepth).toBeUndefined();
|
|
27
27
|
expect(config.autoInferMonorepoPackageSectionPathDepth).toBe(true);
|
|
@@ -29,7 +29,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
29
29
|
});
|
|
30
30
|
it("honors excludePaths by reducing the number of scanned files", async () => {
|
|
31
31
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
32
|
-
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
32
|
+
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
33
33
|
const { scanResult: baseline } = await (0, orchestrator_1.scan)(fixturesRoot, baselineConfig);
|
|
34
34
|
const configWithExcludes = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
35
35
|
excludePaths: ["**/db.ts"],
|
|
@@ -39,7 +39,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
39
39
|
});
|
|
40
40
|
it("supports ? wildcard in excludePaths patterns", async () => {
|
|
41
41
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
42
|
-
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
42
|
+
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
43
43
|
const { files: baselineFiles } = await (0, orchestrator_1.scan)(fixturesRoot, baselineConfig);
|
|
44
44
|
const configWithQuestionGlob = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
45
45
|
excludePaths: ["d?.ts"],
|
|
@@ -52,7 +52,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
52
52
|
});
|
|
53
53
|
it("filters files by languages when languages are specified", async () => {
|
|
54
54
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
55
|
-
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
55
|
+
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
56
56
|
const { scanResult: baseline } = await (0, orchestrator_1.scan)(fixturesRoot, baselineConfig);
|
|
57
57
|
const configLanguagesOnlyTs = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
58
58
|
languages: ["typescript"],
|
|
@@ -79,7 +79,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
79
79
|
});
|
|
80
80
|
it("respects enableAPIDetection and enableDatabaseDetection", async () => {
|
|
81
81
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
82
|
-
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
82
|
+
const baselineConfig = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
83
83
|
const { findings: baselineFindings } = await (0, orchestrator_1.scan)(fixturesRoot, baselineConfig);
|
|
84
84
|
const apiDisabledConfig = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
85
85
|
enableAPIDetection: false,
|
|
@@ -95,7 +95,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
95
95
|
});
|
|
96
96
|
it("emits ScanProgress events in the expected phase order", async () => {
|
|
97
97
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
98
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
98
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
99
99
|
const phases = [];
|
|
100
100
|
await (0, orchestrator_1.scan)(fixturesRoot, config, (progress) => {
|
|
101
101
|
phases.push(progress.phase);
|
|
@@ -112,7 +112,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
112
112
|
});
|
|
113
113
|
it("detects third-party services from Python dependency manifests", async () => {
|
|
114
114
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "python-dependency-manifests-basic");
|
|
115
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
115
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
116
116
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
117
117
|
const thirdParties = scanResult.components.filter((c) => c.type === "third_party");
|
|
118
118
|
const serviceNames = thirdParties
|
|
@@ -123,7 +123,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
123
123
|
});
|
|
124
124
|
it("detects third-party services from TypeScript dependency manifests", async () => {
|
|
125
125
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-dependency-manifests-basic");
|
|
126
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
126
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
127
127
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
128
128
|
const thirdParties = scanResult.components.filter((c) => c.type === "third_party");
|
|
129
129
|
const serviceNames = thirdParties
|
|
@@ -176,7 +176,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
176
176
|
await promises_1.default.writeFile(path_1.default.join(root, "src", "app.ts"), "export const x = 1;\n", "utf8");
|
|
177
177
|
await promises_1.default.writeFile(path_1.default.join(root, "src", "app.spec.ts"), "import { x } from './app';\n", "utf8");
|
|
178
178
|
await promises_1.default.writeFile(path_1.default.join(root, "playwright.config.ts"), "export default {};\n", "utf8");
|
|
179
|
-
const { files } = await (0, orchestrator_1.scan)(root, (0, orchestrator_1.createDefaultScanConfiguration)());
|
|
179
|
+
const { files } = await (0, orchestrator_1.scan)(root, (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false }));
|
|
180
180
|
const paths = files.map((f) => f.path);
|
|
181
181
|
expect(paths.some((p) => p.endsWith("app.spec.ts"))).toBe(false);
|
|
182
182
|
expect(paths.some((p) => p.includes("playwright.config"))).toBe(false);
|
|
@@ -188,7 +188,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
188
188
|
});
|
|
189
189
|
it("produces a combined ScanResult for mixed TypeScript and Python repos", async () => {
|
|
190
190
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "mixed-language-basic");
|
|
191
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
191
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
192
192
|
const { scanResult, files, findings } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
193
193
|
const scannedLanguages = new Set(files.map((file) => file.language));
|
|
194
194
|
expect(scannedLanguages.has("typescript")).toBe(true);
|
|
@@ -231,7 +231,7 @@ describe("core/pipeline/orchestrator - DP-P0-CLI-401", () => {
|
|
|
231
231
|
"export default app;",
|
|
232
232
|
"",
|
|
233
233
|
].join("\n"), "utf8");
|
|
234
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
234
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
235
235
|
const { scanResult } = await (0, orchestrator_1.scan)(root, config);
|
|
236
236
|
const postgresTargets = scanResult.components.filter((c) => c.type === "asset" &&
|
|
237
237
|
c.subType === "database" &&
|
|
@@ -13,7 +13,7 @@ const json_1 = require("../../../src/output/json");
|
|
|
13
13
|
describe("output/json - DP-P0-CLI-403", () => {
|
|
14
14
|
it("builds and writes a validated dataflow.json wrapper for a real ScanResult", async () => {
|
|
15
15
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
16
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
16
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
17
17
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
18
18
|
const graph = (0, graph_mapping_1.buildDiagramGraphFromScanResult)(scanResult);
|
|
19
19
|
const wrapper = (0, json_1.buildDataflowWrapper)(scanResult, graph, {
|
|
@@ -43,7 +43,7 @@ describe("output/json - DP-P0-CLI-403", () => {
|
|
|
43
43
|
});
|
|
44
44
|
it("throws and does not write a file when validation fails", async () => {
|
|
45
45
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
46
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
46
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
47
47
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
48
48
|
const graph = (0, graph_mapping_1.buildDiagramGraphFromScanResult)(scanResult);
|
|
49
49
|
// Create an intentionally invalid graph by clearing the first node id so
|
|
@@ -22,7 +22,7 @@ describe("parsing error handling - non-fatal", () => {
|
|
|
22
22
|
// Python file with a literal null byte to trigger parser warning.
|
|
23
23
|
// Use a Buffer to ensure the null byte is present in the file.
|
|
24
24
|
await promises_1.default.writeFile(path_1.default.join(root, "main.py"), Buffer.from("import os\n\x00\n", "utf8"));
|
|
25
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
25
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false,
|
|
26
26
|
languages: ["typescript", "python"],
|
|
27
27
|
enableDataFlowDetection: false,
|
|
28
28
|
});
|
|
@@ -4,7 +4,7 @@ const orchestrator_1 = require("../../../src/core/pipeline/orchestrator");
|
|
|
4
4
|
describe("ScanResult.languageStats for Python", () => {
|
|
5
5
|
it("populates languageStats entry for Python files", async () => {
|
|
6
6
|
const rootPath = `${__dirname}/../../fixtures/python-parser-basic`;
|
|
7
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)();
|
|
7
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false });
|
|
8
8
|
const { scanResult } = await (0, orchestrator_1.scan)(rootPath, config);
|
|
9
9
|
expect(scanResult.languageStats).toBeDefined();
|
|
10
10
|
const pythonStats = scanResult.languageStats?.find((entry) => entry.language === "python");
|
|
@@ -74,7 +74,7 @@ describe("core/pipeline/structural-scan - DP-P0-CLI-303", () => {
|
|
|
74
74
|
const fixturesRoot = path_1.default.join(__dirname, "..", "..", "fixtures", "typescript-basic");
|
|
75
75
|
const outside = path_1.default.join(fixturesRoot, "..", "terraform-show-extra-bucket.json");
|
|
76
76
|
const warnings = [];
|
|
77
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
77
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false,
|
|
78
78
|
terraformJsonPath: outside,
|
|
79
79
|
});
|
|
80
80
|
await (0, structural_scan_1.runStructuralScanPhase)(fixturesRoot, config, (w) => warnings.push(w));
|
|
@@ -15,7 +15,7 @@ describe("terraform JSON overlay in scan pipeline", () => {
|
|
|
15
15
|
const overlayInRoot = path_1.default.join(fixturesRoot, "show-overlay.json");
|
|
16
16
|
fs_1.default.copyFileSync(jsonFixture, overlayInRoot);
|
|
17
17
|
try {
|
|
18
|
-
const config = (0, orchestrator_1.createDefaultScanConfiguration)({
|
|
18
|
+
const config = (0, orchestrator_1.createDefaultScanConfiguration)({ enableAiInference: false,
|
|
19
19
|
terraformJsonPath: "show-overlay.json",
|
|
20
20
|
});
|
|
21
21
|
const { scanResult } = await (0, orchestrator_1.scan)(fixturesRoot, config);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const anonymous_ai_session_client_1 = require("../../../src/platform-api/anonymous-ai-session-client");
|
|
4
|
+
describe("cliAnonymousAiSession", () => {
|
|
5
|
+
const originalFetch = global.fetch;
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
global.fetch = originalFetch;
|
|
8
|
+
});
|
|
9
|
+
it("returns session payload on success", async () => {
|
|
10
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
11
|
+
ok: true,
|
|
12
|
+
json: async () => ({
|
|
13
|
+
sessionToken: "dp_anon_abc",
|
|
14
|
+
jobId: "job-1",
|
|
15
|
+
suggestedAiBudgetTokens: 100000,
|
|
16
|
+
expiresAt: "2026-01-01T00:00:00.000Z",
|
|
17
|
+
aiDelivery: "platform_proxy",
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
const out = await (0, anonymous_ai_session_client_1.cliAnonymousAiSession)({ projectName: "Demo" });
|
|
21
|
+
expect(out.sessionToken).toBe("dp_anon_abc");
|
|
22
|
+
expect(out.jobId).toBe("job-1");
|
|
23
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.stringContaining("/api/scans/cli/ai/anonymous-session"), expect.objectContaining({ method: "POST" }));
|
|
24
|
+
});
|
|
25
|
+
it("throws CliAnonymousIpLimitError on 429", async () => {
|
|
26
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
27
|
+
ok: false,
|
|
28
|
+
status: 429,
|
|
29
|
+
json: async () => ({
|
|
30
|
+
message: "Too many anonymous CLI scans from this IP (max 3).",
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
await expect((0, anonymous_ai_session_client_1.cliAnonymousAiSession)()).rejects.toBeInstanceOf(anonymous_ai_session_client_1.CliAnonymousIpLimitError);
|
|
34
|
+
});
|
|
35
|
+
});
|