@griffin-app/griffin-ts 0.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/ASSERTIONS_QUICK_REF.md +161 -0
- package/README.md +297 -0
- package/dist/assertions.d.ts +136 -0
- package/dist/assertions.d.ts.map +1 -0
- package/dist/assertions.js +230 -0
- package/dist/assertions.js.map +1 -0
- package/dist/builder.d.ts +168 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +165 -0
- package/dist/builder.js.map +1 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +6 -0
- package/dist/constants.js.map +1 -0
- package/dist/example-sequential.d.ts +11 -0
- package/dist/example-sequential.d.ts.map +1 -0
- package/dist/example-sequential.js +160 -0
- package/dist/example-sequential.js.map +1 -0
- package/dist/example.d.ts +9 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +36 -0
- package/dist/example.js.map +1 -0
- package/dist/frequency.d.ts +15 -0
- package/dist/frequency.d.ts.map +1 -0
- package/dist/frequency.js +34 -0
- package/dist/frequency.js.map +1 -0
- package/dist/http-methods.d.ts +6 -0
- package/dist/http-methods.d.ts.map +1 -0
- package/dist/http-methods.js +9 -0
- package/dist/http-methods.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/response-formats.d.ts +4 -0
- package/dist/response-formats.d.ts.map +1 -0
- package/dist/response-formats.js +7 -0
- package/dist/response-formats.js.map +1 -0
- package/dist/schema-exports.d.ts +6 -0
- package/dist/schema-exports.d.ts.map +1 -0
- package/dist/schema-exports.js +43 -0
- package/dist/schema-exports.js.map +1 -0
- package/dist/schema.d.ts +311 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +214 -0
- package/dist/schema.js.map +1 -0
- package/dist/secrets.d.ts +56 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +74 -0
- package/dist/secrets.js.map +1 -0
- package/dist/sequential-builder.d.ts +127 -0
- package/dist/sequential-builder.d.ts.map +1 -0
- package/dist/sequential-builder.js +128 -0
- package/dist/sequential-builder.js.map +1 -0
- package/dist/shared.d.ts +8 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +36 -0
- package/dist/shared.js.map +1 -0
- package/dist/target.d.ts +33 -0
- package/dist/target.d.ts.map +1 -0
- package/dist/target.js +53 -0
- package/dist/target.js.map +1 -0
- package/dist/type-exports.d.ts +6 -0
- package/dist/type-exports.d.ts.map +1 -0
- package/dist/type-exports.js +7 -0
- package/dist/type-exports.js.map +1 -0
- package/dist/wait.d.ts +9 -0
- package/dist/wait.d.ts.map +1 -0
- package/dist/wait.js +8 -0
- package/dist/wait.js.map +1 -0
- package/package.json +43 -0
- package/src/assertions.ts +327 -0
- package/src/builder.ts +336 -0
- package/src/constants.ts +5 -0
- package/src/example-sequential.ts +191 -0
- package/src/example.ts +55 -0
- package/src/frequency.ts +38 -0
- package/src/http-methods.ts +5 -0
- package/src/index.ts +70 -0
- package/src/response-formats.ts +3 -0
- package/src/schema-exports.ts +43 -0
- package/src/schema.ts +289 -0
- package/src/secrets.ts +112 -0
- package/src/sequential-builder.ts +254 -0
- package/src/shared.ts +46 -0
- package/src/target.ts +55 -0
- package/src/type-exports.ts +20 -0
- package/src/wait.ts +4 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the Sequential Test Builder
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates the simplified createTestBuilder API for straightforward
|
|
5
|
+
* linear test flows. Use this when you don't need complex branching.
|
|
6
|
+
*
|
|
7
|
+
* For more complex graphs with parallel execution or conditional flows,
|
|
8
|
+
* see example.ts which uses createGraphBuilder.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
GET,
|
|
13
|
+
POST,
|
|
14
|
+
createTestBuilder,
|
|
15
|
+
Json,
|
|
16
|
+
Frequency,
|
|
17
|
+
WaitDuration,
|
|
18
|
+
Assert,
|
|
19
|
+
target,
|
|
20
|
+
} from "./index";
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Example 1: Simple health check with new assertion API
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
const healthCheck = createTestBuilder({
|
|
27
|
+
name: "health-check",
|
|
28
|
+
frequency: Frequency.every(1).minute(),
|
|
29
|
+
})
|
|
30
|
+
.request("health", {
|
|
31
|
+
method: GET,
|
|
32
|
+
base: target("api-service"),
|
|
33
|
+
response_format: Json,
|
|
34
|
+
path: "/health",
|
|
35
|
+
})
|
|
36
|
+
.assert((state) => [
|
|
37
|
+
Assert(state["health"].status).equals(200),
|
|
38
|
+
Assert(state["health"].body["status"]).equals("ok"),
|
|
39
|
+
Assert(state["health"].headers["content-type"]).contains(
|
|
40
|
+
"application/json",
|
|
41
|
+
),
|
|
42
|
+
])
|
|
43
|
+
.build();
|
|
44
|
+
|
|
45
|
+
console.log("Health Check Plan:");
|
|
46
|
+
console.log(JSON.stringify(healthCheck, null, 2));
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Example 2: Complex sequential flow with rich assertions
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
const userJourney = createTestBuilder({
|
|
53
|
+
name: "create-and-verify-user",
|
|
54
|
+
frequency: Frequency.every(5).minute(),
|
|
55
|
+
})
|
|
56
|
+
.request("create_user", {
|
|
57
|
+
method: POST,
|
|
58
|
+
base: target("api-service"),
|
|
59
|
+
response_format: Json,
|
|
60
|
+
path: "/api/v1/users",
|
|
61
|
+
body: {
|
|
62
|
+
name: "Test User",
|
|
63
|
+
email: "test@example.com",
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
.assert((state) => [
|
|
67
|
+
// Status code assertions
|
|
68
|
+
Assert(state["create_user"].status).equals(201),
|
|
69
|
+
|
|
70
|
+
// Body assertions with JSONPath
|
|
71
|
+
Assert(state["create_user"].body["data"]["id"]).not.isNull(),
|
|
72
|
+
Assert(state["create_user"].body["data"]["name"]).equals("Test User"),
|
|
73
|
+
Assert(state["create_user"].body["data"]["email"]).equals(
|
|
74
|
+
"test@example.com",
|
|
75
|
+
),
|
|
76
|
+
Assert(state["create_user"].body["data"]["created_at"]).isDefined(),
|
|
77
|
+
|
|
78
|
+
// Header assertions
|
|
79
|
+
Assert(state["create_user"].headers["content-type"]).contains(
|
|
80
|
+
"application/json",
|
|
81
|
+
),
|
|
82
|
+
Assert(state["create_user"].headers["location"]).startsWith(
|
|
83
|
+
"/api/v1/users/",
|
|
84
|
+
),
|
|
85
|
+
])
|
|
86
|
+
.wait("pause", WaitDuration.seconds(2))
|
|
87
|
+
.request("get_user", {
|
|
88
|
+
method: GET,
|
|
89
|
+
base: target("api-service"),
|
|
90
|
+
response_format: Json,
|
|
91
|
+
path: "/api/v1/users/test@example.com",
|
|
92
|
+
})
|
|
93
|
+
.assert((state) => [
|
|
94
|
+
Assert(state["get_user"].status).equals(200),
|
|
95
|
+
Assert(state["get_user"].body["data"]["name"]).equals("Test User"),
|
|
96
|
+
Assert(state["get_user"].body["data"]["active"]).isTrue(),
|
|
97
|
+
Assert(state["get_user"].body["data"]["deleted"]).not.isTrue(),
|
|
98
|
+
])
|
|
99
|
+
.build();
|
|
100
|
+
|
|
101
|
+
console.log("\nUser Journey Plan:");
|
|
102
|
+
console.log(JSON.stringify(userJourney, null, 2));
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Example 3: Advanced assertions with comparisons
|
|
106
|
+
// ============================================================================
|
|
107
|
+
|
|
108
|
+
const performanceCheck = createTestBuilder({
|
|
109
|
+
name: "performance-check",
|
|
110
|
+
frequency: Frequency.every(10).minute(),
|
|
111
|
+
})
|
|
112
|
+
.request("api_call", {
|
|
113
|
+
method: GET,
|
|
114
|
+
base: target("api-service"),
|
|
115
|
+
response_format: Json,
|
|
116
|
+
path: "/api/v1/metrics",
|
|
117
|
+
})
|
|
118
|
+
.assert((state) => [
|
|
119
|
+
// Numeric comparisons
|
|
120
|
+
Assert(state["api_call"].body["response_time_ms"]).lessThan(500),
|
|
121
|
+
Assert(state["api_call"].body["error_rate"]).lessThanOrEqual(0.01),
|
|
122
|
+
Assert(state["api_call"].body["success_count"]).greaterThan(0),
|
|
123
|
+
Assert(state["api_call"].body["memory_usage_mb"]).lessThan(1024),
|
|
124
|
+
|
|
125
|
+
// String assertions
|
|
126
|
+
Assert(state["api_call"].body["version"]).startsWith("v2."),
|
|
127
|
+
Assert(state["api_call"].body["deployment"]).not.equals(""),
|
|
128
|
+
Assert(state["api_call"].body["environment"]).contains("prod"),
|
|
129
|
+
|
|
130
|
+
// Array/collection assertions
|
|
131
|
+
Assert(state["api_call"].body["endpoints"]).not.isEmpty(),
|
|
132
|
+
])
|
|
133
|
+
.build();
|
|
134
|
+
|
|
135
|
+
console.log("\nPerformance Check Plan:");
|
|
136
|
+
console.log(JSON.stringify(performanceCheck, null, 2));
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Example 4: Multi-step API test with state dependencies
|
|
140
|
+
// ============================================================================
|
|
141
|
+
|
|
142
|
+
const orderWorkflow = createTestBuilder({
|
|
143
|
+
name: "order-workflow",
|
|
144
|
+
frequency: Frequency.every(30).minute(),
|
|
145
|
+
})
|
|
146
|
+
.request("create_order", {
|
|
147
|
+
method: POST,
|
|
148
|
+
base: target("api-service"),
|
|
149
|
+
response_format: Json,
|
|
150
|
+
path: "/api/v1/orders",
|
|
151
|
+
body: {
|
|
152
|
+
items: [{ product_id: "ABC123", quantity: 2 }],
|
|
153
|
+
customer_email: "customer@example.com",
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
.assert((state) => [
|
|
157
|
+
Assert(state["create_order"].status).equals(201),
|
|
158
|
+
Assert(state["create_order"].body["order_id"]).isDefined(),
|
|
159
|
+
Assert(state["create_order"].body["status"]).equals("pending"),
|
|
160
|
+
Assert(state["create_order"].body["total"]).greaterThan(0),
|
|
161
|
+
])
|
|
162
|
+
.request("confirm_order", {
|
|
163
|
+
method: POST,
|
|
164
|
+
base: target("api-service"),
|
|
165
|
+
response_format: Json,
|
|
166
|
+
path: "/api/v1/orders/confirm",
|
|
167
|
+
body: {
|
|
168
|
+
order_id: "${create_order.body.order_id}", // Future: template interpolation
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
.assert((state) => [
|
|
172
|
+
Assert(state["confirm_order"].status).equals(200),
|
|
173
|
+
Assert(state["confirm_order"].body["status"]).equals("confirmed"),
|
|
174
|
+
Assert(state["confirm_order"].body["confirmed_at"]).not.isNull(),
|
|
175
|
+
])
|
|
176
|
+
.wait("processing_time", WaitDuration.seconds(5))
|
|
177
|
+
.request("check_order", {
|
|
178
|
+
method: GET,
|
|
179
|
+
base: target("api-service"),
|
|
180
|
+
response_format: Json,
|
|
181
|
+
path: "/api/v1/orders/${create_order.body.order_id}",
|
|
182
|
+
})
|
|
183
|
+
.assert((state) => [
|
|
184
|
+
Assert(state["check_order"].status).equals(200),
|
|
185
|
+
Assert(state["check_order"].body["status"]).not.equals("pending"),
|
|
186
|
+
Assert(state["check_order"].body["processed"]).isTrue(),
|
|
187
|
+
])
|
|
188
|
+
.build();
|
|
189
|
+
|
|
190
|
+
console.log("\nOrder Workflow Plan:");
|
|
191
|
+
console.log(JSON.stringify(orderWorkflow, null, 2));
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the griffin Test System DSL
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates how to create a test plan using the DSL.
|
|
5
|
+
* In practice, test files would be placed in __griffin__ subdirectories
|
|
6
|
+
* and discovered by the CLI tool.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
GET,
|
|
11
|
+
POST,
|
|
12
|
+
createGraphBuilder,
|
|
13
|
+
Endpoint,
|
|
14
|
+
Wait,
|
|
15
|
+
Assertion,
|
|
16
|
+
Json,
|
|
17
|
+
START,
|
|
18
|
+
END,
|
|
19
|
+
Frequency,
|
|
20
|
+
WaitDuration,
|
|
21
|
+
target,
|
|
22
|
+
} from "./index";
|
|
23
|
+
|
|
24
|
+
const plan = createGraphBuilder({
|
|
25
|
+
name: "foo-bar-check",
|
|
26
|
+
frequency: Frequency.every(1).minute(),
|
|
27
|
+
})
|
|
28
|
+
.addNode(
|
|
29
|
+
"create_foo",
|
|
30
|
+
Endpoint({
|
|
31
|
+
method: POST,
|
|
32
|
+
base: target("api-service"),
|
|
33
|
+
response_format: Json,
|
|
34
|
+
path: "/api/v1/foo",
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
37
|
+
.addNode(
|
|
38
|
+
"get_foo",
|
|
39
|
+
Endpoint({
|
|
40
|
+
method: GET,
|
|
41
|
+
base: target("api-service"),
|
|
42
|
+
response_format: Json,
|
|
43
|
+
path: "/api/v1/foo",
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
46
|
+
.addNode("first_wait", Wait(WaitDuration.minutes(1)))
|
|
47
|
+
// Note: Assertion nodes with the rich DSL are best used with the Sequential Builder
|
|
48
|
+
// For graph builder, assertions can be manually constructed with SerializedAssertion format
|
|
49
|
+
.addEdge(START, "create_foo")
|
|
50
|
+
.addEdge("create_foo", "get_foo")
|
|
51
|
+
.addEdge("get_foo", "first_wait")
|
|
52
|
+
.addEdge("first_wait", END)
|
|
53
|
+
.build();
|
|
54
|
+
|
|
55
|
+
console.log(JSON.stringify(plan, null, 2));
|
package/src/frequency.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Frequency as FrequencyType, FrequencyUnit } from "./schema";
|
|
2
|
+
|
|
3
|
+
export class FrequencyBuilder {
|
|
4
|
+
private value: number;
|
|
5
|
+
|
|
6
|
+
constructor(value: number) {
|
|
7
|
+
this.value = value;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
minute(): FrequencyType {
|
|
11
|
+
return { every: this.value, unit: FrequencyUnit.MINUTE };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
minutes(): FrequencyType {
|
|
15
|
+
return { every: this.value, unit: FrequencyUnit.MINUTE };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
hour(): FrequencyType {
|
|
19
|
+
return { every: this.value, unit: FrequencyUnit.HOUR };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
hours(): FrequencyType {
|
|
23
|
+
return { every: this.value, unit: FrequencyUnit.HOUR };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
day(): FrequencyType {
|
|
27
|
+
return { every: this.value, unit: FrequencyUnit.DAY };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
days(): FrequencyType {
|
|
31
|
+
return { every: this.value, unit: FrequencyUnit.DAY };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Export builder as Frequency for convenience
|
|
36
|
+
export const Frequency = {
|
|
37
|
+
every: (value: number) => new FrequencyBuilder(value),
|
|
38
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Griffin DSL - Top-level exports for building API tests.
|
|
3
|
+
*
|
|
4
|
+
* Import schema values from "griffin/schema"
|
|
5
|
+
* Import schema-derived types from "griffin/types"
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// DSL Builders
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export { createGraphBuilder } from "./builder";
|
|
13
|
+
export type { TestBuilder } from "./builder";
|
|
14
|
+
|
|
15
|
+
export { createTestBuilder } from "./sequential-builder";
|
|
16
|
+
export type {
|
|
17
|
+
SequentialTestBuilder,
|
|
18
|
+
AssertionCallback,
|
|
19
|
+
} from "./sequential-builder";
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// DSL Node Factories
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
export { Endpoint, Wait, Assertion } from "./builder";
|
|
26
|
+
export type {
|
|
27
|
+
EndpointConfig,
|
|
28
|
+
WaitDuration as WaitDurationType,
|
|
29
|
+
} from "./builder";
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// DSL Helpers
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
export { Frequency } from "./frequency";
|
|
36
|
+
export { WaitDuration } from "./wait";
|
|
37
|
+
export { target, isTargetRef } from "./target";
|
|
38
|
+
export { secret, isSecretRef } from "./secrets";
|
|
39
|
+
export type { SecretRefData, SecretOptions } from "./secrets";
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// DSL Constants
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
export { START, END } from "./constants";
|
|
46
|
+
export type { START as StartType, END as EndType } from "./constants";
|
|
47
|
+
|
|
48
|
+
export { GET, POST, PUT, DELETE, PATCH } from "./http-methods";
|
|
49
|
+
export { Json, Xml, Text } from "./response-formats";
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Assertion DSL
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
Assert,
|
|
57
|
+
AssertBuilder,
|
|
58
|
+
UnaryPredicate,
|
|
59
|
+
BinaryPredicateOperator,
|
|
60
|
+
createStateProxy,
|
|
61
|
+
} from "./assertions";
|
|
62
|
+
|
|
63
|
+
export type {
|
|
64
|
+
SerializedAssertion,
|
|
65
|
+
PathDescriptor,
|
|
66
|
+
BinaryPredicate,
|
|
67
|
+
StateProxy,
|
|
68
|
+
NodeResultProxy,
|
|
69
|
+
NestedProxy,
|
|
70
|
+
} from "./assertions";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeBox schemas and enums for griffin test plans.
|
|
3
|
+
* Import from "griffin/schema" to access validation schemas.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
// Schema values
|
|
8
|
+
SecretRefDataSchema,
|
|
9
|
+
SecretRefSchema,
|
|
10
|
+
SecretOrStringSchema,
|
|
11
|
+
ResponseFormatSchema,
|
|
12
|
+
HttpMethodSchema,
|
|
13
|
+
TargetRefSchema,
|
|
14
|
+
EndpointSchema,
|
|
15
|
+
FrequencySchema,
|
|
16
|
+
WaitSchema,
|
|
17
|
+
JSONPathSchema,
|
|
18
|
+
XMLPathSchema,
|
|
19
|
+
TextPathSchema,
|
|
20
|
+
UnaryPredicateSchema,
|
|
21
|
+
BinaryPredicateOperatorSchema,
|
|
22
|
+
BinaryPredicateSchema,
|
|
23
|
+
JSONAssertionSchema,
|
|
24
|
+
XMLAssertionSchema,
|
|
25
|
+
TextAssertionSchema,
|
|
26
|
+
AssertionSchema,
|
|
27
|
+
AssertionsSchema,
|
|
28
|
+
NodeTypeSchema,
|
|
29
|
+
NodeSchema,
|
|
30
|
+
EdgeSchema,
|
|
31
|
+
TestPlanV1Schema,
|
|
32
|
+
|
|
33
|
+
// Enums (runtime values)
|
|
34
|
+
FrequencyUnit,
|
|
35
|
+
ResponseFormat,
|
|
36
|
+
HttpMethod,
|
|
37
|
+
NodeType,
|
|
38
|
+
UnaryPredicate,
|
|
39
|
+
BinaryPredicateOperator,
|
|
40
|
+
|
|
41
|
+
// Constants
|
|
42
|
+
TEST_PLAN_VERSION,
|
|
43
|
+
} from "./schema.js";
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { Type, type Static, type TSchema } from "typebox";
|
|
2
|
+
|
|
3
|
+
export const TEST_PLAN_VERSION = "1.0";
|
|
4
|
+
|
|
5
|
+
// Secret reference schema for values that may contain secrets
|
|
6
|
+
export const SecretRefDataSchema = Type.Object({
|
|
7
|
+
provider: Type.String(),
|
|
8
|
+
ref: Type.String(),
|
|
9
|
+
version: Type.Optional(Type.String()),
|
|
10
|
+
field: Type.Optional(Type.String()),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const SecretRefSchema = Type.Object({
|
|
14
|
+
$secret: SecretRefDataSchema,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Union type for values that can be either a literal or a secret reference
|
|
18
|
+
export const SecretOrStringSchema = Type.Union([
|
|
19
|
+
Type.String(),
|
|
20
|
+
SecretRefSchema,
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export enum FrequencyUnit {
|
|
24
|
+
MINUTE = "MINUTE",
|
|
25
|
+
HOUR = "HOUR",
|
|
26
|
+
DAY = "DAY",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export enum ResponseFormat {
|
|
30
|
+
JSON = "JSON",
|
|
31
|
+
XML = "XML",
|
|
32
|
+
TEXT = "TEXT",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export enum HttpMethod {
|
|
36
|
+
GET = "GET",
|
|
37
|
+
POST = "POST",
|
|
38
|
+
PUT = "PUT",
|
|
39
|
+
DELETE = "DELETE",
|
|
40
|
+
PATCH = "PATCH",
|
|
41
|
+
HEAD = "HEAD",
|
|
42
|
+
OPTIONS = "OPTIONS",
|
|
43
|
+
CONNECT = "CONNECT",
|
|
44
|
+
TRACE = "TRACE",
|
|
45
|
+
}
|
|
46
|
+
export enum NodeType {
|
|
47
|
+
ENDPOINT = "ENDPOINT",
|
|
48
|
+
WAIT = "WAIT",
|
|
49
|
+
ASSERTION = "ASSERTION",
|
|
50
|
+
}
|
|
51
|
+
export const ResponseFormatSchema = Type.Union(
|
|
52
|
+
[
|
|
53
|
+
Type.Literal(ResponseFormat.JSON),
|
|
54
|
+
Type.Literal(ResponseFormat.XML),
|
|
55
|
+
Type.Literal(ResponseFormat.TEXT),
|
|
56
|
+
],
|
|
57
|
+
{ $id: "ResponseFormat" },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
export const HttpMethodSchema = Type.Union(
|
|
61
|
+
[
|
|
62
|
+
Type.Literal(HttpMethod.GET),
|
|
63
|
+
Type.Literal(HttpMethod.POST),
|
|
64
|
+
Type.Literal(HttpMethod.PUT),
|
|
65
|
+
Type.Literal(HttpMethod.DELETE),
|
|
66
|
+
Type.Literal(HttpMethod.PATCH),
|
|
67
|
+
Type.Literal(HttpMethod.HEAD),
|
|
68
|
+
Type.Literal(HttpMethod.OPTIONS),
|
|
69
|
+
Type.Literal(HttpMethod.CONNECT),
|
|
70
|
+
Type.Literal(HttpMethod.TRACE),
|
|
71
|
+
],
|
|
72
|
+
{ $id: "HttpMethod" },
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
export const TargetRefSchema = Type.Object({
|
|
76
|
+
type: Type.Literal("target"),
|
|
77
|
+
key: Type.String(),
|
|
78
|
+
});
|
|
79
|
+
export type TargetRef = Static<typeof TargetRefSchema>;
|
|
80
|
+
|
|
81
|
+
export const EndpointSchema = Type.Object(
|
|
82
|
+
{
|
|
83
|
+
id: Type.String(),
|
|
84
|
+
type: Type.Literal(NodeType.ENDPOINT),
|
|
85
|
+
method: HttpMethodSchema,
|
|
86
|
+
path: Type.String(),
|
|
87
|
+
base: TargetRefSchema,
|
|
88
|
+
headers: Type.Optional(Type.Record(Type.String(), SecretOrStringSchema)),
|
|
89
|
+
body: Type.Optional(Type.Any()), // Body can contain nested SecretRefs
|
|
90
|
+
response_format: ResponseFormatSchema,
|
|
91
|
+
},
|
|
92
|
+
{ $id: "Endpoint" },
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
export type Endpoint = Static<typeof EndpointSchema>;
|
|
96
|
+
|
|
97
|
+
export const FrequencySchema = Type.Object(
|
|
98
|
+
{
|
|
99
|
+
every: Type.Number(),
|
|
100
|
+
unit: Type.Union([
|
|
101
|
+
Type.Literal(FrequencyUnit.MINUTE),
|
|
102
|
+
Type.Literal(FrequencyUnit.HOUR),
|
|
103
|
+
Type.Literal(FrequencyUnit.DAY),
|
|
104
|
+
]),
|
|
105
|
+
},
|
|
106
|
+
{ $id: "Frequency" },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export type Frequency = Static<typeof FrequencySchema>;
|
|
110
|
+
|
|
111
|
+
export const WaitSchema = Type.Object(
|
|
112
|
+
{
|
|
113
|
+
id: Type.String(),
|
|
114
|
+
type: Type.Literal(NodeType.WAIT),
|
|
115
|
+
duration_ms: Type.Number(),
|
|
116
|
+
},
|
|
117
|
+
{ $id: "Wait" },
|
|
118
|
+
);
|
|
119
|
+
export type Wait = Static<typeof WaitSchema>;
|
|
120
|
+
|
|
121
|
+
export const JSONPathSchema = Type.Array(Type.String());
|
|
122
|
+
export const XMLPathSchema = Type.Array(Type.String());
|
|
123
|
+
export const TextPathSchema = Type.String(); // Is there a regex to validate regex ????
|
|
124
|
+
|
|
125
|
+
export enum UnaryPredicate {
|
|
126
|
+
IS_NULL = "IS_NULL",
|
|
127
|
+
IS_NOT_NULL = "IS_NOT_NULL",
|
|
128
|
+
IS_TRUE = "IS_TRUE",
|
|
129
|
+
IS_FALSE = "IS_FALSE",
|
|
130
|
+
IS_EMPTY = "IS_EMPTY",
|
|
131
|
+
IS_NOT_EMPTY = "IS_NOT_EMPTY",
|
|
132
|
+
}
|
|
133
|
+
export const UnaryPredicateSchema = Type.Union(
|
|
134
|
+
[
|
|
135
|
+
Type.Literal(UnaryPredicate.IS_NULL),
|
|
136
|
+
Type.Literal(UnaryPredicate.IS_NOT_NULL),
|
|
137
|
+
Type.Literal(UnaryPredicate.IS_TRUE),
|
|
138
|
+
Type.Literal(UnaryPredicate.IS_FALSE),
|
|
139
|
+
Type.Literal(UnaryPredicate.IS_EMPTY),
|
|
140
|
+
Type.Literal(UnaryPredicate.IS_NOT_EMPTY),
|
|
141
|
+
],
|
|
142
|
+
{ $id: "UnaryPredicate" },
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
export enum BinaryPredicateOperator {
|
|
146
|
+
EQUAL = "EQUAL",
|
|
147
|
+
NOT_EQUAL = "NOT_EQUAL",
|
|
148
|
+
GREATER_THAN = "GREATER_THAN",
|
|
149
|
+
LESS_THAN = "LESS_THAN",
|
|
150
|
+
GREATER_THAN_OR_EQUAL = "GREATER_THAN_OR_EQUAL",
|
|
151
|
+
LESS_THAN_OR_EQUAL = "LESS_THAN_OR_EQUAL",
|
|
152
|
+
CONTAINS = "CONTAINS",
|
|
153
|
+
NOT_CONTAINS = "NOT_CONTAINS",
|
|
154
|
+
STARTS_WITH = "STARTS_WITH",
|
|
155
|
+
ENDS_WITH = "ENDS_WITH",
|
|
156
|
+
NOT_STARTS_WITH = "NOT_STARTS_WITH",
|
|
157
|
+
NOT_ENDS_WITH = "NOT_ENDS_WITH",
|
|
158
|
+
}
|
|
159
|
+
export const BinaryPredicateOperatorSchema = Type.Union(
|
|
160
|
+
[
|
|
161
|
+
Type.Literal(BinaryPredicateOperator.EQUAL),
|
|
162
|
+
Type.Literal(BinaryPredicateOperator.NOT_EQUAL),
|
|
163
|
+
Type.Literal(BinaryPredicateOperator.GREATER_THAN),
|
|
164
|
+
Type.Literal(BinaryPredicateOperator.LESS_THAN),
|
|
165
|
+
Type.Literal(BinaryPredicateOperator.GREATER_THAN_OR_EQUAL),
|
|
166
|
+
Type.Literal(BinaryPredicateOperator.LESS_THAN_OR_EQUAL),
|
|
167
|
+
Type.Literal(BinaryPredicateOperator.CONTAINS),
|
|
168
|
+
Type.Literal(BinaryPredicateOperator.NOT_CONTAINS),
|
|
169
|
+
Type.Literal(BinaryPredicateOperator.STARTS_WITH),
|
|
170
|
+
Type.Literal(BinaryPredicateOperator.ENDS_WITH),
|
|
171
|
+
Type.Literal(BinaryPredicateOperator.NOT_STARTS_WITH),
|
|
172
|
+
Type.Literal(BinaryPredicateOperator.NOT_ENDS_WITH),
|
|
173
|
+
],
|
|
174
|
+
{ $id: "BinaryPredicateOperator" },
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
export const BinaryPredicateSchema = Type.Object(
|
|
178
|
+
{
|
|
179
|
+
expected: Type.Any(),
|
|
180
|
+
operator: BinaryPredicateOperatorSchema,
|
|
181
|
+
},
|
|
182
|
+
{ $id: "BinaryPredicate" },
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
export type BinaryPredicate = Static<typeof BinaryPredicateSchema>;
|
|
186
|
+
export const JSONAssertionSchema = Type.Object(
|
|
187
|
+
{
|
|
188
|
+
nodeId: Type.String(),
|
|
189
|
+
accessor: Type.Union([
|
|
190
|
+
Type.Literal("body"),
|
|
191
|
+
Type.Literal("headers"),
|
|
192
|
+
Type.Literal("status"),
|
|
193
|
+
]),
|
|
194
|
+
path: JSONPathSchema,
|
|
195
|
+
predicate: Type.Union([UnaryPredicateSchema, BinaryPredicateSchema]),
|
|
196
|
+
},
|
|
197
|
+
{ $id: "JSONAssertion" },
|
|
198
|
+
);
|
|
199
|
+
export type JSONAssertion = Static<typeof JSONAssertionSchema>;
|
|
200
|
+
|
|
201
|
+
export const XMLAssertionSchema = Type.Object({
|
|
202
|
+
path: XMLPathSchema,
|
|
203
|
+
expected: Type.Any(),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export type XMLAssertion = Static<typeof XMLAssertionSchema>;
|
|
207
|
+
export const TextAssertionSchema = Type.Object({
|
|
208
|
+
path: TextPathSchema,
|
|
209
|
+
expected: Type.Any(),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
export type TextAssertion = Static<typeof TextAssertionSchema>;
|
|
213
|
+
export const AssertionSchema = Type.Union(
|
|
214
|
+
[
|
|
215
|
+
Type.Intersect([
|
|
216
|
+
Type.Object({
|
|
217
|
+
assertionType: Type.Literal(ResponseFormat.JSON),
|
|
218
|
+
}),
|
|
219
|
+
JSONAssertionSchema,
|
|
220
|
+
]),
|
|
221
|
+
Type.Intersect([
|
|
222
|
+
Type.Object({
|
|
223
|
+
assertionType: Type.Literal(ResponseFormat.XML),
|
|
224
|
+
}),
|
|
225
|
+
XMLAssertionSchema,
|
|
226
|
+
]),
|
|
227
|
+
Type.Intersect([
|
|
228
|
+
Type.Object({
|
|
229
|
+
assertionType: Type.Literal(ResponseFormat.TEXT),
|
|
230
|
+
}),
|
|
231
|
+
TextAssertionSchema,
|
|
232
|
+
]),
|
|
233
|
+
],
|
|
234
|
+
{ $id: "Assertion" },
|
|
235
|
+
);
|
|
236
|
+
export type Assertion = Static<typeof AssertionSchema>;
|
|
237
|
+
|
|
238
|
+
export const AssertionsSchema = Type.Object(
|
|
239
|
+
{
|
|
240
|
+
id: Type.String(),
|
|
241
|
+
type: Type.Literal(NodeType.ASSERTION),
|
|
242
|
+
assertions: Type.Array(AssertionSchema),
|
|
243
|
+
},
|
|
244
|
+
{ $id: "Assertions" },
|
|
245
|
+
);
|
|
246
|
+
export type Assertions = Static<typeof AssertionsSchema>;
|
|
247
|
+
|
|
248
|
+
export const NodeTypeSchema = Type.Union(
|
|
249
|
+
[
|
|
250
|
+
Type.Literal(NodeType.ENDPOINT),
|
|
251
|
+
Type.Literal(NodeType.WAIT),
|
|
252
|
+
Type.Literal(NodeType.ASSERTION),
|
|
253
|
+
],
|
|
254
|
+
{ $id: "NodeType" },
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
export const NodeSchema = Type.Union(
|
|
258
|
+
[EndpointSchema, WaitSchema, AssertionsSchema],
|
|
259
|
+
{ $id: "Node" },
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
export const EdgeSchema = Type.Object(
|
|
263
|
+
{
|
|
264
|
+
from: Type.String(),
|
|
265
|
+
to: Type.String(),
|
|
266
|
+
},
|
|
267
|
+
{ $id: "Edge" },
|
|
268
|
+
);
|
|
269
|
+
export type Node = Static<typeof NodeSchema>;
|
|
270
|
+
export type Edge = Static<typeof EdgeSchema>;
|
|
271
|
+
|
|
272
|
+
export const TestPlanV1Schema = Type.Object(
|
|
273
|
+
{
|
|
274
|
+
organization: Type.Optional(Type.String()), // TODO: make these required
|
|
275
|
+
project: Type.Optional(Type.String()), // TODO: make these required
|
|
276
|
+
locations: Type.Optional(Type.Array(Type.String())),
|
|
277
|
+
id: Type.Readonly(Type.String()),
|
|
278
|
+
name: Type.String(),
|
|
279
|
+
version: Type.Literal("1.0"),
|
|
280
|
+
frequency: Type.Optional(FrequencySchema),
|
|
281
|
+
environment: Type.String({ default: "default" }),
|
|
282
|
+
nodes: Type.Array(NodeSchema),
|
|
283
|
+
edges: Type.Array(EdgeSchema),
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
$id: "TestPlanV1",
|
|
287
|
+
},
|
|
288
|
+
);
|
|
289
|
+
export type TestPlanV1 = Static<typeof TestPlanV1Schema>;
|