@camunda/e2e-test-suite 0.0.1242 → 0.0.1244
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/fixtures/api.d.ts +15 -0
- package/dist/fixtures/api.js +27 -0
- package/dist/pages/SM-8.10/ModelerCreatePage.js +5 -4
- package/dist/tests/api/v2/authorization/authorization-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/authorization/authorization-api-tests.spec.js +247 -0
- package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.d.ts +18 -0
- package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.js +208 -0
- package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.js +264 -0
- package/dist/tests/api/v2/cluster/cluster-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/cluster/cluster-api-tests.spec.js +86 -0
- package/dist/tests/api/v2/decision/decision-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/decision/decision-api-tests.spec.js +233 -0
- package/dist/tests/api/v2/deployment/deployment-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/deployment/deployment-api-tests.spec.js +147 -0
- package/dist/tests/api/v2/document/document-api-tests.spec.d.ts +9 -0
- package/dist/tests/api/v2/document/document-api-tests.spec.js +111 -0
- package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.js +127 -0
- package/dist/tests/api/v2/flows/end-to-end-flows.spec.d.ts +9 -0
- package/dist/tests/api/v2/flows/end-to-end-flows.spec.js +204 -0
- package/dist/tests/api/v2/form/form-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/form/form-api-tests.spec.js +88 -0
- package/dist/tests/api/v2/identity/group-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/group-api-tests.spec.js +172 -0
- package/dist/tests/api/v2/identity/membership-api-tests.spec.d.ts +10 -0
- package/dist/tests/api/v2/identity/membership-api-tests.spec.js +164 -0
- package/dist/tests/api/v2/identity/role-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/role-api-tests.spec.js +212 -0
- package/dist/tests/api/v2/identity/tenant-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/tenant-api-tests.spec.js +319 -0
- package/dist/tests/api/v2/identity/user-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/user-api-tests.spec.js +148 -0
- package/dist/tests/api/v2/job/job-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/job/job-api-tests.spec.js +260 -0
- package/dist/tests/api/v2/message/message-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/message/message-api-tests.spec.js +219 -0
- package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.js +118 -0
- package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.js +335 -0
- package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.js +123 -0
- package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.js +174 -0
- package/dist/tests/api/v2/system/clock-api-tests.spec.d.ts +19 -0
- package/dist/tests/api/v2/system/clock-api-tests.spec.js +62 -0
- package/dist/tests/api/v2/system/system-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/system/system-api-tests.spec.js +183 -0
- package/dist/tests/api/v2/user-task/user-task-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/user-task/user-task-api-tests.spec.js +258 -0
- package/dist/utils/api/assertions.d.ts +50 -0
- package/dist/utils/api/assertions.js +142 -0
- package/dist/utils/api/auth.d.ts +27 -0
- package/dist/utils/api/auth.js +137 -0
- package/dist/utils/api/beans.d.ts +46 -0
- package/dist/utils/api/beans.js +87 -0
- package/dist/utils/api/bpmn.d.ts +50 -0
- package/dist/utils/api/bpmn.js +217 -0
- package/dist/utils/api/capabilities.d.ts +90 -0
- package/dist/utils/api/capabilities.js +198 -0
- package/dist/utils/api/cleanup.d.ts +24 -0
- package/dist/utils/api/cleanup.js +120 -0
- package/dist/utils/api/config.d.ts +103 -0
- package/dist/utils/api/config.js +62 -0
- package/dist/utils/api/deployments.d.ts +45 -0
- package/dist/utils/api/deployments.js +120 -0
- package/dist/utils/api/eventually.d.ts +36 -0
- package/dist/utils/api/eventually.js +54 -0
- package/dist/utils/api/http.d.ts +56 -0
- package/dist/utils/api/http.js +244 -0
- package/dist/utils/api/identity.d.ts +29 -0
- package/dist/utils/api/identity.js +43 -0
- package/dist/utils/api/keys.d.ts +37 -0
- package/dist/utils/api/keys.js +40 -0
- package/dist/utils/api/paths.d.ts +25 -0
- package/dist/utils/api/paths.js +167 -0
- package/dist/utils/api/tenancy.d.ts +31 -0
- package/dist/utils/api/tenancy.js +65 -0
- package/package.json +1 -1
- package/playwright.config.ts +14 -2
- package/dist/resources/test-api-v2-complete.sh +0 -12246
- package/resources/test-api-v2-complete.sh +0 -12246
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const api_1 = require("../../../../fixtures/api");
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const assertions_1 = require("../../../../utils/api/assertions");
|
|
6
|
+
const deployments_1 = require("../../../../utils/api/deployments");
|
|
7
|
+
const bpmn_1 = require("../../../../utils/api/bpmn");
|
|
8
|
+
const eventually_1 = require("../../../../utils/api/eventually");
|
|
9
|
+
const keys_1 = require("../../../../utils/api/keys");
|
|
10
|
+
/**
|
|
11
|
+
* Deploys a process, starts `count` instances, and returns a filter that
|
|
12
|
+
* selects exactly those instances.
|
|
13
|
+
*
|
|
14
|
+
* The shell suite ran its batch operations against `{"filter":{}}` — every
|
|
15
|
+
* instance on the cluster — which is why its batch section had to be serial
|
|
16
|
+
* and why it needed a 900-line concurrency block to cope with the collisions
|
|
17
|
+
* that caused. Scoping each batch to its own process definition removes both.
|
|
18
|
+
*/
|
|
19
|
+
async function seedInstances(api, resources, count) {
|
|
20
|
+
const processId = (0, bpmn_1.uniqueId)('batch-api');
|
|
21
|
+
await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask((0, bpmn_1.uniqueId)('job'))));
|
|
22
|
+
const instanceKeys = [];
|
|
23
|
+
for (let i = 0; i < count; i++) {
|
|
24
|
+
const response = await api.post('/process-instances', {
|
|
25
|
+
body: { processDefinitionId: processId },
|
|
26
|
+
tenant: 'root',
|
|
27
|
+
});
|
|
28
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'instance creation is synchronous');
|
|
29
|
+
const json = (await response.json());
|
|
30
|
+
instanceKeys.push(String(json.processInstanceKey));
|
|
31
|
+
}
|
|
32
|
+
// The batch resolves its scope from the search store, so the instances must
|
|
33
|
+
// be exported before the batch is created or it selects nothing.
|
|
34
|
+
await (0, eventually_1.eventually)(async () => {
|
|
35
|
+
const search = await api.post('/process-instances/search', {
|
|
36
|
+
body: { filter: { processDefinitionId: processId }, page: { limit: 50 } },
|
|
37
|
+
});
|
|
38
|
+
await (0, assertions_1.assertPaginated)(search, { minItems: count });
|
|
39
|
+
});
|
|
40
|
+
return { processId, instanceKeys };
|
|
41
|
+
}
|
|
42
|
+
async function createCancellationBatch(api, processId) {
|
|
43
|
+
const response = await api.post('/process-instances/cancellation', {
|
|
44
|
+
body: { filter: { processDefinitionId: processId } },
|
|
45
|
+
tenant: 'filter',
|
|
46
|
+
});
|
|
47
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'the batch is accepted and its key returned synchronously');
|
|
48
|
+
const json = (await response.json());
|
|
49
|
+
(0, assertions_1.assertRequiredFields)(json, ['batchOperationKey']);
|
|
50
|
+
return String(json.batchOperationKey);
|
|
51
|
+
}
|
|
52
|
+
api_1.test.describe('Batch operations', () => {
|
|
53
|
+
(0, api_1.test)('creates a cancellation batch and cancels its instances', async ({ api, resources, }) => {
|
|
54
|
+
const { processId, instanceKeys } = await seedInstances(api, resources, 3);
|
|
55
|
+
const batchKey = await createCancellationBatch(api, processId);
|
|
56
|
+
await (0, eventually_1.eventually)(async () => {
|
|
57
|
+
const response = await api.get('/batch-operations/{batchOperationKey}', {
|
|
58
|
+
params: { batchOperationKey: batchKey },
|
|
59
|
+
tenant: 'none',
|
|
60
|
+
});
|
|
61
|
+
const batch = (await (0, assertions_1.assertOk)(response));
|
|
62
|
+
(0, assertions_1.assertRequiredFields)(batch, [
|
|
63
|
+
'batchOperationKey',
|
|
64
|
+
'state',
|
|
65
|
+
'batchOperationType',
|
|
66
|
+
]);
|
|
67
|
+
(0, test_1.expect)(batch.batchOperationType).toBe('CANCEL_PROCESS_INSTANCE');
|
|
68
|
+
(0, test_1.expect)(['COMPLETED', 'PARTIALLY_COMPLETED']).toContain(batch.state);
|
|
69
|
+
});
|
|
70
|
+
// The shell suite asserted only that the batch endpoint answered. Whether
|
|
71
|
+
// the instances were actually cancelled was never checked.
|
|
72
|
+
await (0, eventually_1.eventually)(async () => {
|
|
73
|
+
const search = await api.post('/process-instances/search', {
|
|
74
|
+
body: {
|
|
75
|
+
filter: { processDefinitionId: processId, state: 'ACTIVE' },
|
|
76
|
+
page: { limit: 50 },
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
await (0, assertions_1.assertPaginated)(search, { exactItems: 0 });
|
|
80
|
+
});
|
|
81
|
+
(0, test_1.expect)(instanceKeys.length).toBe(3);
|
|
82
|
+
});
|
|
83
|
+
(0, api_1.test)('finds a created batch through search', async ({ api, resources }) => {
|
|
84
|
+
const { processId } = await seedInstances(api, resources, 1);
|
|
85
|
+
const batchKey = await createCancellationBatch(api, processId);
|
|
86
|
+
await (0, eventually_1.eventually)(async () => {
|
|
87
|
+
const response = await api.post('/batch-operations/search', {
|
|
88
|
+
body: { filter: { batchOperationKey: batchKey }, page: { limit: 10 } },
|
|
89
|
+
tenant: 'none',
|
|
90
|
+
});
|
|
91
|
+
await (0, assertions_1.assertPaginated)(response, { exactItems: 1 });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
(0, api_1.test)('filters a batch search by state', async ({ api, resources }) => {
|
|
95
|
+
const { processId } = await seedInstances(api, resources, 1);
|
|
96
|
+
const batchKey = await createCancellationBatch(api, processId);
|
|
97
|
+
await (0, eventually_1.eventually)(async () => {
|
|
98
|
+
const lookup = await api.get('/batch-operations/{batchOperationKey}', {
|
|
99
|
+
params: { batchOperationKey: batchKey },
|
|
100
|
+
tenant: 'none',
|
|
101
|
+
});
|
|
102
|
+
const batch = (await (0, assertions_1.assertOk)(lookup));
|
|
103
|
+
const response = await api.post('/batch-operations/search', {
|
|
104
|
+
body: {
|
|
105
|
+
filter: { state: batch.state },
|
|
106
|
+
page: { limit: 20 },
|
|
107
|
+
},
|
|
108
|
+
tenant: 'none',
|
|
109
|
+
});
|
|
110
|
+
const page = await (0, assertions_1.assertPaginated)(response, { minItems: 1 });
|
|
111
|
+
for (const item of page.items) {
|
|
112
|
+
(0, test_1.expect)(item.state).toBe(batch.state);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
(0, api_1.test)('lists the items belonging to a batch', async ({ api, resources }) => {
|
|
117
|
+
const { processId } = await seedInstances(api, resources, 2);
|
|
118
|
+
const batchKey = await createCancellationBatch(api, processId);
|
|
119
|
+
await (0, eventually_1.eventuallyGet)(async () => {
|
|
120
|
+
const response = await api.post('/batch-operation-items/search', {
|
|
121
|
+
body: { filter: { batchOperationKey: batchKey }, page: { limit: 20 } },
|
|
122
|
+
tenant: 'none',
|
|
123
|
+
});
|
|
124
|
+
const page = await (0, assertions_1.assertPaginated)(response);
|
|
125
|
+
return page.items;
|
|
126
|
+
}, (items) => items.length >= 2, { description: `items of batch ${batchKey}` });
|
|
127
|
+
});
|
|
128
|
+
(0, api_1.test)('suspends and resumes a batch', async ({ api, resources }) => {
|
|
129
|
+
// A larger batch keeps the operation in flight long enough to suspend.
|
|
130
|
+
const { processId } = await seedInstances(api, resources, 5);
|
|
131
|
+
const batchKey = await createCancellationBatch(api, processId);
|
|
132
|
+
const suspend = await api.post('/batch-operations/{batchOperationKey}/suspension', { params: { batchOperationKey: batchKey }, body: {}, tenant: 'none' });
|
|
133
|
+
await (0, assertions_1.assertStatusIn)(suspend, [204, 409], 'a batch that has already finished cannot be suspended');
|
|
134
|
+
if (suspend.status() === 204) {
|
|
135
|
+
await (0, eventually_1.eventually)(async () => {
|
|
136
|
+
const lookup = await api.get('/batch-operations/{batchOperationKey}', {
|
|
137
|
+
params: { batchOperationKey: batchKey },
|
|
138
|
+
tenant: 'none',
|
|
139
|
+
});
|
|
140
|
+
const batch = (await (0, assertions_1.assertOk)(lookup));
|
|
141
|
+
(0, test_1.expect)(batch.state).toBe('SUSPENDED');
|
|
142
|
+
});
|
|
143
|
+
const resume = await api.post('/batch-operations/{batchOperationKey}/resumption', { params: { batchOperationKey: batchKey }, body: {}, tenant: 'none' });
|
|
144
|
+
await (0, assertions_1.assertStatusIn)(resume, [204], 'resumption is acknowledged');
|
|
145
|
+
await (0, eventually_1.eventually)(async () => {
|
|
146
|
+
const lookup = await api.get('/batch-operations/{batchOperationKey}', {
|
|
147
|
+
params: { batchOperationKey: batchKey },
|
|
148
|
+
tenant: 'none',
|
|
149
|
+
});
|
|
150
|
+
const batch = (await (0, assertions_1.assertOk)(lookup));
|
|
151
|
+
(0, test_1.expect)(batch.state).not.toBe('SUSPENDED');
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
(0, api_1.test)('cancels a suspended batch', async ({ api, resources }) => {
|
|
156
|
+
const { processId } = await seedInstances(api, resources, 5);
|
|
157
|
+
const batchKey = await createCancellationBatch(api, processId);
|
|
158
|
+
const suspend = await api.post('/batch-operations/{batchOperationKey}/suspension', { params: { batchOperationKey: batchKey }, body: {}, tenant: 'none' });
|
|
159
|
+
await (0, assertions_1.assertStatusIn)(suspend, [204, 409], 'a batch that has already finished cannot be suspended');
|
|
160
|
+
if (suspend.status() === 204) {
|
|
161
|
+
const cancel = await api.post('/batch-operations/{batchOperationKey}/cancellation', { params: { batchOperationKey: batchKey }, body: {}, tenant: 'none' });
|
|
162
|
+
await (0, assertions_1.assertStatusIn)(cancel, [204], 'cancellation is acknowledged');
|
|
163
|
+
await (0, eventually_1.eventually)(async () => {
|
|
164
|
+
const lookup = await api.get('/batch-operations/{batchOperationKey}', {
|
|
165
|
+
params: { batchOperationKey: batchKey },
|
|
166
|
+
tenant: 'none',
|
|
167
|
+
});
|
|
168
|
+
const batch = (await (0, assertions_1.assertOk)(lookup));
|
|
169
|
+
(0, test_1.expect)(batch.state).toBe('CANCELED');
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
(0, api_1.test)('creates a migration batch', async ({ api, resources }) => {
|
|
174
|
+
const processId = (0, bpmn_1.uniqueId)('batch-migration');
|
|
175
|
+
const jobType = (0, bpmn_1.uniqueId)('job');
|
|
176
|
+
const v1 = await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask(jobType), { name: 'v1' }));
|
|
177
|
+
const v2 = await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask(jobType), { name: 'v2' }));
|
|
178
|
+
(0, test_1.expect)(v2.version).toBe(v1.version + 1);
|
|
179
|
+
const start = await api.post('/process-instances', {
|
|
180
|
+
body: { processDefinitionId: processId },
|
|
181
|
+
tenant: 'root',
|
|
182
|
+
});
|
|
183
|
+
const instance = (await start.json());
|
|
184
|
+
resources.track('processInstance', String(instance.processInstanceKey));
|
|
185
|
+
await (0, eventually_1.eventually)(async () => {
|
|
186
|
+
const search = await api.post('/process-instances/search', {
|
|
187
|
+
body: { filter: { processInstanceKey: instance.processInstanceKey } },
|
|
188
|
+
});
|
|
189
|
+
await (0, assertions_1.assertPaginated)(search, { exactItems: 1 });
|
|
190
|
+
});
|
|
191
|
+
const response = await api.post('/process-instances/migration', {
|
|
192
|
+
body: {
|
|
193
|
+
filter: { processDefinitionKey: v1.processDefinitionKey },
|
|
194
|
+
migrationPlan: {
|
|
195
|
+
targetProcessDefinitionKey: v2.processDefinitionKey,
|
|
196
|
+
mappingInstructions: [
|
|
197
|
+
{ sourceElementId: 'task', targetElementId: 'task' },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
tenant: 'filter',
|
|
202
|
+
});
|
|
203
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'the batch key is returned');
|
|
204
|
+
const json = (await response.json());
|
|
205
|
+
(0, assertions_1.assertRequiredFields)(json, ['batchOperationKey']);
|
|
206
|
+
});
|
|
207
|
+
(0, api_1.test)('rejects a lookup for an absent batch', async ({ api }) => {
|
|
208
|
+
const response = await api.get('/batch-operations/{batchOperationKey}', {
|
|
209
|
+
params: { batchOperationKey: keys_1.ABSENT_KEY },
|
|
210
|
+
tenant: 'none',
|
|
211
|
+
});
|
|
212
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
213
|
+
});
|
|
214
|
+
(0, api_1.test)('rejects suspending an absent batch', async ({ api }) => {
|
|
215
|
+
const response = await api.post('/batch-operations/{batchOperationKey}/suspension', {
|
|
216
|
+
params: { batchOperationKey: keys_1.ABSENT_ROUTABLE_KEY },
|
|
217
|
+
body: {},
|
|
218
|
+
tenant: 'none',
|
|
219
|
+
});
|
|
220
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
221
|
+
});
|
|
222
|
+
(0, api_1.test)('rejects resuming an absent batch', async ({ api }) => {
|
|
223
|
+
const response = await api.post('/batch-operations/{batchOperationKey}/resumption', {
|
|
224
|
+
params: { batchOperationKey: keys_1.ABSENT_ROUTABLE_KEY },
|
|
225
|
+
body: {},
|
|
226
|
+
tenant: 'none',
|
|
227
|
+
});
|
|
228
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
229
|
+
});
|
|
230
|
+
(0, api_1.test)('rejects cancelling an absent batch', async ({ api }) => {
|
|
231
|
+
const response = await api.post('/batch-operations/{batchOperationKey}/cancellation', {
|
|
232
|
+
params: { batchOperationKey: keys_1.ABSENT_ROUTABLE_KEY },
|
|
233
|
+
body: {},
|
|
234
|
+
tenant: 'none',
|
|
235
|
+
});
|
|
236
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
237
|
+
});
|
|
238
|
+
(0, api_1.test)('reports an unknown batch operation id as not found', async ({ api }) => {
|
|
239
|
+
// A batch operation is addressed by a string id, not a numeric key, so
|
|
240
|
+
// there is no "malformed" shape to reject here — any string the cluster
|
|
241
|
+
// does not hold is simply absent.
|
|
242
|
+
const response = await api.get('/batch-operations/{batchOperationKey}', {
|
|
243
|
+
params: { batchOperationKey: 'not-a-key' },
|
|
244
|
+
tenant: 'none',
|
|
245
|
+
});
|
|
246
|
+
await (0, assertions_1.assertNotFound)(response, /not found/i);
|
|
247
|
+
});
|
|
248
|
+
(0, api_1.test)('rejects a cancellation batch with an empty filter', async ({ api }) => {
|
|
249
|
+
const response = await api.post('/process-instances/cancellation', {
|
|
250
|
+
body: { filter: {} },
|
|
251
|
+
tenant: 'none',
|
|
252
|
+
});
|
|
253
|
+
// An unscoped batch would cancel every instance on the cluster. The shell
|
|
254
|
+
// suite asserted `200 202 400` here, so it accepted that outcome.
|
|
255
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
256
|
+
});
|
|
257
|
+
(0, api_1.test)('rejects a batch search with an unknown state filter', async ({ api }) => {
|
|
258
|
+
const response = await api.post('/batch-operations/search', {
|
|
259
|
+
body: { filter: { state: 'NOT_A_STATE' } },
|
|
260
|
+
tenant: 'none',
|
|
261
|
+
});
|
|
262
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const api_1 = require("../../../../fixtures/api");
|
|
4
|
+
const assertions_1 = require("../../../../utils/api/assertions");
|
|
5
|
+
const test_1 = require("@playwright/test");
|
|
6
|
+
const config_1 = require("../../../../utils/api/config");
|
|
7
|
+
const capabilities_1 = require("../../../../utils/api/capabilities");
|
|
8
|
+
api_1.test.describe('Cluster and authentication', () => {
|
|
9
|
+
(0, api_1.test)('returns the authenticated principal', async ({ api }) => {
|
|
10
|
+
api_1.test.skip(capabilities_1.unauthenticatedAccessAllowed !== false, capabilities_1.unauthenticatedAccessAllowed === false
|
|
11
|
+
? ''
|
|
12
|
+
: capabilities_1.unauthenticatedAccessAllowed);
|
|
13
|
+
const response = await api.get('/authentication/me', { tenant: 'none' });
|
|
14
|
+
const me = (await (0, assertions_1.assertOk)(response));
|
|
15
|
+
// The shell suite accepted any 200 here and read the user id with a
|
|
16
|
+
// `// empty` fallback, so an empty body passed. What the endpoint
|
|
17
|
+
// guarantees is `CamundaUserResult`, whose required fields describe what
|
|
18
|
+
// the caller may do — assert those.
|
|
19
|
+
(0, assertions_1.assertRequiredFields)(me, [
|
|
20
|
+
'authorizedComponents',
|
|
21
|
+
'tenants',
|
|
22
|
+
'groups',
|
|
23
|
+
'roles',
|
|
24
|
+
'canLogout',
|
|
25
|
+
]);
|
|
26
|
+
(0, test_1.expect)(Array.isArray(me.authorizedComponents)).toBe(true);
|
|
27
|
+
(0, test_1.expect)(Array.isArray(me.tenants)).toBe(true);
|
|
28
|
+
// `username` names a *user*. Authenticating with client credentials —
|
|
29
|
+
// which is how every OIDC cluster runs this suite — means the principal
|
|
30
|
+
// is a client and there is no username to return, so requiring one here
|
|
31
|
+
// asserted a property of the auth method rather than of the endpoint.
|
|
32
|
+
if (config_1.apiConfig.usersApiEnabled) {
|
|
33
|
+
(0, test_1.expect)(me.username, 'a user principal is identified by name').toBeTruthy();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Not gated on `unauthenticatedAccessAllowed`: /v2/authentication/me
|
|
37
|
+
// refuses an anonymous caller even where the rest of the tree does not,
|
|
38
|
+
// so this one holds on an open cluster too and passed on c8Run.
|
|
39
|
+
(0, api_1.test)('rejects an unauthenticated principal lookup', async ({ api }) => {
|
|
40
|
+
const response = await api.get('/authentication/me', {
|
|
41
|
+
tenant: 'none',
|
|
42
|
+
authorization: null,
|
|
43
|
+
});
|
|
44
|
+
await (0, assertions_1.assertUnauthorized)(response);
|
|
45
|
+
});
|
|
46
|
+
(0, api_1.test)('reports cluster topology with at least one broker', async ({ api }) => {
|
|
47
|
+
const response = await api.get('/topology', { tenant: 'none' });
|
|
48
|
+
const topology = (await (0, assertions_1.assertOk)(response));
|
|
49
|
+
(0, assertions_1.assertRequiredFields)(topology, ['brokers', 'clusterSize']);
|
|
50
|
+
(0, test_1.expect)(topology.brokers?.length ?? 0).toBeGreaterThan(0);
|
|
51
|
+
(0, test_1.expect)(topology.clusterSize).toBeGreaterThan(0);
|
|
52
|
+
for (const broker of topology.brokers ?? []) {
|
|
53
|
+
(0, assertions_1.assertRequiredFields)(broker, ['nodeId', 'host', 'port', 'partitions']);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
(0, api_1.test)('reports the cluster licence state', async ({ api }) => {
|
|
57
|
+
const response = await api.get('/license', { tenant: 'none' });
|
|
58
|
+
const license = (await (0, assertions_1.assertOk)(response));
|
|
59
|
+
(0, assertions_1.assertRequiredFields)(license, ['validLicense', 'licenseType']);
|
|
60
|
+
(0, test_1.expect)(typeof license.validLicense).toBe('boolean');
|
|
61
|
+
});
|
|
62
|
+
(0, api_1.test)('reports readiness on the status endpoint', async ({ api }) => {
|
|
63
|
+
const response = await api.get('/status', { tenant: 'none' });
|
|
64
|
+
await (0, assertions_1.assertNoContent)(response);
|
|
65
|
+
});
|
|
66
|
+
(0, api_1.test)('rejects an unauthenticated topology request', async ({ api }) => {
|
|
67
|
+
api_1.test.skip(capabilities_1.unauthenticatedAccessAllowed !== false, capabilities_1.unauthenticatedAccessAllowed === false
|
|
68
|
+
? ''
|
|
69
|
+
: capabilities_1.unauthenticatedAccessAllowed);
|
|
70
|
+
const response = await api.get('/topology', {
|
|
71
|
+
tenant: 'none',
|
|
72
|
+
authorization: null,
|
|
73
|
+
});
|
|
74
|
+
await (0, assertions_1.assertUnauthorized)(response);
|
|
75
|
+
});
|
|
76
|
+
(0, api_1.test)('rejects a request bearing a malformed token', async ({ api }) => {
|
|
77
|
+
api_1.test.skip(capabilities_1.unauthenticatedAccessAllowed !== false, capabilities_1.unauthenticatedAccessAllowed === false
|
|
78
|
+
? ''
|
|
79
|
+
: capabilities_1.unauthenticatedAccessAllowed);
|
|
80
|
+
const response = await api.get('/topology', {
|
|
81
|
+
tenant: 'none',
|
|
82
|
+
authorization: 'Bearer not-a-real-token',
|
|
83
|
+
});
|
|
84
|
+
await (0, assertions_1.assertStatus)(response, 401);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const api_1 = require("../../../../fixtures/api");
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const assertions_1 = require("../../../../utils/api/assertions");
|
|
6
|
+
const deployments_1 = require("../../../../utils/api/deployments");
|
|
7
|
+
const bpmn_1 = require("../../../../utils/api/bpmn");
|
|
8
|
+
const eventually_1 = require("../../../../utils/api/eventually");
|
|
9
|
+
const keys_1 = require("../../../../utils/api/keys");
|
|
10
|
+
/**
|
|
11
|
+
* Deploys a decision for one test and tracks it for deletion.
|
|
12
|
+
*
|
|
13
|
+
* Per test rather than once per file: a definition shared through `beforeAll`
|
|
14
|
+
* outlives the test that needs it, so no single test can clean it up and the
|
|
15
|
+
* tests that evaluate it compete for the same instances — this file already
|
|
16
|
+
* carries two comments about a definition-scoped search returning a sibling
|
|
17
|
+
* test's instance. A private decision id per test removes that competition at
|
|
18
|
+
* the source.
|
|
19
|
+
*/
|
|
20
|
+
async function deployTestDecision(api, resources, prefix) {
|
|
21
|
+
const decisionId = (0, bpmn_1.uniqueId)(prefix);
|
|
22
|
+
const deployed = await (0, deployments_1.deployDecision)(api, resources, decisionId, (0, bpmn_1.simpleDecision)(decisionId));
|
|
23
|
+
return { decisionId, deployed };
|
|
24
|
+
}
|
|
25
|
+
api_1.test.describe('Decision definitions and instances', () => {
|
|
26
|
+
(0, api_1.test)('finds the deployed decision by id', async ({ api, resources }) => {
|
|
27
|
+
const { decisionId, deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
28
|
+
await (0, eventually_1.eventually)(async () => {
|
|
29
|
+
const response = await api.post('/decision-definitions/search', {
|
|
30
|
+
body: { filter: { decisionDefinitionId: decisionId } },
|
|
31
|
+
});
|
|
32
|
+
const page = await (0, assertions_1.assertPaginated)(response, { minItems: 1 });
|
|
33
|
+
const item = page.items[0];
|
|
34
|
+
(0, test_1.expect)(String(item.decisionDefinitionKey)).toBe(deployed.decisionDefinitionKey);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
(0, api_1.test)('returns the decision definition by key', async ({ api, resources }) => {
|
|
38
|
+
const { decisionId, deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
39
|
+
// `x-eventually-consistent: true` — served from secondary storage, so it
|
|
40
|
+
// can 404 briefly after the deployment.
|
|
41
|
+
await (0, eventually_1.eventually)(async () => {
|
|
42
|
+
const response = await api.get('/decision-definitions/{decisionDefinitionKey}', { params: { decisionDefinitionKey: deployed.decisionDefinitionKey } });
|
|
43
|
+
const definition = (await (0, assertions_1.assertOk)(response));
|
|
44
|
+
(0, assertions_1.assertRequiredFields)(definition, [
|
|
45
|
+
'decisionDefinitionKey',
|
|
46
|
+
'decisionDefinitionId',
|
|
47
|
+
'version',
|
|
48
|
+
'decisionRequirementsKey',
|
|
49
|
+
]);
|
|
50
|
+
(0, test_1.expect)(definition.decisionDefinitionId).toBe(decisionId);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
(0, api_1.test)('returns the decision XML by key', async ({ api, resources }) => {
|
|
54
|
+
const { decisionId, deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
55
|
+
await (0, eventually_1.eventually)(async () => {
|
|
56
|
+
const response = await api.get('/decision-definitions/{decisionDefinitionKey}/xml', { params: { decisionDefinitionKey: deployed.decisionDefinitionKey } });
|
|
57
|
+
// assertOk parses the body as JSON; this endpoint returns XML text.
|
|
58
|
+
await (0, assertions_1.assertStatus)(response, 200);
|
|
59
|
+
const xml = await response.text();
|
|
60
|
+
(0, test_1.expect)(xml).toContain(decisionId);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
(0, api_1.test)('evaluates the decision and returns the matching output', async ({ api, resources, }) => {
|
|
64
|
+
const { deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
65
|
+
const response = await api.post('/decision-definitions/evaluation', {
|
|
66
|
+
body: {
|
|
67
|
+
decisionDefinitionKey: deployed.decisionDefinitionKey,
|
|
68
|
+
variables: { inputVariable: 'match' },
|
|
69
|
+
},
|
|
70
|
+
tenant: 'root',
|
|
71
|
+
});
|
|
72
|
+
const result = (await (0, assertions_1.assertOk)(response));
|
|
73
|
+
(0, assertions_1.assertRequiredFields)(result, [
|
|
74
|
+
'decisionDefinitionKey',
|
|
75
|
+
'decisionEvaluationKey',
|
|
76
|
+
'output',
|
|
77
|
+
]);
|
|
78
|
+
// The shell suite accepted `200 400 404` here, so a decision that failed
|
|
79
|
+
// to evaluate passed. The rule table maps "match" to "matched", and
|
|
80
|
+
// `output` carries the JSON encoding of the value, not the bare value.
|
|
81
|
+
(0, test_1.expect)(result.output).toBe('"matched"');
|
|
82
|
+
});
|
|
83
|
+
(0, api_1.test)('evaluates the decision to its default rule', async ({ api, resources, }) => {
|
|
84
|
+
const { deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
85
|
+
const response = await api.post('/decision-definitions/evaluation', {
|
|
86
|
+
body: {
|
|
87
|
+
decisionDefinitionKey: deployed.decisionDefinitionKey,
|
|
88
|
+
variables: { inputVariable: 'anything-else' },
|
|
89
|
+
},
|
|
90
|
+
tenant: 'root',
|
|
91
|
+
});
|
|
92
|
+
const result = (await (0, assertions_1.assertOk)(response));
|
|
93
|
+
(0, test_1.expect)(result.output).toBe('"default"');
|
|
94
|
+
});
|
|
95
|
+
(0, api_1.test)('records an instance for each evaluation', async ({ api, resources }) => {
|
|
96
|
+
const { decisionId, deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
97
|
+
const evaluation = await api.post('/decision-definitions/evaluation', {
|
|
98
|
+
body: {
|
|
99
|
+
decisionDefinitionKey: deployed.decisionDefinitionKey,
|
|
100
|
+
variables: { inputVariable: 'match' },
|
|
101
|
+
},
|
|
102
|
+
tenant: 'root',
|
|
103
|
+
});
|
|
104
|
+
const evaluated = (await (0, assertions_1.assertOk)(evaluation));
|
|
105
|
+
const evaluationKey = String(evaluated.decisionEvaluationKey);
|
|
106
|
+
// Filtered by the evaluation, not by the definition: this test evaluates
|
|
107
|
+
// once, but a definition-scoped search would be satisfied by whichever
|
|
108
|
+
// instance is exported first, so as soon as a test evaluates twice
|
|
109
|
+
// `items[0]` is whichever evaluation landed first rather than the one
|
|
110
|
+
// under assertion. That is how it failed when the whole file shared one
|
|
111
|
+
// definition — one flake in run 35606946161, where the search returned a
|
|
112
|
+
// sibling test's instance.
|
|
113
|
+
const instances = await (0, eventually_1.eventuallyGet)(async () => {
|
|
114
|
+
const response = await api.post('/decision-instances/search', {
|
|
115
|
+
body: {
|
|
116
|
+
filter: { decisionEvaluationKey: evaluationKey },
|
|
117
|
+
page: { limit: 10 },
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return (await (0, assertions_1.assertPaginated)(response)).items;
|
|
121
|
+
}, (items) => items.length > 0, { description: `decision instance for evaluation ${evaluationKey}` });
|
|
122
|
+
// The single-instance GET is addressed by `decisionEvaluationInstanceKey`
|
|
123
|
+
// — the compound `<decisionEvaluationKey>-<index>` that identifies one
|
|
124
|
+
// decision inside an evaluation, not the plain numeric evaluation key.
|
|
125
|
+
// Passing the numeric key earns a 400 naming the `^\d+-\d+$` pattern.
|
|
126
|
+
const byKey = await api.get('/decision-instances/{decisionEvaluationInstanceKey}', {
|
|
127
|
+
params: {
|
|
128
|
+
decisionEvaluationInstanceKey: String(instances[0].decisionEvaluationInstanceKey),
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
const json = (await (0, assertions_1.assertOk)(byKey));
|
|
132
|
+
(0, assertions_1.assertRequiredFields)(json, [
|
|
133
|
+
'decisionEvaluationInstanceKey',
|
|
134
|
+
'decisionDefinitionId',
|
|
135
|
+
'state',
|
|
136
|
+
]);
|
|
137
|
+
(0, test_1.expect)(json.decisionDefinitionId).toBe(decisionId);
|
|
138
|
+
});
|
|
139
|
+
(0, api_1.test)('exposes the evaluated inputs and outputs of an instance', async ({ api, resources, }) => {
|
|
140
|
+
const { deployed } = await deployTestDecision(api, resources, 'decision-api');
|
|
141
|
+
const evaluation = await api.post('/decision-definitions/evaluation', {
|
|
142
|
+
body: {
|
|
143
|
+
decisionDefinitionKey: deployed.decisionDefinitionKey,
|
|
144
|
+
variables: { inputVariable: 'match' },
|
|
145
|
+
},
|
|
146
|
+
tenant: 'root',
|
|
147
|
+
});
|
|
148
|
+
const evaluated = (await (0, assertions_1.assertOk)(evaluation));
|
|
149
|
+
const evaluationKey = String(evaluated.decisionEvaluationKey);
|
|
150
|
+
// There are no `/evaluated-inputs` and `/evaluated-outputs`
|
|
151
|
+
// sub-resources: the single-instance GET carries `evaluatedInputs` and
|
|
152
|
+
// `matchedRules` inline, and the v2 spec marks both required on it. That
|
|
153
|
+
// also lets this assert the values rather than only that an array came
|
|
154
|
+
// back.
|
|
155
|
+
//
|
|
156
|
+
// The key comes from the search rather than the evaluation response: the
|
|
157
|
+
// GET is addressed by the compound `<evaluationKey>-<index>`, which the
|
|
158
|
+
// evaluation result does not carry. The search is scoped to this
|
|
159
|
+
// evaluation for the same reason as above — a definition-scoped one can
|
|
160
|
+
// return a sibling test's instance, whose input value differs.
|
|
161
|
+
await (0, eventually_1.eventually)(async () => {
|
|
162
|
+
const search = await api.post('/decision-instances/search', {
|
|
163
|
+
body: {
|
|
164
|
+
filter: { decisionEvaluationKey: evaluationKey },
|
|
165
|
+
page: { limit: 1 },
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
const page = await (0, assertions_1.assertPaginated)(search, { minItems: 1 });
|
|
169
|
+
const decisionEvaluationInstanceKey = String(page.items[0]
|
|
170
|
+
.decisionEvaluationInstanceKey);
|
|
171
|
+
const response = await api.get('/decision-instances/{decisionEvaluationInstanceKey}', { params: { decisionEvaluationInstanceKey } });
|
|
172
|
+
const json = (await (0, assertions_1.assertOk)(response));
|
|
173
|
+
(0, test_1.expect)(json.evaluatedInputs).toEqual([
|
|
174
|
+
test_1.expect.objectContaining({ inputValue: '"match"' }),
|
|
175
|
+
]);
|
|
176
|
+
(0, test_1.expect)(json.matchedRules).toEqual([
|
|
177
|
+
test_1.expect.objectContaining({ ruleId: 'rule_match' }),
|
|
178
|
+
]);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
(0, api_1.test)('rejects evaluating an absent decision', async ({ api }) => {
|
|
182
|
+
const response = await api.post('/decision-definitions/evaluation', {
|
|
183
|
+
body: { decisionDefinitionKey: keys_1.ABSENT_ROUTABLE_KEY, variables: {} },
|
|
184
|
+
tenant: 'root',
|
|
185
|
+
});
|
|
186
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
187
|
+
});
|
|
188
|
+
(0, api_1.test)('rejects a lookup for an absent decision key', async ({ api }) => {
|
|
189
|
+
const response = await api.get('/decision-definitions/{decisionDefinitionKey}', { params: { decisionDefinitionKey: keys_1.ABSENT_KEY } });
|
|
190
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
191
|
+
});
|
|
192
|
+
(0, api_1.test)('rejects a malformed decision key', async ({ api }) => {
|
|
193
|
+
const response = await api.get('/decision-definitions/{decisionDefinitionKey}', { params: { decisionDefinitionKey: 'not-a-key' } });
|
|
194
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
api_1.test.describe('Decision requirements', () => {
|
|
198
|
+
(0, api_1.test)('returns the requirements definition by key', async ({ api, resources, }) => {
|
|
199
|
+
const { deployed } = await deployTestDecision(api, resources, 'drd-api');
|
|
200
|
+
await (0, eventually_1.eventually)(async () => {
|
|
201
|
+
const response = await api.get('/decision-requirements/{decisionRequirementsKey}', { params: { decisionRequirementsKey: deployed.decisionRequirementsKey } });
|
|
202
|
+
const json = (await (0, assertions_1.assertOk)(response));
|
|
203
|
+
(0, assertions_1.assertRequiredFields)(json, [
|
|
204
|
+
'decisionRequirementsKey',
|
|
205
|
+
'decisionRequirementsId',
|
|
206
|
+
'version',
|
|
207
|
+
]);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
(0, api_1.test)('returns the requirements XML by key', async ({ api, resources }) => {
|
|
211
|
+
const { decisionId, deployed } = await deployTestDecision(api, resources, 'drd-api');
|
|
212
|
+
await (0, eventually_1.eventually)(async () => {
|
|
213
|
+
const response = await api.get('/decision-requirements/{decisionRequirementsKey}/xml', { params: { decisionRequirementsKey: deployed.decisionRequirementsKey } });
|
|
214
|
+
await (0, assertions_1.assertStatus)(response, 200);
|
|
215
|
+
(0, test_1.expect)(await response.text()).toContain(decisionId);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
(0, api_1.test)('finds the requirements through search', async ({ api, resources }) => {
|
|
219
|
+
const { deployed } = await deployTestDecision(api, resources, 'drd-api');
|
|
220
|
+
await (0, eventually_1.eventually)(async () => {
|
|
221
|
+
const response = await api.post('/decision-requirements/search', {
|
|
222
|
+
body: {
|
|
223
|
+
filter: { decisionRequirementsKey: deployed.decisionRequirementsKey },
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
await (0, assertions_1.assertPaginated)(response, { minItems: 1 });
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
(0, api_1.test)('rejects a lookup for absent requirements', async ({ api }) => {
|
|
230
|
+
const response = await api.get('/decision-requirements/{decisionRequirementsKey}', { params: { decisionRequirementsKey: keys_1.ABSENT_KEY } });
|
|
231
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|