@chanl/cli 2.0.4 → 3.1.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/dist/__tests__/agents.test.js +574 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/auth.test.d.ts +2 -0
- package/dist/__tests__/auth.test.js +87 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/calls.test.d.ts +2 -0
- package/dist/__tests__/calls.test.js +115 -0
- package/dist/__tests__/calls.test.js.map +1 -0
- package/dist/__tests__/chat.test.d.ts +2 -0
- package/dist/__tests__/chat.test.js +357 -0
- package/dist/__tests__/chat.test.js.map +1 -0
- package/dist/__tests__/cli-base.test.d.ts +2 -0
- package/dist/__tests__/cli-base.test.js +87 -0
- package/dist/__tests__/cli-base.test.js.map +1 -0
- package/dist/__tests__/cli-missing.test.d.ts +2 -0
- package/dist/__tests__/cli-missing.test.js +546 -0
- package/dist/__tests__/cli-missing.test.js.map +1 -0
- package/dist/__tests__/cli-p0-scenarios.test.d.ts +2 -0
- package/dist/__tests__/cli-p0-scenarios.test.js +123 -0
- package/dist/__tests__/cli-p0-scenarios.test.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +234 -0
- package/dist/__tests__/helpers.js +314 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/__tests__/mcp.test.d.ts +2 -0
- package/dist/__tests__/mcp.test.js +470 -0
- package/dist/__tests__/mcp.test.js.map +1 -0
- package/dist/__tests__/prompts.test.d.ts +2 -0
- package/dist/__tests__/prompts.test.js +227 -0
- package/dist/__tests__/prompts.test.js.map +1 -0
- package/dist/__tests__/scenarios.test.d.ts +2 -0
- package/dist/__tests__/scenarios.test.js +270 -0
- package/dist/__tests__/scenarios.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +2 -0
- package/dist/__tests__/tools.test.js +331 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/__tests__/toolsets.test.d.ts +2 -0
- package/dist/__tests__/toolsets.test.js +196 -0
- package/dist/__tests__/toolsets.test.js.map +1 -0
- package/dist/__tests__/workspaces.test.d.ts +2 -0
- package/dist/__tests__/workspaces.test.js +155 -0
- package/dist/__tests__/workspaces.test.js.map +1 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents.js +194 -6
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/chat.js +2 -2
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.js +24 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/customers.d.ts +8 -0
- package/dist/commands/customers.js +289 -0
- package/dist/commands/customers.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/memory.js +134 -1
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/messaging.d.ts +12 -0
- package/dist/commands/messaging.js +208 -0
- package/dist/commands/messaging.js.map +1 -0
- package/dist/commands/prompts.js +148 -1
- package/dist/commands/prompts.js.map +1 -1
- package/dist/commands/scenarios.js +121 -0
- package/dist/commands/scenarios.js.map +1 -1
- package/dist/commands/scorecards.js +3 -3
- package/dist/commands/scorecards.js.map +1 -1
- package/dist/commands/whatsapp.d.ts +8 -0
- package/dist/commands/whatsapp.js +332 -0
- package/dist/commands/whatsapp.js.map +1 -0
- package/dist/utils/config-store.d.ts +8 -0
- package/dist/utils/config-store.js +18 -1
- package/dist/utils/config-store.js.map +1 -1
- package/dist/utils/sdk-factory.js +15 -3
- package/dist/utils/sdk-factory.js.map +1 -1
- package/package.json +4 -4
- package/dist/__tests__/cli.test.js +0 -2308
- package/dist/__tests__/cli.test.js.map +0 -1
- /package/dist/__tests__/{cli.test.d.ts → agents.test.d.ts} +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { configStore } from "../utils/config-store";
|
|
3
|
+
import { ChanlSDK } from "@chanl/sdk";
|
|
4
|
+
import {
|
|
5
|
+
runCli,
|
|
6
|
+
makeMockSdk,
|
|
7
|
+
setupAuthenticatedConfigStore
|
|
8
|
+
} from "./helpers";
|
|
9
|
+
vi.mock("@chanl/sdk", () => ({
|
|
10
|
+
ChanlSDK: vi.fn()
|
|
11
|
+
}));
|
|
12
|
+
vi.mock("../utils/config-store", () => ({
|
|
13
|
+
configStore: {
|
|
14
|
+
getApiKey: vi.fn(),
|
|
15
|
+
getBaseUrl: vi.fn(() => "https://api.chanl.ai"),
|
|
16
|
+
getWorkspaceId: vi.fn(() => "ws_123"),
|
|
17
|
+
getJwtToken: vi.fn(() => null),
|
|
18
|
+
getAuthMethod: vi.fn(() => null),
|
|
19
|
+
getAppUrl: vi.fn(() => "https://app.chanl.ai"),
|
|
20
|
+
getDeployment: vi.fn(() => "cloud"),
|
|
21
|
+
getApiPrefix: vi.fn(() => "/api/v1"),
|
|
22
|
+
setApiKey: vi.fn(),
|
|
23
|
+
setBaseUrl: vi.fn(),
|
|
24
|
+
setWorkspaceId: vi.fn(),
|
|
25
|
+
removeApiKey: vi.fn(),
|
|
26
|
+
clearAuth: vi.fn(),
|
|
27
|
+
delete: vi.fn(),
|
|
28
|
+
isAuthenticated: vi.fn(),
|
|
29
|
+
getPath: vi.fn(() => "/home/user/.chanl/config.json")
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
describe("CLI", () => {
|
|
33
|
+
let mockSdk;
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
vi.clearAllMocks();
|
|
36
|
+
mockSdk = makeMockSdk();
|
|
37
|
+
ChanlSDK.mockImplementation(() => mockSdk);
|
|
38
|
+
setupAuthenticatedConfigStore(configStore);
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
process.exitCode = void 0;
|
|
42
|
+
});
|
|
43
|
+
describe("prompts list", () => {
|
|
44
|
+
it("should list prompts with table output", async () => {
|
|
45
|
+
mockSdk.prompts.list.mockResolvedValue({
|
|
46
|
+
success: true,
|
|
47
|
+
data: [
|
|
48
|
+
{
|
|
49
|
+
id: "prompt_abc123",
|
|
50
|
+
name: "Support Greeting",
|
|
51
|
+
category: "support",
|
|
52
|
+
status: "active",
|
|
53
|
+
version: 3,
|
|
54
|
+
createdAt: "2025-01-01T00:00:00Z",
|
|
55
|
+
updatedAt: "2025-01-15T10:30:00Z"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "prompt_def456",
|
|
59
|
+
name: "Sales Pitch",
|
|
60
|
+
category: "sales",
|
|
61
|
+
status: "draft",
|
|
62
|
+
version: 1,
|
|
63
|
+
createdAt: "2025-02-01T00:00:00Z",
|
|
64
|
+
updatedAt: "2025-02-05T10:30:00Z"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
message: "Success",
|
|
68
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
69
|
+
});
|
|
70
|
+
const { logs, exitCode } = await runCli(["prompts", "list"]);
|
|
71
|
+
expect(exitCode).toBe(0);
|
|
72
|
+
expect(mockSdk.prompts.list).toHaveBeenCalled();
|
|
73
|
+
const output = logs.join("\n");
|
|
74
|
+
expect(output).toContain("Support Greeting");
|
|
75
|
+
expect(output).toContain("Sales Pitch");
|
|
76
|
+
});
|
|
77
|
+
it("should list prompts with JSON output", async () => {
|
|
78
|
+
const mockPrompts = [
|
|
79
|
+
{
|
|
80
|
+
id: "prompt_abc123",
|
|
81
|
+
name: "Support Greeting",
|
|
82
|
+
category: "support",
|
|
83
|
+
status: "active"
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
mockSdk.prompts.list.mockResolvedValue({
|
|
87
|
+
success: true,
|
|
88
|
+
data: mockPrompts,
|
|
89
|
+
message: "Success",
|
|
90
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
91
|
+
});
|
|
92
|
+
const { logs, exitCode } = await runCli(["--json", "prompts", "list"]);
|
|
93
|
+
expect(exitCode).toBe(0);
|
|
94
|
+
const output = logs.join("\n");
|
|
95
|
+
const parsed = JSON.parse(output);
|
|
96
|
+
expect(parsed).toHaveLength(1);
|
|
97
|
+
expect(parsed[0].name).toBe("Support Greeting");
|
|
98
|
+
});
|
|
99
|
+
it("should unwrap API list shape { prompts: [...] } for JSON output", async () => {
|
|
100
|
+
mockSdk.prompts.list.mockResolvedValue({
|
|
101
|
+
success: true,
|
|
102
|
+
data: {
|
|
103
|
+
prompts: [{ id: "prompt_xyz", name: "Wrapped List", category: "general" }],
|
|
104
|
+
total: 1
|
|
105
|
+
},
|
|
106
|
+
message: "Success",
|
|
107
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
108
|
+
});
|
|
109
|
+
const { logs, exitCode } = await runCli(["--json", "prompts", "list"]);
|
|
110
|
+
expect(exitCode).toBe(0);
|
|
111
|
+
const parsed = JSON.parse(logs.join("\n"));
|
|
112
|
+
expect(parsed).toHaveLength(1);
|
|
113
|
+
expect(parsed[0].name).toBe("Wrapped List");
|
|
114
|
+
});
|
|
115
|
+
it("should show message when no prompts found", async () => {
|
|
116
|
+
mockSdk.prompts.list.mockResolvedValue({
|
|
117
|
+
success: true,
|
|
118
|
+
data: [],
|
|
119
|
+
message: "Success",
|
|
120
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
121
|
+
});
|
|
122
|
+
const { logs, exitCode } = await runCli(["prompts", "list"]);
|
|
123
|
+
expect(exitCode).toBe(0);
|
|
124
|
+
const output = logs.join("\n");
|
|
125
|
+
expect(output).toContain("No prompts found");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe("prompts get", () => {
|
|
129
|
+
it("should get prompt details by ID", async () => {
|
|
130
|
+
mockSdk.prompts.get.mockResolvedValue({
|
|
131
|
+
success: true,
|
|
132
|
+
data: {
|
|
133
|
+
id: "prompt_abc123",
|
|
134
|
+
name: "Support Greeting",
|
|
135
|
+
content: "Hello! How can I help you today?",
|
|
136
|
+
category: "support",
|
|
137
|
+
status: "active",
|
|
138
|
+
version: 3,
|
|
139
|
+
tags: ["greeting", "support"],
|
|
140
|
+
createdAt: "2025-01-01T00:00:00Z",
|
|
141
|
+
updatedAt: "2025-01-15T10:30:00Z"
|
|
142
|
+
},
|
|
143
|
+
message: "Success",
|
|
144
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
145
|
+
});
|
|
146
|
+
const { logs, exitCode } = await runCli(["prompts", "get", "prompt_abc123"]);
|
|
147
|
+
expect(exitCode).toBe(0);
|
|
148
|
+
expect(mockSdk.prompts.get).toHaveBeenCalledWith("prompt_abc123");
|
|
149
|
+
const output = logs.join("\n");
|
|
150
|
+
expect(output).toContain("Support Greeting");
|
|
151
|
+
expect(output).toContain("prompt_abc123");
|
|
152
|
+
});
|
|
153
|
+
it("should output JSON when --json flag is used", async () => {
|
|
154
|
+
mockSdk.prompts.get.mockResolvedValue({
|
|
155
|
+
success: true,
|
|
156
|
+
data: {
|
|
157
|
+
id: "prompt_abc123",
|
|
158
|
+
name: "Support Greeting",
|
|
159
|
+
content: "Hello! How can I help you today?",
|
|
160
|
+
category: "support",
|
|
161
|
+
status: "active"
|
|
162
|
+
},
|
|
163
|
+
message: "Success",
|
|
164
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
165
|
+
});
|
|
166
|
+
const { logs, exitCode } = await runCli(["--json", "prompts", "get", "prompt_abc123"]);
|
|
167
|
+
expect(exitCode).toBe(0);
|
|
168
|
+
const output = logs.join("\n");
|
|
169
|
+
const parsed = JSON.parse(output);
|
|
170
|
+
expect(parsed.name).toBe("Support Greeting");
|
|
171
|
+
expect(parsed.content).toBe("Hello! How can I help you today?");
|
|
172
|
+
});
|
|
173
|
+
it("should handle not found error", async () => {
|
|
174
|
+
mockSdk.prompts.get.mockResolvedValue({
|
|
175
|
+
success: false,
|
|
176
|
+
data: null,
|
|
177
|
+
message: "Prompt not found",
|
|
178
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
179
|
+
});
|
|
180
|
+
const { exitCode, errors } = await runCli(["prompts", "get", "nonexistent"]);
|
|
181
|
+
expect(exitCode).toBe(1);
|
|
182
|
+
expect(errors.some((e) => e.includes("Failed") || e.includes("not found"))).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
describe("prompts delete", () => {
|
|
186
|
+
it("should delete a prompt", async () => {
|
|
187
|
+
mockSdk.prompts.delete.mockResolvedValue({
|
|
188
|
+
success: true,
|
|
189
|
+
data: null,
|
|
190
|
+
message: "Success",
|
|
191
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
192
|
+
});
|
|
193
|
+
const { logs, exitCode } = await runCli(["prompts", "delete", "prompt_abc123", "--yes"]);
|
|
194
|
+
expect(exitCode).toBe(0);
|
|
195
|
+
expect(mockSdk.prompts.delete).toHaveBeenCalledWith("prompt_abc123");
|
|
196
|
+
const output = logs.join("\n");
|
|
197
|
+
expect(output).toContain("deleted");
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe("prompts versions list", () => {
|
|
201
|
+
it("should list prompt versions", async () => {
|
|
202
|
+
mockSdk.prompts.getVersions.mockResolvedValue({
|
|
203
|
+
success: true,
|
|
204
|
+
data: [{ version: 2, versionName: "v2", isActive: true, createdAt: "2025-01-01T00:00:00Z" }],
|
|
205
|
+
message: "Success",
|
|
206
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
207
|
+
});
|
|
208
|
+
const { exitCode } = await runCli(["prompts", "versions", "list", "prompt_abc123"]);
|
|
209
|
+
expect(exitCode).toBe(0);
|
|
210
|
+
expect(mockSdk.prompts.getVersions).toHaveBeenCalledWith("prompt_abc123");
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
describe("prompts revert", () => {
|
|
214
|
+
it("should revert to a version", async () => {
|
|
215
|
+
mockSdk.prompts.revert.mockResolvedValue({
|
|
216
|
+
success: true,
|
|
217
|
+
data: { id: "prompt_abc123", version: 1 },
|
|
218
|
+
message: "Success",
|
|
219
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
220
|
+
});
|
|
221
|
+
const { exitCode } = await runCli(["prompts", "revert", "prompt_abc123", "--to-version", "1"]);
|
|
222
|
+
expect(exitCode).toBe(0);
|
|
223
|
+
expect(mockSdk.prompts.revert).toHaveBeenCalledWith("prompt_abc123", { version: 1 });
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
//# sourceMappingURL=prompts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/prompts.test.ts"],"sourcesContent":["/**\n * CLI prompts tests — prompts list, prompts get, prompts delete\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi, Mock } from 'vitest';\nimport { configStore } from '../utils/config-store';\nimport { ChanlSDK } from '@chanl/sdk';\nimport {\n runCli,\n makeMockSdk,\n setupAuthenticatedConfigStore,\n type MockSdk,\n} from './helpers';\n\n// Mock the SDK\nvi.mock('@chanl/sdk', () => ({\n ChanlSDK: vi.fn(),\n}));\n\n// Mock config store\nvi.mock('../utils/config-store', () => ({\n configStore: {\n getApiKey: vi.fn(),\n getBaseUrl: vi.fn(() => 'https://api.chanl.ai'),\n getWorkspaceId: vi.fn(() => 'ws_123'),\n getJwtToken: vi.fn(() => null),\n getAuthMethod: vi.fn(() => null),\n getAppUrl: vi.fn(() => 'https://app.chanl.ai'),\n getDeployment: vi.fn(() => 'cloud'),\n getApiPrefix: vi.fn(() => '/api/v1'),\n setApiKey: vi.fn(),\n setBaseUrl: vi.fn(),\n setWorkspaceId: vi.fn(),\n removeApiKey: vi.fn(),\n clearAuth: vi.fn(),\n delete: vi.fn(),\n isAuthenticated: vi.fn(),\n getPath: vi.fn(() => '/home/user/.chanl/config.json'),\n },\n}));\n\ndescribe('CLI', () => {\n let mockSdk: MockSdk;\n\n beforeEach(() => {\n vi.clearAllMocks();\n mockSdk = makeMockSdk();\n (ChanlSDK as Mock).mockImplementation(() => mockSdk);\n setupAuthenticatedConfigStore(configStore as unknown as {\n getApiKey: Mock;\n getWorkspaceId: Mock;\n isAuthenticated: Mock;\n });\n });\n\n afterEach(() => {\n process.exitCode = undefined;\n });\n\n // ==========================================================================\n // PROMPTS CLI TESTS (Story 8)\n // ==========================================================================\n\n describe('prompts list', () => {\n it('should list prompts with table output', async () => {\n mockSdk.prompts.list.mockResolvedValue({\n success: true,\n data: [\n {\n id: 'prompt_abc123',\n name: 'Support Greeting',\n category: 'support',\n status: 'active',\n version: 3,\n createdAt: '2025-01-01T00:00:00Z',\n updatedAt: '2025-01-15T10:30:00Z',\n },\n {\n id: 'prompt_def456',\n name: 'Sales Pitch',\n category: 'sales',\n status: 'draft',\n version: 1,\n createdAt: '2025-02-01T00:00:00Z',\n updatedAt: '2025-02-05T10:30:00Z',\n },\n ],\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['prompts', 'list']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.prompts.list).toHaveBeenCalled();\n\n const output = logs.join('\\n');\n expect(output).toContain('Support Greeting');\n expect(output).toContain('Sales Pitch');\n });\n\n it('should list prompts with JSON output', async () => {\n const mockPrompts = [\n {\n id: 'prompt_abc123',\n name: 'Support Greeting',\n category: 'support',\n status: 'active',\n },\n ];\n\n mockSdk.prompts.list.mockResolvedValue({\n success: true,\n data: mockPrompts,\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'prompts', 'list']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed).toHaveLength(1);\n expect(parsed[0].name).toBe('Support Greeting');\n });\n\n it('should unwrap API list shape { prompts: [...] } for JSON output', async () => {\n mockSdk.prompts.list.mockResolvedValue({\n success: true,\n data: {\n prompts: [{ id: 'prompt_xyz', name: 'Wrapped List', category: 'general' }],\n total: 1,\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'prompts', 'list']);\n\n expect(exitCode).toBe(0);\n const parsed = JSON.parse(logs.join('\\n'));\n expect(parsed).toHaveLength(1);\n expect(parsed[0].name).toBe('Wrapped List');\n });\n\n it('should show message when no prompts found', async () => {\n mockSdk.prompts.list.mockResolvedValue({\n success: true,\n data: [],\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['prompts', 'list']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n expect(output).toContain('No prompts found');\n });\n });\n\n describe('prompts get', () => {\n it('should get prompt details by ID', async () => {\n mockSdk.prompts.get.mockResolvedValue({\n success: true,\n data: {\n id: 'prompt_abc123',\n name: 'Support Greeting',\n content: 'Hello! How can I help you today?',\n category: 'support',\n status: 'active',\n version: 3,\n tags: ['greeting', 'support'],\n createdAt: '2025-01-01T00:00:00Z',\n updatedAt: '2025-01-15T10:30:00Z',\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['prompts', 'get', 'prompt_abc123']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.prompts.get).toHaveBeenCalledWith('prompt_abc123');\n\n const output = logs.join('\\n');\n expect(output).toContain('Support Greeting');\n expect(output).toContain('prompt_abc123');\n });\n\n it('should output JSON when --json flag is used', async () => {\n mockSdk.prompts.get.mockResolvedValue({\n success: true,\n data: {\n id: 'prompt_abc123',\n name: 'Support Greeting',\n content: 'Hello! How can I help you today?',\n category: 'support',\n status: 'active',\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'prompts', 'get', 'prompt_abc123']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.name).toBe('Support Greeting');\n expect(parsed.content).toBe('Hello! How can I help you today?');\n });\n\n it('should handle not found error', async () => {\n mockSdk.prompts.get.mockResolvedValue({\n success: false,\n data: null,\n message: 'Prompt not found',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode, errors } = await runCli(['prompts', 'get', 'nonexistent']);\n\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Failed') || e.includes('not found'))).toBe(true);\n });\n });\n\n describe('prompts delete', () => {\n it('should delete a prompt', async () => {\n mockSdk.prompts.delete.mockResolvedValue({\n success: true,\n data: null,\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['prompts', 'delete', 'prompt_abc123', '--yes']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.prompts.delete).toHaveBeenCalledWith('prompt_abc123');\n\n const output = logs.join('\\n');\n expect(output).toContain('deleted');\n });\n });\n\n describe('prompts versions list', () => {\n it('should list prompt versions', async () => {\n mockSdk.prompts.getVersions.mockResolvedValue({\n success: true,\n data: [{ version: 2, versionName: 'v2', isActive: true, createdAt: '2025-01-01T00:00:00Z' }],\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['prompts', 'versions', 'list', 'prompt_abc123']);\n expect(exitCode).toBe(0);\n expect(mockSdk.prompts.getVersions).toHaveBeenCalledWith('prompt_abc123');\n });\n });\n\n describe('prompts revert', () => {\n it('should revert to a version', async () => {\n mockSdk.prompts.revert.mockResolvedValue({\n success: true,\n data: { id: 'prompt_abc123', version: 1 },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['prompts', 'revert', 'prompt_abc123', '--to-version', '1']);\n expect(exitCode).toBe(0);\n expect(mockSdk.prompts.revert).toHaveBeenCalledWith('prompt_abc123', { version: 1 });\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { configStore } from "../utils/config-store";
|
|
3
|
+
import { ChanlSDK } from "@chanl/sdk";
|
|
4
|
+
import {
|
|
5
|
+
runCli,
|
|
6
|
+
makeMockSdk,
|
|
7
|
+
setupAuthenticatedConfigStore
|
|
8
|
+
} from "./helpers";
|
|
9
|
+
vi.mock("@chanl/sdk", () => ({
|
|
10
|
+
ChanlSDK: vi.fn()
|
|
11
|
+
}));
|
|
12
|
+
vi.mock("../utils/config-store", () => ({
|
|
13
|
+
configStore: {
|
|
14
|
+
getApiKey: vi.fn(),
|
|
15
|
+
getBaseUrl: vi.fn(() => "https://api.chanl.ai"),
|
|
16
|
+
getWorkspaceId: vi.fn(() => "ws_123"),
|
|
17
|
+
getJwtToken: vi.fn(() => null),
|
|
18
|
+
getAuthMethod: vi.fn(() => null),
|
|
19
|
+
getAppUrl: vi.fn(() => "https://app.chanl.ai"),
|
|
20
|
+
getDeployment: vi.fn(() => "cloud"),
|
|
21
|
+
getApiPrefix: vi.fn(() => "/api/v1"),
|
|
22
|
+
setApiKey: vi.fn(),
|
|
23
|
+
setBaseUrl: vi.fn(),
|
|
24
|
+
setWorkspaceId: vi.fn(),
|
|
25
|
+
removeApiKey: vi.fn(),
|
|
26
|
+
clearAuth: vi.fn(),
|
|
27
|
+
delete: vi.fn(),
|
|
28
|
+
isAuthenticated: vi.fn(),
|
|
29
|
+
getPath: vi.fn(() => "/home/user/.chanl/config.json")
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
describe("CLI", () => {
|
|
33
|
+
let mockSdk;
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
vi.clearAllMocks();
|
|
36
|
+
mockSdk = makeMockSdk();
|
|
37
|
+
ChanlSDK.mockImplementation(() => mockSdk);
|
|
38
|
+
setupAuthenticatedConfigStore(configStore);
|
|
39
|
+
});
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
process.exitCode = void 0;
|
|
42
|
+
});
|
|
43
|
+
describe("scenarios execution with tool calls", () => {
|
|
44
|
+
const mockToolCalls = [
|
|
45
|
+
{
|
|
46
|
+
id: "tc-001",
|
|
47
|
+
name: "get_weather",
|
|
48
|
+
parameters: { city: "London" },
|
|
49
|
+
result: { temperature: "12\xB0C" },
|
|
50
|
+
status: "success",
|
|
51
|
+
durationMs: 245,
|
|
52
|
+
startedAt: "2025-01-09T10:30:00.000Z",
|
|
53
|
+
completedAt: "2025-01-09T10:30:00.245Z"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "tc-002",
|
|
57
|
+
name: "send_email",
|
|
58
|
+
parameters: { to: "user@test.com" },
|
|
59
|
+
status: "error",
|
|
60
|
+
durationMs: 1200,
|
|
61
|
+
startedAt: "2025-01-09T10:30:01.000Z",
|
|
62
|
+
error: { message: "SMTP connection failed", code: "NETWORK_ERROR" }
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
it("should display tool calls in execution details", async () => {
|
|
66
|
+
mockSdk.scenarios.getExecution.mockResolvedValue({
|
|
67
|
+
success: true,
|
|
68
|
+
data: {
|
|
69
|
+
id: "exec_tc1",
|
|
70
|
+
executionId: "exec_tc1",
|
|
71
|
+
scenarioId: "scen_abc123",
|
|
72
|
+
status: "completed",
|
|
73
|
+
overallScore: 85,
|
|
74
|
+
duration: 12.5,
|
|
75
|
+
stepResults: [
|
|
76
|
+
{
|
|
77
|
+
stepId: "step_1",
|
|
78
|
+
status: "completed",
|
|
79
|
+
actualResponse: "The weather is 12\xB0C in London.",
|
|
80
|
+
toolCalls: mockToolCalls
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
message: "Success",
|
|
85
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
86
|
+
});
|
|
87
|
+
const { logs, exitCode } = await runCli(["scenarios", "execution", "exec_tc1"]);
|
|
88
|
+
expect(exitCode).toBe(0);
|
|
89
|
+
expect(mockSdk.scenarios.getExecution).toHaveBeenCalledWith("exec_tc1");
|
|
90
|
+
const output = logs.join("\n");
|
|
91
|
+
expect(output).toContain("Tool Calls");
|
|
92
|
+
expect(output).toContain("get_weather");
|
|
93
|
+
expect(output).toContain("send_email");
|
|
94
|
+
expect(output).toContain("245ms");
|
|
95
|
+
expect(output).toContain("SMTP connection failed");
|
|
96
|
+
});
|
|
97
|
+
it("should include tool calls in JSON execution output", async () => {
|
|
98
|
+
mockSdk.scenarios.getExecution.mockResolvedValue({
|
|
99
|
+
success: true,
|
|
100
|
+
data: {
|
|
101
|
+
execution: {
|
|
102
|
+
id: "exec_tc2",
|
|
103
|
+
executionId: "exec_tc2",
|
|
104
|
+
scenarioId: "scen_abc123",
|
|
105
|
+
status: "completed",
|
|
106
|
+
overallScore: 85,
|
|
107
|
+
duration: 12.5,
|
|
108
|
+
stepResults: [
|
|
109
|
+
{
|
|
110
|
+
stepId: "step_1",
|
|
111
|
+
status: "completed",
|
|
112
|
+
actualResponse: "The weather is 12\xB0C in London.",
|
|
113
|
+
toolCalls: mockToolCalls
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
message: "Success",
|
|
119
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
120
|
+
});
|
|
121
|
+
const { logs, exitCode } = await runCli(["--json", "scenarios", "execution", "exec_tc2"]);
|
|
122
|
+
expect(exitCode).toBe(0);
|
|
123
|
+
const output = logs.join("\n");
|
|
124
|
+
const parsed = JSON.parse(output);
|
|
125
|
+
expect(parsed.execution).toBeDefined();
|
|
126
|
+
expect(parsed.execution.stepResults[0].toolCalls).toHaveLength(2);
|
|
127
|
+
expect(parsed.execution.stepResults[0].toolCalls[0].name).toBe("get_weather");
|
|
128
|
+
expect(parsed.execution.stepResults[0].toolCalls[1].name).toBe("send_email");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe("scenarios run-all", () => {
|
|
132
|
+
const mockBatchResult = {
|
|
133
|
+
agentId: "agent_abc",
|
|
134
|
+
totalScenarios: 3,
|
|
135
|
+
passed: 3,
|
|
136
|
+
failed: 0,
|
|
137
|
+
averageScore: 90,
|
|
138
|
+
allPassed: true,
|
|
139
|
+
minScore: 80,
|
|
140
|
+
results: [
|
|
141
|
+
{
|
|
142
|
+
scenarioId: "scen_001",
|
|
143
|
+
scenarioName: "Greeting Flow",
|
|
144
|
+
executionId: "exec_001",
|
|
145
|
+
status: "completed",
|
|
146
|
+
score: 90,
|
|
147
|
+
duration: 15.5,
|
|
148
|
+
toolCallCount: 2,
|
|
149
|
+
passed: true
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
scenarioId: "scen_002",
|
|
153
|
+
scenarioName: "Refund Flow",
|
|
154
|
+
executionId: "exec_002",
|
|
155
|
+
status: "completed",
|
|
156
|
+
score: 85,
|
|
157
|
+
duration: 22.3,
|
|
158
|
+
toolCallCount: 3,
|
|
159
|
+
passed: true
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
scenarioId: "scen_003",
|
|
163
|
+
scenarioName: "Escalation Flow",
|
|
164
|
+
executionId: "exec_003",
|
|
165
|
+
status: "completed",
|
|
166
|
+
score: 95,
|
|
167
|
+
duration: 10.1,
|
|
168
|
+
toolCallCount: 1,
|
|
169
|
+
passed: true
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
it("should display results when all scenarios pass", async () => {
|
|
174
|
+
mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);
|
|
175
|
+
const { logs, exitCode } = await runCli([
|
|
176
|
+
"scenarios",
|
|
177
|
+
"run-all",
|
|
178
|
+
"--agent",
|
|
179
|
+
"agent_abc",
|
|
180
|
+
"--min-score",
|
|
181
|
+
"80"
|
|
182
|
+
]);
|
|
183
|
+
expect(exitCode).toBe(0);
|
|
184
|
+
expect(mockSdk.scenarios.runAll).toHaveBeenCalledWith(
|
|
185
|
+
expect.objectContaining({
|
|
186
|
+
agentId: "agent_abc",
|
|
187
|
+
minScore: 80
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
const output = logs.join("\n");
|
|
191
|
+
expect(output).toContain("Greeting Flow");
|
|
192
|
+
expect(output).toContain("Refund Flow");
|
|
193
|
+
expect(output).toContain("Escalation Flow");
|
|
194
|
+
expect(output).toMatch(/3.*passed/i);
|
|
195
|
+
});
|
|
196
|
+
it("should exit code 1 when any scenario fails", async () => {
|
|
197
|
+
const failedResult = {
|
|
198
|
+
...mockBatchResult,
|
|
199
|
+
allPassed: false,
|
|
200
|
+
passed: 2,
|
|
201
|
+
failed: 1,
|
|
202
|
+
averageScore: 78,
|
|
203
|
+
results: [
|
|
204
|
+
{ ...mockBatchResult.results[0] },
|
|
205
|
+
{ ...mockBatchResult.results[1], score: 60, passed: false },
|
|
206
|
+
{ ...mockBatchResult.results[2] }
|
|
207
|
+
]
|
|
208
|
+
};
|
|
209
|
+
mockSdk.scenarios.runAll.mockResolvedValue(failedResult);
|
|
210
|
+
const { exitCode } = await runCli([
|
|
211
|
+
"scenarios",
|
|
212
|
+
"run-all",
|
|
213
|
+
"--agent",
|
|
214
|
+
"agent_abc",
|
|
215
|
+
"--min-score",
|
|
216
|
+
"80"
|
|
217
|
+
]);
|
|
218
|
+
expect(exitCode).toBe(1);
|
|
219
|
+
});
|
|
220
|
+
it("should output JSON with --json flag", async () => {
|
|
221
|
+
mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);
|
|
222
|
+
const { logs, exitCode } = await runCli([
|
|
223
|
+
"--json",
|
|
224
|
+
"scenarios",
|
|
225
|
+
"run-all",
|
|
226
|
+
"--agent",
|
|
227
|
+
"agent_abc"
|
|
228
|
+
]);
|
|
229
|
+
expect(exitCode).toBe(0);
|
|
230
|
+
const output = logs.join("\n");
|
|
231
|
+
const parsed = JSON.parse(output);
|
|
232
|
+
expect(parsed.allPassed).toBe(true);
|
|
233
|
+
expect(parsed.totalScenarios).toBe(3);
|
|
234
|
+
expect(parsed.results).toHaveLength(3);
|
|
235
|
+
});
|
|
236
|
+
it("should pass parallel option to SDK", async () => {
|
|
237
|
+
mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);
|
|
238
|
+
await runCli([
|
|
239
|
+
"scenarios",
|
|
240
|
+
"run-all",
|
|
241
|
+
"--agent",
|
|
242
|
+
"agent_abc",
|
|
243
|
+
"--parallel",
|
|
244
|
+
"5"
|
|
245
|
+
]);
|
|
246
|
+
expect(mockSdk.scenarios.runAll).toHaveBeenCalledWith(
|
|
247
|
+
expect.objectContaining({
|
|
248
|
+
agentId: "agent_abc",
|
|
249
|
+
parallel: 5
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
it("should display summary with average score", async () => {
|
|
254
|
+
mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);
|
|
255
|
+
const { logs } = await runCli([
|
|
256
|
+
"scenarios",
|
|
257
|
+
"run-all",
|
|
258
|
+
"--agent",
|
|
259
|
+
"agent_abc"
|
|
260
|
+
]);
|
|
261
|
+
const output = logs.join("\n");
|
|
262
|
+
expect(output).toContain("90");
|
|
263
|
+
});
|
|
264
|
+
it("should not call SDK when --agent option is missing", async () => {
|
|
265
|
+
await runCli(["scenarios", "run-all"]);
|
|
266
|
+
expect(mockSdk.scenarios.runAll).not.toHaveBeenCalled();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
//# sourceMappingURL=scenarios.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/scenarios.test.ts"],"sourcesContent":["/**\n * CLI scenarios tests — scenarios execution with tool calls, scenarios run-all\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi, Mock } from 'vitest';\nimport { configStore } from '../utils/config-store';\nimport { ChanlSDK } from '@chanl/sdk';\nimport {\n runCli,\n makeMockSdk,\n setupAuthenticatedConfigStore,\n type MockSdk,\n} from './helpers';\n\n// Mock the SDK\nvi.mock('@chanl/sdk', () => ({\n ChanlSDK: vi.fn(),\n}));\n\n// Mock config store\nvi.mock('../utils/config-store', () => ({\n configStore: {\n getApiKey: vi.fn(),\n getBaseUrl: vi.fn(() => 'https://api.chanl.ai'),\n getWorkspaceId: vi.fn(() => 'ws_123'),\n getJwtToken: vi.fn(() => null),\n getAuthMethod: vi.fn(() => null),\n getAppUrl: vi.fn(() => 'https://app.chanl.ai'),\n getDeployment: vi.fn(() => 'cloud'),\n getApiPrefix: vi.fn(() => '/api/v1'),\n setApiKey: vi.fn(),\n setBaseUrl: vi.fn(),\n setWorkspaceId: vi.fn(),\n removeApiKey: vi.fn(),\n clearAuth: vi.fn(),\n delete: vi.fn(),\n isAuthenticated: vi.fn(),\n getPath: vi.fn(() => '/home/user/.chanl/config.json'),\n },\n}));\n\ndescribe('CLI', () => {\n let mockSdk: MockSdk;\n\n beforeEach(() => {\n vi.clearAllMocks();\n mockSdk = makeMockSdk();\n (ChanlSDK as Mock).mockImplementation(() => mockSdk);\n setupAuthenticatedConfigStore(configStore as unknown as {\n getApiKey: Mock;\n getWorkspaceId: Mock;\n isAuthenticated: Mock;\n });\n });\n\n afterEach(() => {\n process.exitCode = undefined;\n });\n\n describe('scenarios execution with tool calls', () => {\n const mockToolCalls = [\n {\n id: 'tc-001',\n name: 'get_weather',\n parameters: { city: 'London' },\n result: { temperature: '12°C' },\n status: 'success',\n durationMs: 245,\n startedAt: '2025-01-09T10:30:00.000Z',\n completedAt: '2025-01-09T10:30:00.245Z',\n },\n {\n id: 'tc-002',\n name: 'send_email',\n parameters: { to: 'user@test.com' },\n status: 'error',\n durationMs: 1200,\n startedAt: '2025-01-09T10:30:01.000Z',\n error: { message: 'SMTP connection failed', code: 'NETWORK_ERROR' },\n },\n ];\n\n it('should display tool calls in execution details', async () => {\n mockSdk.scenarios.getExecution.mockResolvedValue({\n success: true,\n data: {\n id: 'exec_tc1',\n executionId: 'exec_tc1',\n scenarioId: 'scen_abc123',\n status: 'completed',\n overallScore: 85,\n duration: 12.5,\n stepResults: [\n {\n stepId: 'step_1',\n status: 'completed',\n actualResponse: 'The weather is 12°C in London.',\n toolCalls: mockToolCalls,\n },\n ],\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['scenarios', 'execution', 'exec_tc1']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.scenarios.getExecution).toHaveBeenCalledWith('exec_tc1');\n\n const output = logs.join('\\n');\n // Should display Tool Calls section\n expect(output).toContain('Tool Calls');\n // Should display tool names\n expect(output).toContain('get_weather');\n expect(output).toContain('send_email');\n // Should display duration\n expect(output).toContain('245ms');\n // Should display error message for failed tool call\n expect(output).toContain('SMTP connection failed');\n });\n\n it('should include tool calls in JSON execution output', async () => {\n mockSdk.scenarios.getExecution.mockResolvedValue({\n success: true,\n data: {\n execution: {\n id: 'exec_tc2',\n executionId: 'exec_tc2',\n scenarioId: 'scen_abc123',\n status: 'completed',\n overallScore: 85,\n duration: 12.5,\n stepResults: [\n {\n stepId: 'step_1',\n status: 'completed',\n actualResponse: 'The weather is 12°C in London.',\n toolCalls: mockToolCalls,\n },\n ],\n },\n },\n message: 'Success',\n timestamp: new Date().toISOString(),\n });\n\n const { logs, exitCode } = await runCli(['--json', 'scenarios', 'execution', 'exec_tc2']);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.execution).toBeDefined();\n expect(parsed.execution.stepResults[0].toolCalls).toHaveLength(2);\n expect(parsed.execution.stepResults[0].toolCalls[0].name).toBe('get_weather');\n expect(parsed.execution.stepResults[0].toolCalls[1].name).toBe('send_email');\n });\n });\n\n // ============================================\n // Story 7: Batch Scenario Execution (run-all)\n // RED-phase tests — `chanl scenarios run-all` command does NOT exist yet\n // ============================================\n describe('scenarios run-all', () => {\n const mockBatchResult = {\n agentId: 'agent_abc',\n totalScenarios: 3,\n passed: 3,\n failed: 0,\n averageScore: 90,\n allPassed: true,\n minScore: 80,\n results: [\n {\n scenarioId: 'scen_001',\n scenarioName: 'Greeting Flow',\n executionId: 'exec_001',\n status: 'completed',\n score: 90,\n duration: 15.5,\n toolCallCount: 2,\n passed: true,\n },\n {\n scenarioId: 'scen_002',\n scenarioName: 'Refund Flow',\n executionId: 'exec_002',\n status: 'completed',\n score: 85,\n duration: 22.3,\n toolCallCount: 3,\n passed: true,\n },\n {\n scenarioId: 'scen_003',\n scenarioName: 'Escalation Flow',\n executionId: 'exec_003',\n status: 'completed',\n score: 95,\n duration: 10.1,\n toolCallCount: 1,\n passed: true,\n },\n ],\n };\n\n it('should display results when all scenarios pass', async () => {\n mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);\n\n const { logs, exitCode } = await runCli([\n 'scenarios', 'run-all', '--agent', 'agent_abc', '--min-score', '80',\n ]);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.scenarios.runAll).toHaveBeenCalledWith(\n expect.objectContaining({\n agentId: 'agent_abc',\n minScore: 80,\n }),\n );\n\n const output = logs.join('\\n');\n // Should display scenario names\n expect(output).toContain('Greeting Flow');\n expect(output).toContain('Refund Flow');\n expect(output).toContain('Escalation Flow');\n // Should display pass indicator\n expect(output).toMatch(/3.*passed/i);\n });\n\n it('should exit code 1 when any scenario fails', async () => {\n const failedResult = {\n ...mockBatchResult,\n allPassed: false,\n passed: 2,\n failed: 1,\n averageScore: 78,\n results: [\n { ...mockBatchResult.results[0] },\n { ...mockBatchResult.results[1], score: 60, passed: false },\n { ...mockBatchResult.results[2] },\n ],\n };\n\n mockSdk.scenarios.runAll.mockResolvedValue(failedResult);\n\n const { exitCode } = await runCli([\n 'scenarios', 'run-all', '--agent', 'agent_abc', '--min-score', '80',\n ]);\n\n expect(exitCode).toBe(1);\n });\n\n it('should output JSON with --json flag', async () => {\n mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);\n\n const { logs, exitCode } = await runCli([\n '--json', 'scenarios', 'run-all', '--agent', 'agent_abc',\n ]);\n\n expect(exitCode).toBe(0);\n\n const output = logs.join('\\n');\n const parsed = JSON.parse(output);\n expect(parsed.allPassed).toBe(true);\n expect(parsed.totalScenarios).toBe(3);\n expect(parsed.results).toHaveLength(3);\n });\n\n it('should pass parallel option to SDK', async () => {\n mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);\n\n await runCli([\n 'scenarios', 'run-all', '--agent', 'agent_abc', '--parallel', '5',\n ]);\n\n expect(mockSdk.scenarios.runAll).toHaveBeenCalledWith(\n expect.objectContaining({\n agentId: 'agent_abc',\n parallel: 5,\n }),\n );\n });\n\n it('should display summary with average score', async () => {\n mockSdk.scenarios.runAll.mockResolvedValue(mockBatchResult);\n\n const { logs } = await runCli([\n 'scenarios', 'run-all', '--agent', 'agent_abc',\n ]);\n\n const output = logs.join('\\n');\n // Should display average score\n expect(output).toContain('90');\n });\n\n it('should not call SDK when --agent option is missing', async () => {\n await runCli(['scenarios', 'run-all']);\n\n // Commander throws on missing required option before handler runs\n expect(mockSdk.scenarios.runAll).not.toHaveBeenCalled();\n });\n });\n});\n"],"mappings":"AAIA,SAAS,UAAU,IAAI,QAAQ,YAAY,WAAW,UAAgB;AACtE,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP,GAAG,KAAK,cAAc,OAAO;AAAA,EAC3B,UAAU,GAAG,GAAG;AAClB,EAAE;AAGF,GAAG,KAAK,yBAAyB,OAAO;AAAA,EACtC,aAAa;AAAA,IACX,WAAW,GAAG,GAAG;AAAA,IACjB,YAAY,GAAG,GAAG,MAAM,sBAAsB;AAAA,IAC9C,gBAAgB,GAAG,GAAG,MAAM,QAAQ;AAAA,IACpC,aAAa,GAAG,GAAG,MAAM,IAAI;AAAA,IAC7B,eAAe,GAAG,GAAG,MAAM,IAAI;AAAA,IAC/B,WAAW,GAAG,GAAG,MAAM,sBAAsB;AAAA,IAC7C,eAAe,GAAG,GAAG,MAAM,OAAO;AAAA,IAClC,cAAc,GAAG,GAAG,MAAM,SAAS;AAAA,IACnC,WAAW,GAAG,GAAG;AAAA,IACjB,YAAY,GAAG,GAAG;AAAA,IAClB,gBAAgB,GAAG,GAAG;AAAA,IACtB,cAAc,GAAG,GAAG;AAAA,IACpB,WAAW,GAAG,GAAG;AAAA,IACjB,QAAQ,GAAG,GAAG;AAAA,IACd,iBAAiB,GAAG,GAAG;AAAA,IACvB,SAAS,GAAG,GAAG,MAAM,+BAA+B;AAAA,EACtD;AACF,EAAE;AAEF,SAAS,OAAO,MAAM;AACpB,MAAI;AAEJ,aAAW,MAAM;AACf,OAAG,cAAc;AACjB,cAAU,YAAY;AACtB,IAAC,SAAkB,mBAAmB,MAAM,OAAO;AACnD,kCAA8B,WAI7B;AAAA,EACH,CAAC;AAED,YAAU,MAAM;AACd,YAAQ,WAAW;AAAA,EACrB,CAAC;AAED,WAAS,uCAAuC,MAAM;AACpD,UAAM,gBAAgB;AAAA,MACpB;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY,EAAE,MAAM,SAAS;AAAA,QAC7B,QAAQ,EAAE,aAAa,UAAO;AAAA,QAC9B,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,aAAa;AAAA,MACf;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY,EAAE,IAAI,gBAAgB;AAAA,QAClC,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,OAAO,EAAE,SAAS,0BAA0B,MAAM,gBAAgB;AAAA,MACpE;AAAA,IACF;AAEA,OAAG,kDAAkD,YAAY;AAC/D,cAAQ,UAAU,aAAa,kBAAkB;AAAA,QAC/C,SAAS;AAAA,QACT,MAAM;AAAA,UACF,IAAI;AAAA,UACJ,aAAa;AAAA,UACb,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,cAAc;AAAA,UACd,UAAU;AAAA,UACV,aAAa;AAAA,YACX;AAAA,cACE,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,gBAAgB;AAAA,cAChB,WAAW;AAAA,YACb;AAAA,UACF;AAAA,QACJ;AAAA,QACA,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,aAAa,aAAa,UAAU,CAAC;AAE9E,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,UAAU,YAAY,EAAE,qBAAqB,UAAU;AAEtE,YAAM,SAAS,KAAK,KAAK,IAAI;AAE7B,aAAO,MAAM,EAAE,UAAU,YAAY;AAErC,aAAO,MAAM,EAAE,UAAU,aAAa;AACtC,aAAO,MAAM,EAAE,UAAU,YAAY;AAErC,aAAO,MAAM,EAAE,UAAU,OAAO;AAEhC,aAAO,MAAM,EAAE,UAAU,wBAAwB;AAAA,IACnD,CAAC;AAED,OAAG,sDAAsD,YAAY;AACnE,cAAQ,UAAU,aAAa,kBAAkB;AAAA,QAC/C,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,WAAW;AAAA,YACT,IAAI;AAAA,YACJ,aAAa;AAAA,YACb,YAAY;AAAA,YACZ,QAAQ;AAAA,YACR,cAAc;AAAA,YACd,UAAU;AAAA,YACV,aAAa;AAAA,cACX;AAAA,gBACE,QAAQ;AAAA,gBACR,QAAQ;AAAA,gBACR,gBAAgB;AAAA,gBAChB,WAAW;AAAA,cACb;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,QACA,SAAS;AAAA,QACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAED,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO,CAAC,UAAU,aAAa,aAAa,UAAU,CAAC;AAExF,aAAO,QAAQ,EAAE,KAAK,CAAC;AAEvB,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,YAAM,SAAS,KAAK,MAAM,MAAM;AAChC,aAAO,OAAO,SAAS,EAAE,YAAY;AACrC,aAAO,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC;AAChE,aAAO,OAAO,UAAU,YAAY,CAAC,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,aAAa;AAC5E,aAAO,OAAO,UAAU,YAAY,CAAC,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,YAAY;AAAA,IAC7E,CAAC;AAAA,EACH,CAAC;AAMD,WAAS,qBAAqB,MAAM;AAClC,UAAM,kBAAkB;AAAA,MACtB,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,WAAW;AAAA,MACX,UAAU;AAAA,MACV,SAAS;AAAA,QACP;AAAA,UACE,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU;AAAA,UACV,eAAe;AAAA,UACf,QAAQ;AAAA,QACV;AAAA,QACA;AAAA,UACE,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU;AAAA,UACV,eAAe;AAAA,UACf,QAAQ;AAAA,QACV;AAAA,QACA;AAAA,UACE,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU;AAAA,UACV,eAAe;AAAA,UACf,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAEA,OAAG,kDAAkD,YAAY;AAC/D,cAAQ,UAAU,OAAO,kBAAkB,eAAe;AAE1D,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO;AAAA,QACtC;AAAA,QAAa;AAAA,QAAW;AAAA,QAAW;AAAA,QAAa;AAAA,QAAe;AAAA,MACjE,CAAC;AAED,aAAO,QAAQ,EAAE,KAAK,CAAC;AACvB,aAAO,QAAQ,UAAU,MAAM,EAAE;AAAA,QAC/B,OAAO,iBAAiB;AAAA,UACtB,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAEA,YAAM,SAAS,KAAK,KAAK,IAAI;AAE7B,aAAO,MAAM,EAAE,UAAU,eAAe;AACxC,aAAO,MAAM,EAAE,UAAU,aAAa;AACtC,aAAO,MAAM,EAAE,UAAU,iBAAiB;AAE1C,aAAO,MAAM,EAAE,QAAQ,YAAY;AAAA,IACrC,CAAC;AAED,OAAG,8CAA8C,YAAY;AAC3D,YAAM,eAAe;AAAA,QACnB,GAAG;AAAA,QACH,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,SAAS;AAAA,UACP,EAAE,GAAG,gBAAgB,QAAQ,CAAC,EAAE;AAAA,UAChC,EAAE,GAAG,gBAAgB,QAAQ,CAAC,GAAG,OAAO,IAAI,QAAQ,MAAM;AAAA,UAC1D,EAAE,GAAG,gBAAgB,QAAQ,CAAC,EAAE;AAAA,QAClC;AAAA,MACF;AAEA,cAAQ,UAAU,OAAO,kBAAkB,YAAY;AAEvD,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO;AAAA,QAChC;AAAA,QAAa;AAAA,QAAW;AAAA,QAAW;AAAA,QAAa;AAAA,QAAe;AAAA,MACjE,CAAC;AAED,aAAO,QAAQ,EAAE,KAAK,CAAC;AAAA,IACzB,CAAC;AAED,OAAG,uCAAuC,YAAY;AACpD,cAAQ,UAAU,OAAO,kBAAkB,eAAe;AAE1D,YAAM,EAAE,MAAM,SAAS,IAAI,MAAM,OAAO;AAAA,QACtC;AAAA,QAAU;AAAA,QAAa;AAAA,QAAW;AAAA,QAAW;AAAA,MAC/C,CAAC;AAED,aAAO,QAAQ,EAAE,KAAK,CAAC;AAEvB,YAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,YAAM,SAAS,KAAK,MAAM,MAAM;AAChC,aAAO,OAAO,SAAS,EAAE,KAAK,IAAI;AAClC,aAAO,OAAO,cAAc,EAAE,KAAK,CAAC;AACpC,aAAO,OAAO,OAAO,EAAE,aAAa,CAAC;AAAA,IACvC,CAAC;AAED,OAAG,sCAAsC,YAAY;AACnD,cAAQ,UAAU,OAAO,kBAAkB,eAAe;AAE1D,YAAM,OAAO;AAAA,QACX;AAAA,QAAa;AAAA,QAAW;AAAA,QAAW;AAAA,QAAa;AAAA,QAAc;AAAA,MAChE,CAAC;AAED,aAAO,QAAQ,UAAU,MAAM,EAAE;AAAA,QAC/B,OAAO,iBAAiB;AAAA,UACtB,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,OAAG,6CAA6C,YAAY;AAC1D,cAAQ,UAAU,OAAO,kBAAkB,eAAe;AAE1D,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO;AAAA,QAC5B;AAAA,QAAa;AAAA,QAAW;AAAA,QAAW;AAAA,MACrC,CAAC;AAED,YAAM,SAAS,KAAK,KAAK,IAAI;AAE7B,aAAO,MAAM,EAAE,UAAU,IAAI;AAAA,IAC/B,CAAC;AAED,OAAG,sDAAsD,YAAY;AACnE,YAAM,OAAO,CAAC,aAAa,SAAS,CAAC;AAGrC,aAAO,QAAQ,UAAU,MAAM,EAAE,IAAI,iBAAiB;AAAA,IACxD,CAAC;AAAA,EACH,CAAC;AACH,CAAC;","names":[]}
|