@autobe/agent 0.8.0 → 0.9.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/lib/AutoBeAgent.d.ts +183 -12
- package/lib/AutoBeAgent.js +247 -65
- package/lib/AutoBeAgent.js.map +1 -1
- package/lib/constants/AutoBeSystemPromptConstant.d.ts +4 -3
- package/lib/constants/AutoBeSystemPromptConstant.js.map +1 -1
- package/lib/context/AutoBeContext.d.ts +2 -2
- package/lib/factory/index.d.ts +0 -1
- package/lib/factory/index.js +0 -1
- package/lib/factory/index.js.map +1 -1
- package/lib/index.mjs +1195 -834
- package/lib/index.mjs.map +1 -1
- package/lib/orchestrate/analyze/AutoBeAnalyzeAgent.js +1 -1
- package/lib/orchestrate/analyze/AutoBeAnalyzeAgent.js.map +1 -1
- package/lib/orchestrate/interface/orchestrateInterface.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterface.js.map +1 -1
- package/lib/orchestrate/prisma/orchestratePrisma.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrisma.js.map +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaCorrect.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaCorrect.js.map +1 -1
- package/lib/orchestrate/test/compileTestScenario.d.ts +5 -0
- package/lib/orchestrate/test/compileTestScenario.js +56 -0
- package/lib/orchestrate/test/compileTestScenario.js.map +1 -0
- package/lib/orchestrate/test/filterTestFileName.d.ts +1 -0
- package/lib/orchestrate/test/filterTestFileName.js +13 -0
- package/lib/orchestrate/test/filterTestFileName.js.map +1 -0
- package/lib/orchestrate/test/orchestrateTest.js +10 -11
- package/lib/orchestrate/test/orchestrateTest.js.map +1 -1
- package/lib/orchestrate/test/orchestrateTestCorrect.d.ts +2 -2
- package/lib/orchestrate/test/orchestrateTestCorrect.js +91 -73
- package/lib/orchestrate/test/orchestrateTestCorrect.js.map +1 -1
- package/lib/orchestrate/test/orchestrateTestScenario.d.ts +2 -2
- package/lib/orchestrate/test/orchestrateTestScenario.js +616 -237
- package/lib/orchestrate/test/orchestrateTestScenario.js.map +1 -1
- package/lib/orchestrate/test/orchestrateTestWrite.d.ts +4 -0
- package/lib/orchestrate/test/{orchestrateTestProgress.js → orchestrateTestWrite.js} +37 -51
- package/lib/orchestrate/test/orchestrateTestWrite.js.map +1 -0
- package/lib/orchestrate/test/structures/IAutoBeTestScenarioApplication.d.ts +123 -0
- package/lib/orchestrate/test/structures/IAutoBeTestScenarioApplication.js +3 -0
- package/lib/orchestrate/test/structures/IAutoBeTestScenarioApplication.js.map +1 -0
- package/lib/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.d.ts +5 -0
- package/lib/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.js +3 -0
- package/lib/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.js.map +1 -0
- package/lib/orchestrate/test/transformTestCorrectHistories.d.ts +2 -1
- package/lib/orchestrate/test/transformTestCorrectHistories.js +4 -4
- package/lib/orchestrate/test/transformTestCorrectHistories.js.map +1 -1
- package/lib/orchestrate/test/transformTestScenarioHistories.d.ts +1 -2
- package/lib/orchestrate/test/transformTestScenarioHistories.js +1 -77
- package/lib/orchestrate/test/transformTestScenarioHistories.js.map +1 -1
- package/lib/orchestrate/test/transformTestWriteHistories.d.ts +7 -0
- package/lib/orchestrate/test/transformTestWriteHistories.js +47 -0
- package/lib/orchestrate/test/transformTestWriteHistories.js.map +1 -0
- package/lib/structures/IAutoBeConfig.d.ts +48 -10
- package/lib/structures/IAutoBeProps.d.ts +87 -0
- package/lib/structures/IAutoBeVendor.d.ts +64 -22
- package/lib/utils/backoffRetry.d.ts +7 -0
- package/lib/utils/backoffRetry.js +73 -0
- package/lib/utils/backoffRetry.js.map +1 -0
- package/lib/utils/types/BackoffOptions.d.ts +12 -0
- package/lib/utils/types/BackoffOptions.js +3 -0
- package/lib/utils/types/BackoffOptions.js.map +1 -0
- package/package.json +4 -4
- package/src/AutoBeAgent.ts +251 -51
- package/src/constants/AutoBeSystemPromptConstant.ts +4 -3
- package/src/context/AutoBeContext.ts +7 -2
- package/src/factory/index.ts +0 -1
- package/src/orchestrate/analyze/AutoBeAnalyzeAgent.ts +1 -1
- package/src/orchestrate/interface/orchestrateInterface.ts +1 -1
- package/src/orchestrate/prisma/orchestratePrisma.ts +1 -0
- package/src/orchestrate/prisma/orchestratePrismaCorrect.ts +4 -2
- package/src/orchestrate/test/compileTestScenario.ts +63 -0
- package/src/orchestrate/test/filterTestFileName.ts +9 -0
- package/src/orchestrate/test/orchestrateTest.ts +11 -17
- package/src/orchestrate/test/orchestrateTestCorrect.ts +152 -100
- package/src/orchestrate/test/orchestrateTestScenario.ts +195 -151
- package/src/orchestrate/test/{orchestrateTestProgress.ts → orchestrateTestWrite.ts} +29 -44
- package/src/orchestrate/test/structures/IAutoBeTestScenarioApplication.ts +132 -0
- package/src/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.ts +5 -0
- package/src/orchestrate/test/transformTestCorrectHistories.ts +4 -4
- package/src/orchestrate/test/transformTestScenarioHistories.ts +0 -79
- package/src/orchestrate/test/transformTestWriteHistories.ts +53 -0
- package/src/structures/IAutoBeConfig.ts +48 -10
- package/src/structures/IAutoBeProps.ts +91 -0
- package/src/structures/IAutoBeVendor.ts +64 -22
- package/src/utils/backoffRetry.ts +84 -0
- package/src/utils/types/BackoffOptions.ts +15 -0
- package/lib/orchestrate/test/orchestrateTestProgress.d.ts +0 -4
- package/lib/orchestrate/test/orchestrateTestProgress.js.map +0 -1
- package/lib/orchestrate/test/transformTestProgressHistories.d.ts +0 -2
- package/lib/orchestrate/test/transformTestProgressHistories.js +0 -47
- package/lib/orchestrate/test/transformTestProgressHistories.js.map +0 -1
- package/src/orchestrate/test/transformTestProgressHistories.ts +0 -51
|
@@ -1,198 +1,256 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
IAgenticaController,
|
|
3
|
+
IAgenticaHistoryJson,
|
|
4
|
+
MicroAgentica,
|
|
5
|
+
} from "@agentica/core";
|
|
6
|
+
import {
|
|
7
|
+
AutoBeOpenApi,
|
|
8
|
+
AutoBeTestScenario,
|
|
9
|
+
AutoBeTestScenarioEvent,
|
|
10
|
+
} from "@autobe/interface";
|
|
11
|
+
import { ILlmApplication, ILlmSchema, IValidation } from "@samchon/openapi";
|
|
12
|
+
import { IPointer } from "tstl";
|
|
6
13
|
import typia from "typia";
|
|
14
|
+
import { v4 } from "uuid";
|
|
7
15
|
|
|
16
|
+
import { AutoBeSystemPromptConstant } from "../../constants/AutoBeSystemPromptConstant";
|
|
8
17
|
import { AutoBeContext } from "../../context/AutoBeContext";
|
|
9
18
|
import { assertSchemaModel } from "../../context/assertSchemaModel";
|
|
10
19
|
import { divideArray } from "../../utils/divideArray";
|
|
11
20
|
import { enforceToolCall } from "../../utils/enforceToolCall";
|
|
12
|
-
import {
|
|
13
|
-
import { transformTestScenarioHistories } from "./transformTestScenarioHistories";
|
|
21
|
+
import { IAutoBeTestScenarioApplication } from "./structures/IAutoBeTestScenarioApplication";
|
|
14
22
|
|
|
15
23
|
export async function orchestrateTestScenario<Model extends ILlmSchema.Model>(
|
|
16
24
|
ctx: AutoBeContext<Model>,
|
|
17
|
-
capacity: number = 4,
|
|
18
25
|
): Promise<AutoBeTestScenarioEvent> {
|
|
19
|
-
const files = Object.entries(ctx.state().interface?.files ?? {})
|
|
20
|
-
.filter(([filename]) => {
|
|
21
|
-
return filename.startsWith("test/features/api/");
|
|
22
|
-
})
|
|
23
|
-
.reduce<Record<string, string>>((acc, [filename, content]) => {
|
|
24
|
-
return Object.assign(acc, { [filename]: content });
|
|
25
|
-
}, {});
|
|
26
|
-
|
|
27
26
|
const operations = ctx.state().interface?.document.operations ?? [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
summary: it.summary,
|
|
34
|
-
description: it.description,
|
|
35
|
-
parameters: it.parameters,
|
|
36
|
-
requestBody: it.requestBody,
|
|
37
|
-
responseBody: it.responseBody,
|
|
38
|
-
};
|
|
39
|
-
});
|
|
27
|
+
if (operations.length === 0) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"Cannot write test scenarios because these are no operations.",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
40
32
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
capacity,
|
|
44
|
-
});
|
|
45
|
-
const start: Date = new Date();
|
|
46
|
-
|
|
47
|
-
let completed: number = 0;
|
|
48
|
-
|
|
49
|
-
const scenarios: AutoBeTest.IScenario[][] = await Promise.all(
|
|
50
|
-
matrix.map(async (e) => {
|
|
51
|
-
const rows: AutoBeTest.IScenario[] = await divideAndConquer(
|
|
52
|
-
ctx,
|
|
53
|
-
e,
|
|
54
|
-
endpoints,
|
|
55
|
-
files,
|
|
56
|
-
3,
|
|
57
|
-
(count) => {
|
|
58
|
-
completed += count;
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
ctx.dispatch({
|
|
62
|
-
type: "testScenario",
|
|
63
|
-
scenarios: rows,
|
|
64
|
-
total: rows.flatMap((el) => el.scenarios).length,
|
|
65
|
-
step: ctx.state().test?.step ?? 0,
|
|
66
|
-
completed,
|
|
67
|
-
created_at: start.toISOString(),
|
|
68
|
-
});
|
|
69
|
-
return rows;
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
33
|
+
const exclude: IAutoBeTestScenarioApplication.IScenarioGroup[] = [];
|
|
34
|
+
let include: AutoBeOpenApi.IOperation[] = Array.from(operations);
|
|
72
35
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
scenarios: scenarios.flat(),
|
|
76
|
-
total: scenarios.flat().flatMap((el) => el.scenarios).length,
|
|
77
|
-
step: ctx.state().test?.step ?? 0,
|
|
78
|
-
completed,
|
|
79
|
-
created_at: start.toISOString(),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
36
|
+
do {
|
|
37
|
+
const matrix = divideArray({ array: include, capacity: 30 });
|
|
82
38
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
OpenApiEndpointComparator.equals,
|
|
95
|
-
);
|
|
96
|
-
const scenarios: HashMap<AutoBeOpenApi.IEndpoint, AutoBeTest.Scenario[]> =
|
|
97
|
-
new HashMap(
|
|
98
|
-
OpenApiEndpointComparator.hashCode,
|
|
99
|
-
OpenApiEndpointComparator.equals,
|
|
100
|
-
);
|
|
101
|
-
for (let i: number = 0; i < retry; ++i) {
|
|
102
|
-
if (remained.empty() === true || scenarios.size() >= endpoints.length)
|
|
103
|
-
break;
|
|
104
|
-
const before: number = scenarios.size();
|
|
105
|
-
const newbie: AutoBeTest.IScenario[] = await process(
|
|
106
|
-
ctx,
|
|
107
|
-
Array.from(remained),
|
|
108
|
-
allEndpoints,
|
|
109
|
-
files,
|
|
39
|
+
await Promise.all(
|
|
40
|
+
matrix.map(async (_include) => {
|
|
41
|
+
exclude.push(
|
|
42
|
+
...(await execute(
|
|
43
|
+
ctx,
|
|
44
|
+
operations,
|
|
45
|
+
_include,
|
|
46
|
+
exclude.map((x) => x.endpoint),
|
|
47
|
+
)),
|
|
48
|
+
);
|
|
49
|
+
}),
|
|
110
50
|
);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
51
|
+
|
|
52
|
+
include = include.filter((op) => {
|
|
53
|
+
if (
|
|
54
|
+
exclude.some(
|
|
55
|
+
(pg) =>
|
|
56
|
+
pg.endpoint.method === op.method && pg.endpoint.path === op.path,
|
|
57
|
+
)
|
|
58
|
+
) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
} while (include.length > 0);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
type: "testScenario",
|
|
67
|
+
step: ctx.state().analyze?.step ?? 0,
|
|
68
|
+
scenarios: exclude.flatMap((pg) => {
|
|
69
|
+
return pg.scenarios.map((plan) => {
|
|
70
|
+
return {
|
|
71
|
+
endpoint: pg.endpoint,
|
|
72
|
+
draft: plan.draft,
|
|
73
|
+
functionName: plan.functionName,
|
|
74
|
+
dependencies: plan.dependsOn,
|
|
75
|
+
} satisfies AutoBeTestScenario;
|
|
76
|
+
});
|
|
77
|
+
}),
|
|
78
|
+
created_at: new Date().toISOString(),
|
|
79
|
+
} as AutoBeTestScenarioEvent;
|
|
121
80
|
}
|
|
122
81
|
|
|
123
|
-
async
|
|
82
|
+
const execute = async <Model extends ILlmSchema.Model>(
|
|
124
83
|
ctx: AutoBeContext<Model>,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
const pointer: IPointer<
|
|
130
|
-
value:
|
|
84
|
+
ops: AutoBeOpenApi.IOperation[],
|
|
85
|
+
include: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
|
|
86
|
+
exclude: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
|
|
87
|
+
) => {
|
|
88
|
+
const pointer: IPointer<IAutoBeTestScenarioApplication.IScenarioGroup[]> = {
|
|
89
|
+
value: [],
|
|
131
90
|
};
|
|
132
|
-
|
|
133
|
-
const agentica = new MicroAgentica({
|
|
91
|
+
const agentica: MicroAgentica<Model> = new MicroAgentica({
|
|
134
92
|
model: ctx.model,
|
|
135
93
|
vendor: ctx.vendor,
|
|
136
94
|
config: {
|
|
137
|
-
...(ctx.config ?? {
|
|
138
|
-
|
|
139
|
-
describe:
|
|
140
|
-
return "Answer only 'completion' or 'failure'.";
|
|
141
|
-
},
|
|
95
|
+
...(ctx.config ?? {}),
|
|
96
|
+
executor: {
|
|
97
|
+
describe: null,
|
|
142
98
|
},
|
|
143
99
|
},
|
|
144
100
|
tokenUsage: ctx.usage(),
|
|
145
|
-
histories:
|
|
146
|
-
...transformTestScenarioHistories(ctx.state(), allEndpoints, files),
|
|
147
|
-
],
|
|
101
|
+
histories: createHistoryProperties(ops, include, exclude),
|
|
148
102
|
controllers: [
|
|
149
103
|
createApplication({
|
|
150
104
|
model: ctx.model,
|
|
151
105
|
build: (next) => {
|
|
152
106
|
pointer.value ??= [];
|
|
153
|
-
pointer.value.push(...next.
|
|
107
|
+
pointer.value.push(...next.scenarioGroups);
|
|
154
108
|
},
|
|
155
109
|
}),
|
|
156
110
|
],
|
|
157
111
|
});
|
|
158
112
|
enforceToolCall(agentica);
|
|
159
113
|
|
|
160
|
-
await agentica.conversate(
|
|
161
|
-
|
|
162
|
-
|
|
114
|
+
await agentica.conversate(`create test scenarios.`);
|
|
115
|
+
if (pointer.value.length === 0) {
|
|
116
|
+
throw new Error("Failed to create test plans.");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return pointer.value;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const createHistoryProperties = (
|
|
123
|
+
operations: AutoBeOpenApi.IOperation[],
|
|
124
|
+
include: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
|
|
125
|
+
exclude: Pick<AutoBeOpenApi.IOperation, "method" | "path">[],
|
|
126
|
+
) => [
|
|
127
|
+
{
|
|
128
|
+
id: v4(),
|
|
129
|
+
created_at: new Date().toISOString(),
|
|
130
|
+
type: "systemMessage",
|
|
131
|
+
text: AutoBeSystemPromptConstant.TEST_SCENARIO,
|
|
132
|
+
} satisfies IAgenticaHistoryJson.ISystemMessage,
|
|
133
|
+
{
|
|
134
|
+
id: v4(),
|
|
135
|
+
created_at: new Date().toISOString(),
|
|
136
|
+
type: "systemMessage",
|
|
137
|
+
text: [
|
|
138
|
+
"Below are the full operations. Please refer to this.",
|
|
139
|
+
"Your role is to draft all test cases for each given Operation.",
|
|
140
|
+
"It is also permissible to write multiple test codes on a single endpoint.",
|
|
141
|
+
"However, rather than meaningless tests, business logic tests should be written and an E2E test situation should be assumed.",
|
|
163
142
|
"",
|
|
164
143
|
"```json",
|
|
165
|
-
JSON.stringify(
|
|
144
|
+
JSON.stringify(
|
|
145
|
+
operations.map((el) => ({
|
|
146
|
+
path: el.path,
|
|
147
|
+
method: el.method,
|
|
148
|
+
summary: el.summary,
|
|
149
|
+
})),
|
|
150
|
+
),
|
|
166
151
|
"```",
|
|
167
152
|
].join("\n"),
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
153
|
+
} satisfies IAgenticaHistoryJson.ISystemMessage,
|
|
154
|
+
{
|
|
155
|
+
id: v4(),
|
|
156
|
+
created_at: new Date().toISOString(),
|
|
157
|
+
type: "systemMessage",
|
|
158
|
+
text: [
|
|
159
|
+
"# Included in Test Plan",
|
|
160
|
+
include
|
|
161
|
+
.map((el) => `- ${el.method.toUpperCase()}: ${el.path}`)
|
|
162
|
+
.join("\n"),
|
|
163
|
+
"",
|
|
164
|
+
"# Excluded from Test Plan",
|
|
165
|
+
"These are the endpoints that have already been used in test codes generated as part of a plan group.",
|
|
166
|
+
"These endpoints do not need to be tested again.",
|
|
167
|
+
"However, it is allowed to reference or depend on these endpoints when writing test codes for other purposes.",
|
|
168
|
+
exclude
|
|
169
|
+
.map((el) => `- ${el.method.toUpperCase()}: ${el.path}`)
|
|
170
|
+
.join("\n"),
|
|
171
|
+
].join("\n"),
|
|
172
|
+
} satisfies IAgenticaHistoryJson.ISystemMessage,
|
|
173
|
+
];
|
|
172
174
|
|
|
173
175
|
function createApplication<Model extends ILlmSchema.Model>(props: {
|
|
174
176
|
model: Model;
|
|
175
|
-
build: (next:
|
|
177
|
+
build: (next: IAutoBeTestScenarioApplication.IProps) => void;
|
|
176
178
|
}): IAgenticaController.IClass<Model> {
|
|
177
179
|
assertSchemaModel(props.model);
|
|
178
180
|
|
|
179
181
|
const application: ILlmApplication<Model> = collection[
|
|
180
182
|
props.model
|
|
181
183
|
] as unknown as ILlmApplication<Model>;
|
|
184
|
+
|
|
185
|
+
application.functions[0].validate = (next: unknown): IValidation => {
|
|
186
|
+
const result: IValidation<IAutoBeTestScenarioApplication.IProps> =
|
|
187
|
+
typia.validate<IAutoBeTestScenarioApplication.IProps>(next);
|
|
188
|
+
if (result.success === false) return result;
|
|
189
|
+
|
|
190
|
+
const errors: IValidation.IError[] = [];
|
|
191
|
+
result.data.scenarioGroups.forEach((pg, i, arr) => {
|
|
192
|
+
arr.forEach((target, j) => {
|
|
193
|
+
if (
|
|
194
|
+
i !== j &&
|
|
195
|
+
target.endpoint.method === pg.endpoint.method &&
|
|
196
|
+
target.endpoint.path === pg.endpoint.path
|
|
197
|
+
) {
|
|
198
|
+
if (
|
|
199
|
+
!errors.some(
|
|
200
|
+
(el) =>
|
|
201
|
+
el.path !== `planGroups[${j}].path` &&
|
|
202
|
+
el.value !== target.endpoint.path,
|
|
203
|
+
)
|
|
204
|
+
) {
|
|
205
|
+
errors.push({
|
|
206
|
+
path: `planGroups[${j}].path`,
|
|
207
|
+
expected: `planGroup's {method + path} cannot duplicated.`,
|
|
208
|
+
value: target.endpoint.path,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (
|
|
213
|
+
!errors.some(
|
|
214
|
+
(el) =>
|
|
215
|
+
el.path !== `planGroups[${j}].method` &&
|
|
216
|
+
el.value !== target.endpoint.method,
|
|
217
|
+
)
|
|
218
|
+
) {
|
|
219
|
+
errors.push({
|
|
220
|
+
path: `planGroups[${j}].method`,
|
|
221
|
+
expected: `planGroup's {method + path} cannot duplicated.`,
|
|
222
|
+
value: target.endpoint.method,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (errors.length !== 0) {
|
|
230
|
+
console.log(JSON.stringify(errors, null, 2), "errors");
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
errors,
|
|
234
|
+
data: next,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result;
|
|
239
|
+
};
|
|
182
240
|
return {
|
|
183
241
|
protocol: "class",
|
|
184
|
-
name: "Make
|
|
242
|
+
name: "Make test plans",
|
|
185
243
|
application,
|
|
186
244
|
execute: {
|
|
187
245
|
makeScenario: (next) => {
|
|
188
246
|
props.build(next);
|
|
189
247
|
},
|
|
190
|
-
} satisfies
|
|
248
|
+
} satisfies IAutoBeTestScenarioApplication,
|
|
191
249
|
};
|
|
192
250
|
}
|
|
193
251
|
|
|
194
252
|
const claude = typia.llm.application<
|
|
195
|
-
|
|
253
|
+
IAutoBeTestScenarioApplication,
|
|
196
254
|
"claude",
|
|
197
255
|
{
|
|
198
256
|
reference: true;
|
|
@@ -200,7 +258,7 @@ const claude = typia.llm.application<
|
|
|
200
258
|
>();
|
|
201
259
|
const collection = {
|
|
202
260
|
chatgpt: typia.llm.application<
|
|
203
|
-
|
|
261
|
+
IAutoBeTestScenarioApplication,
|
|
204
262
|
"chatgpt",
|
|
205
263
|
{ reference: true }
|
|
206
264
|
>(),
|
|
@@ -208,19 +266,5 @@ const collection = {
|
|
|
208
266
|
llama: claude,
|
|
209
267
|
deepseek: claude,
|
|
210
268
|
"3.1": claude,
|
|
211
|
-
"3.0": typia.llm.application<
|
|
269
|
+
"3.0": typia.llm.application<IAutoBeTestScenarioApplication, "3.0">(),
|
|
212
270
|
};
|
|
213
|
-
|
|
214
|
-
interface IApplication {
|
|
215
|
-
/**
|
|
216
|
-
* Make user scenarios for the given endpoints.
|
|
217
|
-
*
|
|
218
|
-
* @param props Properties containing the endpoints and user scenarios.
|
|
219
|
-
*/
|
|
220
|
-
makeScenario(props: IMakeScenarioProps): void;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
interface IMakeScenarioProps {
|
|
224
|
-
/** Array of user scenarios. */
|
|
225
|
-
scenarios: AutoBeTest.IScenario[];
|
|
226
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IAgenticaController, MicroAgentica } from "@agentica/core";
|
|
2
|
-
import {
|
|
2
|
+
import { AutoBeTestScenario, AutoBeTestWriteEvent } from "@autobe/interface";
|
|
3
3
|
import { ILlmApplication, ILlmSchema } from "@samchon/openapi";
|
|
4
4
|
import { IPointer } from "tstl";
|
|
5
5
|
import typia from "typia";
|
|
@@ -7,28 +7,29 @@ import typia from "typia";
|
|
|
7
7
|
import { AutoBeContext } from "../../context/AutoBeContext";
|
|
8
8
|
import { assertSchemaModel } from "../../context/assertSchemaModel";
|
|
9
9
|
import { enforceToolCall } from "../../utils/enforceToolCall";
|
|
10
|
-
import {
|
|
10
|
+
import { compileTestScenario } from "./compileTestScenario";
|
|
11
|
+
import { IAutoBeTestScenarioArtifacts } from "./structures/IAutoBeTestScenarioArtifacts";
|
|
12
|
+
import { transformTestWriteHistories } from "./transformTestWriteHistories";
|
|
11
13
|
|
|
12
|
-
export async function
|
|
14
|
+
export async function orchestrateTestWrite<Model extends ILlmSchema.Model>(
|
|
13
15
|
ctx: AutoBeContext<Model>,
|
|
14
|
-
scenarios:
|
|
15
|
-
): Promise<
|
|
16
|
+
scenarios: AutoBeTestScenario[],
|
|
17
|
+
): Promise<AutoBeTestWriteEvent[]> {
|
|
16
18
|
const start: Date = new Date();
|
|
17
19
|
let complete: number = 0;
|
|
18
20
|
|
|
19
|
-
const events:
|
|
21
|
+
const events: AutoBeTestWriteEvent[] = await Promise.all(
|
|
20
22
|
/**
|
|
21
|
-
* Generate test code for each scenario. Maps through
|
|
22
|
-
*
|
|
23
|
-
*
|
|
23
|
+
* Generate test code for each scenario. Maps through plans array to create
|
|
24
|
+
* individual test code implementations. Each scenario is processed to
|
|
25
|
+
* generate corresponding test code and progress events.
|
|
24
26
|
*/
|
|
25
27
|
scenarios.map(async (scenario) => {
|
|
26
28
|
const code: ICreateTestCodeProps = await process(ctx, scenario);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
type: "testProgress",
|
|
29
|
+
const event: AutoBeTestWriteEvent = {
|
|
30
|
+
type: "testWrite",
|
|
30
31
|
created_at: start.toISOString(),
|
|
31
|
-
filename:
|
|
32
|
+
filename: `test/features/api/${code.domain}/${scenario.functionName}.ts`,
|
|
32
33
|
content: code.content,
|
|
33
34
|
completed: ++complete,
|
|
34
35
|
total: scenarios.length,
|
|
@@ -54,27 +55,15 @@ export async function orchestrateTestProgress<Model extends ILlmSchema.Model>(
|
|
|
54
55
|
*/
|
|
55
56
|
async function process<Model extends ILlmSchema.Model>(
|
|
56
57
|
ctx: AutoBeContext<Model>,
|
|
57
|
-
scenario:
|
|
58
|
+
scenario: AutoBeTestScenario,
|
|
58
59
|
): Promise<ICreateTestCodeProps> {
|
|
59
60
|
const pointer: IPointer<ICreateTestCodeProps | null> = {
|
|
60
61
|
value: null,
|
|
61
62
|
};
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
67
|
-
.reduce<Record<string, string>>((acc, [filename, content]) => {
|
|
68
|
-
return Object.assign(acc, { [filename]: content });
|
|
69
|
-
}, {});
|
|
70
|
-
|
|
71
|
-
const dtoFiles = Object.entries(ctx.state().interface?.files ?? {})
|
|
72
|
-
.filter(([filename]) => {
|
|
73
|
-
return filename.startsWith("src/api/structures/");
|
|
74
|
-
})
|
|
75
|
-
.reduce<Record<string, string>>((acc, [filename, content]) => {
|
|
76
|
-
return Object.assign(acc, { [filename]: content });
|
|
77
|
-
}, {});
|
|
63
|
+
const artifacts: IAutoBeTestScenarioArtifacts = await compileTestScenario(
|
|
64
|
+
ctx,
|
|
65
|
+
scenario,
|
|
66
|
+
);
|
|
78
67
|
|
|
79
68
|
const agentica = new MicroAgentica({
|
|
80
69
|
model: ctx.model,
|
|
@@ -82,7 +71,10 @@ async function process<Model extends ILlmSchema.Model>(
|
|
|
82
71
|
config: {
|
|
83
72
|
...(ctx.config ?? {}),
|
|
84
73
|
},
|
|
85
|
-
histories:
|
|
74
|
+
histories: transformTestWriteHistories({
|
|
75
|
+
scenario,
|
|
76
|
+
artifacts,
|
|
77
|
+
}),
|
|
86
78
|
controllers: [
|
|
87
79
|
createApplication({
|
|
88
80
|
model: ctx.model,
|
|
@@ -91,18 +83,11 @@ async function process<Model extends ILlmSchema.Model>(
|
|
|
91
83
|
},
|
|
92
84
|
}),
|
|
93
85
|
],
|
|
86
|
+
tokenUsage: ctx.usage(),
|
|
94
87
|
});
|
|
95
88
|
enforceToolCall(agentica);
|
|
96
89
|
|
|
97
|
-
await agentica.conversate(
|
|
98
|
-
[
|
|
99
|
-
"Create test code for below scenario:",
|
|
100
|
-
"",
|
|
101
|
-
"```json",
|
|
102
|
-
JSON.stringify(scenario, null, 2),
|
|
103
|
-
"```",
|
|
104
|
-
].join("\n"),
|
|
105
|
-
);
|
|
90
|
+
await agentica.conversate("Create e2e test functions.");
|
|
106
91
|
if (pointer.value === null) throw new Error("Failed to create test code.");
|
|
107
92
|
return pointer.value;
|
|
108
93
|
}
|
|
@@ -178,7 +163,7 @@ interface ICreateTestCodeProps {
|
|
|
178
163
|
* #### Execution Strategy
|
|
179
164
|
*
|
|
180
165
|
* - Outline step-by-step test execution flow
|
|
181
|
-
* - Plan error handling and exception
|
|
166
|
+
* - Plan error handling and exception plans
|
|
182
167
|
* - Define cleanup and teardown procedures
|
|
183
168
|
* - Identify dependencies and prerequisites
|
|
184
169
|
*
|
|
@@ -188,12 +173,12 @@ interface ICreateTestCodeProps {
|
|
|
188
173
|
* 1. Prepare valid article data with required fields
|
|
189
174
|
* 2. Execute POST request to create article
|
|
190
175
|
* 3. Validate response structure and data integrity
|
|
191
|
-
* 4. Test error
|
|
176
|
+
* 4. Test error plans (missing fields, invalid data)
|
|
192
177
|
* 5. Verify database state changes
|
|
193
|
-
* 6. Reconsider the
|
|
178
|
+
* 6. Reconsider the scenario if it doesn't follow the Test Generation
|
|
194
179
|
* Guildelines.
|
|
195
180
|
*/
|
|
196
|
-
|
|
181
|
+
scenario: string;
|
|
197
182
|
|
|
198
183
|
/**
|
|
199
184
|
* Functional domain classification for test organization.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { AutoBeOpenApi } from "@autobe/interface";
|
|
2
|
+
|
|
3
|
+
export interface IAutoBeTestScenarioApplication {
|
|
4
|
+
/**
|
|
5
|
+
* Make test scenarios for the given endpoints.
|
|
6
|
+
*
|
|
7
|
+
* @param props Properties containing the endpoints and test scenarios.
|
|
8
|
+
*/
|
|
9
|
+
makeScenario(props: IAutoBeTestScenarioApplication.IProps): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export namespace IAutoBeTestScenarioApplication {
|
|
13
|
+
export interface IProps {
|
|
14
|
+
/** Array of test scenario groups. */
|
|
15
|
+
scenarioGroups: IAutoBeTestScenarioApplication.IScenarioGroup[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IScenarioGroup {
|
|
19
|
+
/** Target API endpoint to test. */
|
|
20
|
+
endpoint: AutoBeOpenApi.IEndpoint;
|
|
21
|
+
|
|
22
|
+
/** Array of test scenarios. */
|
|
23
|
+
scenarios: IScenario[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Represents a test scenario for a single API operation.
|
|
28
|
+
*
|
|
29
|
+
* This interface extends `AutoBeOpenApi.IEndpoint`, inheriting its HTTP
|
|
30
|
+
* method and path information, and adds two key properties:
|
|
31
|
+
*
|
|
32
|
+
* - `draft`: A free-form, human-readable test scenario description for the API
|
|
33
|
+
* endpoint.
|
|
34
|
+
* - `dependsOn`: A list of other API endpoints that must be invoked beforehand
|
|
35
|
+
* in order to prepare the context for this test. Each dependency includes
|
|
36
|
+
* the purpose of the dependency.
|
|
37
|
+
*
|
|
38
|
+
* This structure is intended to help organize test specifications for complex
|
|
39
|
+
* workflows and ensure that all prerequisites are explicitly declared.
|
|
40
|
+
*/
|
|
41
|
+
export interface IScenario {
|
|
42
|
+
/**
|
|
43
|
+
* A detailed natural language description of how this API endpoint should
|
|
44
|
+
* be tested. This should include both successful and failure scenarios,
|
|
45
|
+
* business rule validations, edge cases, and any sequence of steps
|
|
46
|
+
* necessary to perform the test. A subsequent agent will use this draft to
|
|
47
|
+
* generate multiple test scenarios.
|
|
48
|
+
*/
|
|
49
|
+
draft: string;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Descriptive function name derived from the user scenario.
|
|
53
|
+
*
|
|
54
|
+
* The function name serves as a concise, technical identifier that clearly
|
|
55
|
+
* represents the specific user scenario being described. It should be
|
|
56
|
+
* immediately understandable and directly correspond to the user situation
|
|
57
|
+
* without requiring additional context.
|
|
58
|
+
*
|
|
59
|
+
* ## Naming Convention
|
|
60
|
+
*
|
|
61
|
+
* - Must start with `test_` prefix (mandatory requirement)
|
|
62
|
+
* - Use snake_case formatting throughout
|
|
63
|
+
* - Include the primary user action (create, get, update, delete, list, etc.)
|
|
64
|
+
* - Specify the target resource (user, product, order, profile, etc.)
|
|
65
|
+
* - Add scenario-specific context (valid_data, invalid_email, not_found,
|
|
66
|
+
* etc.)
|
|
67
|
+
*
|
|
68
|
+
* ## Content Structure
|
|
69
|
+
*
|
|
70
|
+
* Function names should follow this pattern:
|
|
71
|
+
* `test_[user_action]_[resource]_[scenario_context]`
|
|
72
|
+
*
|
|
73
|
+
* Where:
|
|
74
|
+
*
|
|
75
|
+
* - `user_action`: What the user is trying to do
|
|
76
|
+
* - `resource`: What the user is interacting with
|
|
77
|
+
* - `scenario_context`: The specific situation or condition
|
|
78
|
+
*
|
|
79
|
+
* ## User-Focused Examples
|
|
80
|
+
*
|
|
81
|
+
* - `test_create_user_profile_with_complete_information` - User providing all
|
|
82
|
+
* available profile data
|
|
83
|
+
* - `test_retrieve_user_profile_when_profile_exists` - User accessing their
|
|
84
|
+
* existing profile
|
|
85
|
+
* - `test_update_user_email_with_valid_new_address` - User changing their
|
|
86
|
+
* email to a valid new one
|
|
87
|
+
* - `test_delete_user_account_when_user_lacks_permission` - User attempting
|
|
88
|
+
* account deletion without authorization
|
|
89
|
+
* - `test_search_user_profiles_with_pagination_preferences` - User browsing
|
|
90
|
+
* profiles with specific pagination
|
|
91
|
+
*
|
|
92
|
+
* ## Clarity Guidelines
|
|
93
|
+
*
|
|
94
|
+
* - Prioritize clarity over brevity
|
|
95
|
+
* - Avoid technical jargon or implementation terms
|
|
96
|
+
* - Use terminology that reflects user perspective
|
|
97
|
+
* - Ensure the name alone conveys the user's intent
|
|
98
|
+
* - Make it understandable to non-technical stakeholders
|
|
99
|
+
* - Keep consistent with user scenario description
|
|
100
|
+
*
|
|
101
|
+
* ## Single Endpoint Alignment
|
|
102
|
+
*
|
|
103
|
+
* Function names must reflect scenarios that:
|
|
104
|
+
*
|
|
105
|
+
* - Accomplish user goals through this single endpoint only
|
|
106
|
+
* - Don't imply dependency on other API operations
|
|
107
|
+
* - Represent complete user interactions
|
|
108
|
+
*/
|
|
109
|
+
functionName: string;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A list of other API endpoints that must be executed before this test
|
|
113
|
+
* scenario. This helps express dependencies such as data creation or
|
|
114
|
+
* authentication steps required to reach the intended test state.
|
|
115
|
+
*/
|
|
116
|
+
dependsOn: IDependsOn[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface IDependsOn {
|
|
120
|
+
/** Target API endpoint that must be executed before the main operation. */
|
|
121
|
+
endpoint: AutoBeOpenApi.IEndpoint;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A concise exscenarioation of why this API call is required before
|
|
125
|
+
* executing the test for the main operation.
|
|
126
|
+
*
|
|
127
|
+
* Example: "Creates a category so that a product can be linked to it during
|
|
128
|
+
* creation."
|
|
129
|
+
*/
|
|
130
|
+
purpose: string;
|
|
131
|
+
}
|
|
132
|
+
}
|