@camunda/e2e-test-suite 0.0.1241 → 0.0.1243
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/NavigationPage.d.ts +3 -2
- package/dist/pages/SM-8.10/NavigationPage.js +24 -34
- package/dist/pages/SM-8.10/ProcessApplicationPage.js +7 -0
- 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,118 @@
|
|
|
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 the definition under test for one test, and tracks it for deletion.
|
|
12
|
+
*
|
|
13
|
+
* The shell suite hunted the cluster for a pre-existing process named
|
|
14
|
+
* "benchmark" or "one_task" and fell back to "first available" with a printed
|
|
15
|
+
* warning, so the endpoints under test were exercised against whatever
|
|
16
|
+
* happened to be deployed. Deploying our own makes every assertion below about
|
|
17
|
+
* a known model.
|
|
18
|
+
*
|
|
19
|
+
* Per test rather than once per file: a definition deployed in `beforeAll`
|
|
20
|
+
* outlives the test that needs it, so no test can clean it up and the run
|
|
21
|
+
* leaves it on the cluster and in its indices for good.
|
|
22
|
+
*/
|
|
23
|
+
async function deployDefinitionUnderTest(api, resources) {
|
|
24
|
+
const processId = (0, bpmn_1.uniqueId)('process-definition-api');
|
|
25
|
+
const deployed = await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask((0, bpmn_1.uniqueId)('job-type'))));
|
|
26
|
+
return { processId, deployed };
|
|
27
|
+
}
|
|
28
|
+
api_1.test.describe('Process definitions', () => {
|
|
29
|
+
(0, api_1.test)('finds the deployed definition by id', async ({ api, resources }) => {
|
|
30
|
+
const { processId, deployed } = await deployDefinitionUnderTest(api, resources);
|
|
31
|
+
await (0, eventually_1.eventually)(async () => {
|
|
32
|
+
const response = await api.post('/process-definitions/search', {
|
|
33
|
+
body: { filter: { processDefinitionId: processId } },
|
|
34
|
+
});
|
|
35
|
+
const page = await (0, assertions_1.assertPaginated)(response, { minItems: 1 });
|
|
36
|
+
const item = page.items[0];
|
|
37
|
+
(0, test_1.expect)(String(item.processDefinitionKey)).toBe(deployed.processDefinitionKey);
|
|
38
|
+
(0, test_1.expect)(item.version).toBe(deployed.version);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
(0, api_1.test)('returns the definition by key', async ({ api, resources }) => {
|
|
42
|
+
const { processId, deployed } = await deployDefinitionUnderTest(api, resources);
|
|
43
|
+
// Marked `x-eventually-consistent: true` in the API description: the read
|
|
44
|
+
// is served from secondary storage, so it can answer 404 for a short
|
|
45
|
+
// window after the deployment command is written.
|
|
46
|
+
await (0, eventually_1.eventually)(async () => {
|
|
47
|
+
const response = await api.get('/process-definitions/{processDefinitionKey}', { params: { processDefinitionKey: deployed.processDefinitionKey } });
|
|
48
|
+
const definition = (await (0, assertions_1.assertOk)(response));
|
|
49
|
+
(0, assertions_1.assertRequiredFields)(definition, [
|
|
50
|
+
'processDefinitionKey',
|
|
51
|
+
'processDefinitionId',
|
|
52
|
+
'version',
|
|
53
|
+
]);
|
|
54
|
+
(0, test_1.expect)(definition.processDefinitionId).toBe(processId);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
(0, api_1.test)('returns the definition XML by key', async ({ api, resources }) => {
|
|
58
|
+
const { processId, deployed } = await deployDefinitionUnderTest(api, resources);
|
|
59
|
+
// Also `x-eventually-consistent: true`. The earlier "XML retrieval is
|
|
60
|
+
// synchronous for a deployed definition" reasoning was wrong.
|
|
61
|
+
await (0, eventually_1.eventually)(async () => {
|
|
62
|
+
const response = await api.get('/process-definitions/{processDefinitionKey}/xml', { params: { processDefinitionKey: deployed.processDefinitionKey } });
|
|
63
|
+
await (0, assertions_1.assertStatus)(response, 200);
|
|
64
|
+
// The shell suite asserted only the 200. A definition whose XML came
|
|
65
|
+
// back empty or belonging to another process would have passed.
|
|
66
|
+
const xml = await response.text();
|
|
67
|
+
(0, test_1.expect)(xml).toContain(`id="${processId}"`);
|
|
68
|
+
(0, test_1.expect)(xml).toContain('bpmn:process');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
(0, api_1.test)('reports element-instance statistics for the definition', async ({ api, resources, }) => {
|
|
72
|
+
const { deployed } = await deployDefinitionUnderTest(api, resources);
|
|
73
|
+
const response = await api.post('/process-definitions/{processDefinitionKey}/statistics/element-instances', {
|
|
74
|
+
params: { processDefinitionKey: deployed.processDefinitionKey },
|
|
75
|
+
body: { filter: {} },
|
|
76
|
+
});
|
|
77
|
+
const stats = (await (0, assertions_1.assertOk)(response));
|
|
78
|
+
(0, test_1.expect)(Array.isArray(stats.items)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
(0, api_1.test)('searches instances of the definition', async ({ api, resources }) => {
|
|
81
|
+
const { deployed } = await deployDefinitionUnderTest(api, resources);
|
|
82
|
+
// There is no /process-definitions/{key}/instances sub-resource in any
|
|
83
|
+
// supported version; instances of a definition are found by filtering the
|
|
84
|
+
// process-instance search. The nested path was read as "404 until the
|
|
85
|
+
// deployment reaches secondary storage", but it 404s because nothing
|
|
86
|
+
// serves it, so polling only spent the retry budget before failing.
|
|
87
|
+
const response = await api.post('/process-instances/search', {
|
|
88
|
+
body: { filter: { processDefinitionKey: deployed.processDefinitionKey } },
|
|
89
|
+
});
|
|
90
|
+
await (0, assertions_1.assertPaginated)(response);
|
|
91
|
+
});
|
|
92
|
+
(0, api_1.test)('reports no linked start form for a definition without one', async ({ api, resources, }) => {
|
|
93
|
+
const { deployed } = await deployDefinitionUnderTest(api, resources);
|
|
94
|
+
const response = await api.get('/process-definitions/{processDefinitionKey}/form', { params: { processDefinitionKey: deployed.processDefinitionKey } });
|
|
95
|
+
// The deployed model carries no start form, so the API reports "nothing
|
|
96
|
+
// linked". Which of these two it uses depends on the minor version.
|
|
97
|
+
await (0, assertions_1.assertStatusIn)(response, [204, 404], 'a definition with no linked start form reports 204 on 8.8+ and 404 on earlier minors');
|
|
98
|
+
});
|
|
99
|
+
(0, api_1.test)('rejects a lookup for an absent definition key', async ({ api }) => {
|
|
100
|
+
const response = await api.get('/process-definitions/{processDefinitionKey}', { params: { processDefinitionKey: keys_1.ABSENT_KEY } });
|
|
101
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
102
|
+
});
|
|
103
|
+
(0, api_1.test)('rejects an XML lookup for an absent definition key', async ({ api }) => {
|
|
104
|
+
const response = await api.get('/process-definitions/{processDefinitionKey}/xml', { params: { processDefinitionKey: keys_1.ABSENT_KEY } });
|
|
105
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
106
|
+
});
|
|
107
|
+
(0, api_1.test)('rejects a malformed definition key', async ({ api }) => {
|
|
108
|
+
const response = await api.get('/process-definitions/{processDefinitionKey}', { params: { processDefinitionKey: 'not-a-key' } });
|
|
109
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
110
|
+
});
|
|
111
|
+
(0, api_1.test)('honours the page size on search', async ({ api }) => {
|
|
112
|
+
const response = await api.post('/process-definitions/search', {
|
|
113
|
+
body: { filter: {}, page: { limit: 1 } },
|
|
114
|
+
});
|
|
115
|
+
const page = await (0, assertions_1.assertPaginated)(response, { minItems: 1 });
|
|
116
|
+
(0, test_1.expect)(page.items.length).toBe(1);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,335 @@
|
|
|
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 capabilities_1 = require("../../../../utils/api/capabilities");
|
|
10
|
+
const keys_1 = require("../../../../utils/api/keys");
|
|
11
|
+
/**
|
|
12
|
+
* Deploys the one-service-task process this file's tests start instances of,
|
|
13
|
+
* and tracks it for deletion.
|
|
14
|
+
*
|
|
15
|
+
* Per test rather than once per file: a definition deployed in `beforeAll`
|
|
16
|
+
* outlives the test that needs it, so no test can clean it up and the run
|
|
17
|
+
* leaves it on the cluster and in its indices for good. The service task is
|
|
18
|
+
* what makes an instance wait rather than complete, which every assertion
|
|
19
|
+
* below about a running instance depends on.
|
|
20
|
+
*/
|
|
21
|
+
async function deployProcessUnderTest(api, resources, prefix = 'process-instance-api') {
|
|
22
|
+
const processId = (0, bpmn_1.uniqueId)(prefix);
|
|
23
|
+
const deployed = await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask((0, bpmn_1.uniqueId)('pi-job'))));
|
|
24
|
+
return { processId, deployed };
|
|
25
|
+
}
|
|
26
|
+
async function startInstance(api, processDefinitionId, variables = {}) {
|
|
27
|
+
const response = await api.post('/process-instances', {
|
|
28
|
+
body: { processDefinitionId, variables },
|
|
29
|
+
tenant: 'root',
|
|
30
|
+
});
|
|
31
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'instance creation answers synchronously unless awaitCompletion is set');
|
|
32
|
+
const json = (await response.json());
|
|
33
|
+
(0, assertions_1.assertRequiredFields)(json, [
|
|
34
|
+
'processInstanceKey',
|
|
35
|
+
'processDefinitionKey',
|
|
36
|
+
'processDefinitionId',
|
|
37
|
+
]);
|
|
38
|
+
return json;
|
|
39
|
+
}
|
|
40
|
+
api_1.test.describe('Process instances', () => {
|
|
41
|
+
(0, api_1.test)('starts an instance of the deployed process', async ({ api, resources, }) => {
|
|
42
|
+
const { processId, deployed } = await deployProcessUnderTest(api, resources);
|
|
43
|
+
const instance = await startInstance(api, processId);
|
|
44
|
+
resources.track('processInstance', instance.processInstanceKey);
|
|
45
|
+
(0, test_1.expect)(String(instance.processDefinitionKey)).toBe(deployed.processDefinitionKey);
|
|
46
|
+
(0, test_1.expect)(instance.processDefinitionId).toBe(processId);
|
|
47
|
+
});
|
|
48
|
+
(0, api_1.test)('starts an instance carrying variables', async ({ api, resources }) => {
|
|
49
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
50
|
+
const instance = await startInstance(api, processId, {
|
|
51
|
+
orderId: 'order-42',
|
|
52
|
+
amount: 99,
|
|
53
|
+
});
|
|
54
|
+
resources.track('processInstance', instance.processInstanceKey);
|
|
55
|
+
await (0, eventually_1.eventually)(async () => {
|
|
56
|
+
const response = await api.post('/variables/search', {
|
|
57
|
+
body: {
|
|
58
|
+
filter: { processInstanceKey: instance.processInstanceKey },
|
|
59
|
+
page: { limit: 50 },
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const page = await (0, assertions_1.assertPaginated)(response, { minItems: 1 });
|
|
63
|
+
const names = page.items.map((item) => item.name);
|
|
64
|
+
(0, test_1.expect)(names).toEqual(test_1.expect.arrayContaining(['orderId', 'amount']));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
(0, api_1.test)('finds a started instance through search', async ({ api, resources }) => {
|
|
68
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
69
|
+
const instance = await startInstance(api, processId);
|
|
70
|
+
resources.track('processInstance', instance.processInstanceKey);
|
|
71
|
+
await (0, eventually_1.eventually)(async () => {
|
|
72
|
+
const response = await api.post('/process-instances/search', {
|
|
73
|
+
body: { filter: { processInstanceKey: instance.processInstanceKey } },
|
|
74
|
+
});
|
|
75
|
+
const page = await (0, assertions_1.assertPaginated)(response, { exactItems: 1 });
|
|
76
|
+
const item = page.items[0];
|
|
77
|
+
(0, test_1.expect)(item.processDefinitionId).toBe(processId);
|
|
78
|
+
(0, test_1.expect)(item.state).toBe('ACTIVE');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
(0, api_1.test)('returns a started instance by key', async ({ api, resources }) => {
|
|
82
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
83
|
+
const instance = await startInstance(api, processId);
|
|
84
|
+
const key = instance.processInstanceKey;
|
|
85
|
+
resources.track('processInstance', key);
|
|
86
|
+
await (0, eventually_1.eventually)(async () => {
|
|
87
|
+
const response = await api.get('/process-instances/{processInstanceKey}', { params: { processInstanceKey: key } });
|
|
88
|
+
const json = (await (0, assertions_1.assertOk)(response));
|
|
89
|
+
(0, assertions_1.assertRequiredFields)(json, [
|
|
90
|
+
'processInstanceKey',
|
|
91
|
+
'processDefinitionId',
|
|
92
|
+
'state',
|
|
93
|
+
]);
|
|
94
|
+
(0, test_1.expect)(json.state).toBe('ACTIVE');
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
(0, api_1.test)('reports sequence flows taken by the instance', async ({ api, resources, }) => {
|
|
98
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
99
|
+
const instance = await startInstance(api, processId);
|
|
100
|
+
const key = instance.processInstanceKey;
|
|
101
|
+
resources.track('processInstance', key);
|
|
102
|
+
await (0, eventually_1.eventually)(async () => {
|
|
103
|
+
const response = await api.get('/process-instances/{processInstanceKey}/sequence-flows', { params: { processInstanceKey: key } });
|
|
104
|
+
const json = (await (0, assertions_1.assertOk)(response));
|
|
105
|
+
(0, test_1.expect)(Array.isArray(json.items)).toBe(true);
|
|
106
|
+
// The instance waits at the service task, so the flow into it is taken.
|
|
107
|
+
(0, test_1.expect)(json.items.length).toBeGreaterThan(0);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
(0, api_1.test)('reports element-instance statistics for the instance', async ({ api, resources, }) => {
|
|
111
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
112
|
+
const instance = await startInstance(api, processId);
|
|
113
|
+
const key = instance.processInstanceKey;
|
|
114
|
+
resources.track('processInstance', key);
|
|
115
|
+
await (0, eventually_1.eventually)(async () => {
|
|
116
|
+
const response = await api.get('/process-instances/{processInstanceKey}/statistics/element-instances', { params: { processInstanceKey: key } });
|
|
117
|
+
const json = (await (0, assertions_1.assertOk)(response));
|
|
118
|
+
(0, test_1.expect)(Array.isArray(json.items)).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
(0, api_1.test)('reports an empty call hierarchy for a top-level instance', async ({ api, resources, }) => {
|
|
122
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
123
|
+
const instance = await startInstance(api, processId);
|
|
124
|
+
const key = instance.processInstanceKey;
|
|
125
|
+
resources.track('processInstance', key);
|
|
126
|
+
await (0, eventually_1.eventually)(async () => {
|
|
127
|
+
const response = await api.get('/process-instances/{processInstanceKey}/call-hierarchy', { params: { processInstanceKey: key } });
|
|
128
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'call hierarchy is available once the instance is exported');
|
|
129
|
+
const json = (await response.json());
|
|
130
|
+
// A process with no call activity is its own only hierarchy entry.
|
|
131
|
+
(0, test_1.expect)(Array.isArray(json)).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
(0, api_1.test)('reports no incidents for a healthy instance', async ({ api, resources, }) => {
|
|
135
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
136
|
+
const instance = await startInstance(api, processId);
|
|
137
|
+
const key = instance.processInstanceKey;
|
|
138
|
+
resources.track('processInstance', key);
|
|
139
|
+
await (0, eventually_1.eventually)(async () => {
|
|
140
|
+
const response = await api.post('/process-instances/{processInstanceKey}/incidents/search', { params: { processInstanceKey: key }, body: { filter: {} } });
|
|
141
|
+
await (0, assertions_1.assertPaginated)(response, { exactItems: 0 });
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
(0, api_1.test)('cancels a running instance', async ({ api, resources }) => {
|
|
145
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
146
|
+
const instance = await startInstance(api, processId);
|
|
147
|
+
const key = instance.processInstanceKey;
|
|
148
|
+
const response = await api.post('/process-instances/{processInstanceKey}/cancellation', { params: { processInstanceKey: key }, body: {}, tenant: 'none' });
|
|
149
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'cancellation is acknowledged once the command is written');
|
|
150
|
+
await (0, eventually_1.eventually)(async () => {
|
|
151
|
+
const search = await api.post('/process-instances/search', {
|
|
152
|
+
body: { filter: { processInstanceKey: key } },
|
|
153
|
+
});
|
|
154
|
+
const page = await (0, assertions_1.assertPaginated)(search, { exactItems: 1 });
|
|
155
|
+
(0, test_1.expect)(page.items[0].state).toBe('TERMINATED');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
(0, api_1.test)('updates instance variables through modification', async ({ api, resources, }) => {
|
|
159
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
160
|
+
const instance = await startInstance(api, processId);
|
|
161
|
+
const key = instance.processInstanceKey;
|
|
162
|
+
resources.track('processInstance', key);
|
|
163
|
+
const response = await api.post('/process-instances/{processInstanceKey}/modification', {
|
|
164
|
+
params: { processInstanceKey: key },
|
|
165
|
+
body: {
|
|
166
|
+
activateInstructions: [
|
|
167
|
+
{
|
|
168
|
+
elementId: 'task',
|
|
169
|
+
variableInstructions: [{ variables: { injected: 'value' } }],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
tenant: 'none',
|
|
174
|
+
});
|
|
175
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'modification is acknowledged once the command is written');
|
|
176
|
+
await (0, eventually_1.eventually)(async () => {
|
|
177
|
+
const variables = await api.post('/variables/search', {
|
|
178
|
+
body: {
|
|
179
|
+
filter: { processInstanceKey: key, name: 'injected' },
|
|
180
|
+
page: { limit: 10 },
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
await (0, assertions_1.assertPaginated)(variables, { minItems: 1 });
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
(0, api_1.test)('rejects starting an instance of an absent process', async ({ api }) => {
|
|
187
|
+
const response = await api.post('/process-instances', {
|
|
188
|
+
body: { processDefinitionId: (0, bpmn_1.uniqueId)('does-not-exist') },
|
|
189
|
+
tenant: 'root',
|
|
190
|
+
});
|
|
191
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
192
|
+
});
|
|
193
|
+
(0, api_1.test)('rejects starting an instance with no process identifier', async ({ api, }) => {
|
|
194
|
+
const response = await api.post('/process-instances', {
|
|
195
|
+
body: { variables: {} },
|
|
196
|
+
tenant: 'root',
|
|
197
|
+
});
|
|
198
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
199
|
+
});
|
|
200
|
+
(0, api_1.test)('rejects a lookup for an absent instance key', async ({ api }) => {
|
|
201
|
+
const response = await api.get('/process-instances/{processInstanceKey}', {
|
|
202
|
+
params: { processInstanceKey: keys_1.ABSENT_KEY },
|
|
203
|
+
});
|
|
204
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
205
|
+
});
|
|
206
|
+
(0, api_1.test)('rejects cancelling an absent instance', async ({ api }) => {
|
|
207
|
+
const response = await api.post('/process-instances/{processInstanceKey}/cancellation', {
|
|
208
|
+
params: { processInstanceKey: keys_1.ABSENT_ROUTABLE_KEY },
|
|
209
|
+
body: {},
|
|
210
|
+
tenant: 'none',
|
|
211
|
+
});
|
|
212
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
213
|
+
});
|
|
214
|
+
(0, api_1.test)('rejects an unauthenticated instance start', async ({ api, resources, }) => {
|
|
215
|
+
api_1.test.skip(capabilities_1.unauthenticatedAccessAllowed !== false, capabilities_1.unauthenticatedAccessAllowed === false
|
|
216
|
+
? ''
|
|
217
|
+
: capabilities_1.unauthenticatedAccessAllowed);
|
|
218
|
+
const { processId } = await deployProcessUnderTest(api, resources);
|
|
219
|
+
const response = await api.post('/process-instances', {
|
|
220
|
+
body: { processDefinitionId: processId },
|
|
221
|
+
tenant: 'root',
|
|
222
|
+
authorization: null,
|
|
223
|
+
});
|
|
224
|
+
await (0, assertions_1.assertUnauthorized)(response);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
api_1.test.describe('Process instance migration', () => {
|
|
228
|
+
(0, api_1.test)('migrates a running instance to a new definition version', async ({ api, resources, }) => {
|
|
229
|
+
const processId = (0, bpmn_1.uniqueId)('migration-api');
|
|
230
|
+
const jobType = (0, bpmn_1.uniqueId)('migration-job');
|
|
231
|
+
const source = await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask(jobType), {
|
|
232
|
+
name: 'version one',
|
|
233
|
+
}));
|
|
234
|
+
const target = await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask(jobType), {
|
|
235
|
+
name: 'version two',
|
|
236
|
+
}));
|
|
237
|
+
(0, test_1.expect)(target.version).toBe(source.version + 1);
|
|
238
|
+
const instance = await startInstance(api, processId);
|
|
239
|
+
const key = instance.processInstanceKey;
|
|
240
|
+
resources.track('processInstance', key);
|
|
241
|
+
const response = await api.post('/process-instances/{processInstanceKey}/migration', {
|
|
242
|
+
params: { processInstanceKey: key },
|
|
243
|
+
body: {
|
|
244
|
+
targetProcessDefinitionKey: target.processDefinitionKey,
|
|
245
|
+
mappingInstructions: [
|
|
246
|
+
{ sourceElementId: 'task', targetElementId: 'task' },
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
tenant: 'none',
|
|
250
|
+
});
|
|
251
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'migration is acknowledged once the command is written');
|
|
252
|
+
await (0, eventually_1.eventually)(async () => {
|
|
253
|
+
const search = await api.post('/process-instances/search', {
|
|
254
|
+
body: { filter: { processInstanceKey: key } },
|
|
255
|
+
});
|
|
256
|
+
const page = await (0, assertions_1.assertPaginated)(search, { exactItems: 1 });
|
|
257
|
+
// The shell suite accepted `200 204 400 500` for this call, so a
|
|
258
|
+
// migration that never happened counted as a pass. Reading the instance
|
|
259
|
+
// back is what proves it did.
|
|
260
|
+
(0, test_1.expect)(String(page.items[0].processDefinitionKey)).toBe(target.processDefinitionKey);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
(0, api_1.test)('rejects migration to an absent target definition', async ({ api, resources, }) => {
|
|
264
|
+
const processId = (0, bpmn_1.uniqueId)('migration-bad-target');
|
|
265
|
+
await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask((0, bpmn_1.uniqueId)('job'))));
|
|
266
|
+
const instance = await startInstance(api, processId);
|
|
267
|
+
const key = instance.processInstanceKey;
|
|
268
|
+
resources.track('processInstance', key);
|
|
269
|
+
const response = await api.post('/process-instances/{processInstanceKey}/migration', {
|
|
270
|
+
params: { processInstanceKey: key },
|
|
271
|
+
body: {
|
|
272
|
+
targetProcessDefinitionKey: keys_1.ABSENT_KEY,
|
|
273
|
+
mappingInstructions: [
|
|
274
|
+
{ sourceElementId: 'task', targetElementId: 'task' },
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
tenant: 'none',
|
|
278
|
+
});
|
|
279
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
api_1.test.describe('Call activity migration', () => {
|
|
283
|
+
/**
|
|
284
|
+
* Regression cover for the 8.9 NPE: migrating an instance whose active
|
|
285
|
+
* element is a call activity used to fail inside the engine rather than
|
|
286
|
+
* returning a result. The shell suite's version of this accepted
|
|
287
|
+
* `200 204 400 500`, which would have passed on the very 500 the test was
|
|
288
|
+
* written to catch.
|
|
289
|
+
*/
|
|
290
|
+
(0, api_1.test)('migrates an instance waiting at a call activity', async ({ api, resources, }) => {
|
|
291
|
+
const childId = (0, bpmn_1.uniqueId)('migration-child');
|
|
292
|
+
const parentId = (0, bpmn_1.uniqueId)('migration-parent');
|
|
293
|
+
await (0, deployments_1.deployProcess)(api, resources, childId, (0, bpmn_1.linearProcess)(childId, bpmn_1.element.serviceTask((0, bpmn_1.uniqueId)('child-job'))));
|
|
294
|
+
const parentV1 = await (0, deployments_1.deployProcess)(api, resources, parentId, (0, bpmn_1.linearProcess)(parentId, bpmn_1.element.callActivity(childId), {
|
|
295
|
+
name: 'parent v1',
|
|
296
|
+
}));
|
|
297
|
+
const parentV2 = await (0, deployments_1.deployProcess)(api, resources, parentId, (0, bpmn_1.linearProcess)(parentId, bpmn_1.element.callActivity(childId), {
|
|
298
|
+
name: 'parent v2',
|
|
299
|
+
}));
|
|
300
|
+
(0, test_1.expect)(parentV2.version).toBe(parentV1.version + 1);
|
|
301
|
+
const instance = await startInstance(api, parentId);
|
|
302
|
+
const key = instance.processInstanceKey;
|
|
303
|
+
resources.track('processInstance', key);
|
|
304
|
+
// The call activity must be active before migrating, otherwise the test
|
|
305
|
+
// exercises an instance that has not reached the element under test.
|
|
306
|
+
await (0, eventually_1.eventually)(async () => {
|
|
307
|
+
const elements = await api.post('/element-instances/search', {
|
|
308
|
+
body: {
|
|
309
|
+
filter: { processInstanceKey: key, elementId: 'task' },
|
|
310
|
+
page: { limit: 10 },
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
await (0, assertions_1.assertPaginated)(elements, { minItems: 1 });
|
|
314
|
+
});
|
|
315
|
+
const response = await api.post('/process-instances/{processInstanceKey}/migration', {
|
|
316
|
+
params: { processInstanceKey: key },
|
|
317
|
+
body: {
|
|
318
|
+
targetProcessDefinitionKey: parentV2.processDefinitionKey,
|
|
319
|
+
mappingInstructions: [
|
|
320
|
+
{ sourceElementId: 'task', targetElementId: 'task' },
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
tenant: 'none',
|
|
324
|
+
});
|
|
325
|
+
(0, test_1.expect)(response.status(), 'migrating at a call activity must not fault the engine').not.toBe(500);
|
|
326
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'migration is acknowledged once the command is written');
|
|
327
|
+
await (0, eventually_1.eventually)(async () => {
|
|
328
|
+
const search = await api.post('/process-instances/search', {
|
|
329
|
+
body: { filter: { processInstanceKey: key } },
|
|
330
|
+
});
|
|
331
|
+
const page = await (0, assertions_1.assertPaginated)(search, { exactItems: 1 });
|
|
332
|
+
(0, test_1.expect)(String(page.items[0].processDefinitionKey)).toBe(parentV2.processDefinitionKey);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
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
|
+
async function seed(api, resources, count) {
|
|
10
|
+
const processId = (0, bpmn_1.uniqueId)('batch-mod');
|
|
11
|
+
const jobType = (0, bpmn_1.uniqueId)('batch-mod-job');
|
|
12
|
+
await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask(jobType)));
|
|
13
|
+
const instanceKeys = [];
|
|
14
|
+
for (let i = 0; i < count; i++) {
|
|
15
|
+
const response = await api.post('/process-instances', {
|
|
16
|
+
body: { processDefinitionId: processId },
|
|
17
|
+
tenant: 'root',
|
|
18
|
+
});
|
|
19
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'instance creation is synchronous');
|
|
20
|
+
const json = (await response.json());
|
|
21
|
+
instanceKeys.push(String(json.processInstanceKey));
|
|
22
|
+
}
|
|
23
|
+
await (0, eventually_1.eventually)(async () => {
|
|
24
|
+
const search = await api.post('/process-instances/search', {
|
|
25
|
+
body: { filter: { processDefinitionId: processId }, page: { limit: 50 } },
|
|
26
|
+
});
|
|
27
|
+
await (0, assertions_1.assertPaginated)(search, { minItems: count });
|
|
28
|
+
});
|
|
29
|
+
return { processId, jobType, instanceKeys };
|
|
30
|
+
}
|
|
31
|
+
api_1.test.describe('Batch process-instance modification', () => {
|
|
32
|
+
(0, api_1.test)('creates a modification batch scoped to a definition', async ({ api, resources, }) => {
|
|
33
|
+
const { processId, instanceKeys } = await seed(api, resources, 2);
|
|
34
|
+
instanceKeys.forEach((key) => resources.track('processInstance', key));
|
|
35
|
+
const response = await api.post('/process-instances/modification', {
|
|
36
|
+
body: {
|
|
37
|
+
filter: { processDefinitionId: processId },
|
|
38
|
+
moveInstructions: [{ sourceElementId: 'task', targetElementId: 'task' }],
|
|
39
|
+
},
|
|
40
|
+
tenant: 'filter',
|
|
41
|
+
});
|
|
42
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'the batch is accepted and its key returned synchronously');
|
|
43
|
+
const json = (await response.json());
|
|
44
|
+
(0, assertions_1.assertRequiredFields)(json, ['batchOperationKey']);
|
|
45
|
+
});
|
|
46
|
+
(0, api_1.test)('rejects a modification batch with an empty filter', async ({ api }) => {
|
|
47
|
+
const response = await api.post('/process-instances/modification', {
|
|
48
|
+
body: {
|
|
49
|
+
filter: {},
|
|
50
|
+
moveInstructions: [{ sourceElementId: 'a', targetElementId: 'b' }],
|
|
51
|
+
},
|
|
52
|
+
tenant: 'none',
|
|
53
|
+
});
|
|
54
|
+
// An unscoped batch would modify every instance on the cluster.
|
|
55
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
api_1.test.describe('Batch incident resolution', () => {
|
|
59
|
+
(0, api_1.test)('resolves the incidents of a definition through a batch', async ({ api, resources, }) => {
|
|
60
|
+
const { processId, jobType, instanceKeys } = await seed(api, resources, 2);
|
|
61
|
+
instanceKeys.forEach((key) => resources.track('processInstance', key));
|
|
62
|
+
// Drive both jobs to zero retries so each instance carries an incident.
|
|
63
|
+
const jobs = await (0, eventually_1.eventuallyGet)(async () => {
|
|
64
|
+
const response = await api.post('/jobs/activation', {
|
|
65
|
+
body: {
|
|
66
|
+
type: jobType,
|
|
67
|
+
worker: 'api-v2-suite',
|
|
68
|
+
maxJobsToActivate: 2,
|
|
69
|
+
timeout: 60000,
|
|
70
|
+
},
|
|
71
|
+
tenant: 'tenantIds',
|
|
72
|
+
});
|
|
73
|
+
const json = (await response.json());
|
|
74
|
+
return json.jobs ?? [];
|
|
75
|
+
}, (found) => found.length >= 2, { description: `jobs of type ${jobType}` });
|
|
76
|
+
for (const job of jobs) {
|
|
77
|
+
await api.post('/jobs/{jobKey}/failure', {
|
|
78
|
+
params: { jobKey: String(job.jobKey) },
|
|
79
|
+
body: { errorMessage: 'exhausted', retries: 0, retryBackOff: 0 },
|
|
80
|
+
tenant: 'none',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
await (0, eventually_1.eventually)(async () => {
|
|
84
|
+
const search = await api.post('/incidents/search', {
|
|
85
|
+
body: {
|
|
86
|
+
filter: { processDefinitionId: processId, state: 'ACTIVE' },
|
|
87
|
+
page: { limit: 20 },
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
await (0, assertions_1.assertPaginated)(search, { minItems: 2 });
|
|
91
|
+
});
|
|
92
|
+
const response = await api.post('/process-instances/incident-resolution', {
|
|
93
|
+
body: { filter: { processDefinitionId: processId } },
|
|
94
|
+
tenant: 'filter',
|
|
95
|
+
});
|
|
96
|
+
await (0, assertions_1.assertStatusIn)(response, [200], 'the batch is accepted and its key returned synchronously');
|
|
97
|
+
const json = (await response.json());
|
|
98
|
+
(0, assertions_1.assertRequiredFields)(json, ['batchOperationKey']);
|
|
99
|
+
// The batch retries each failed element. With no retries left on the jobs
|
|
100
|
+
// the incidents return, so the assertion here is that the batch itself ran
|
|
101
|
+
// to a terminal state rather than that the incidents disappeared.
|
|
102
|
+
// A batch is scheduled cluster-wide and works through its items, so it
|
|
103
|
+
// takes materially longer than an index refresh — the search-tuned
|
|
104
|
+
// default left it still in CREATED. The assertion names the state it got
|
|
105
|
+
// stuck in, so a batch that never starts is distinguishable from one that
|
|
106
|
+
// is merely slow.
|
|
107
|
+
await (0, eventually_1.eventually)(async () => {
|
|
108
|
+
const lookup = await api.get('/batch-operations/{batchOperationKey}', {
|
|
109
|
+
params: { batchOperationKey: String(json.batchOperationKey) },
|
|
110
|
+
tenant: 'none',
|
|
111
|
+
});
|
|
112
|
+
const batch = (await lookup.json());
|
|
113
|
+
(0, test_1.expect)(['COMPLETED', 'PARTIALLY_COMPLETED'], `batch ${String(json.batchOperationKey)} reached ${String(batch.state)} rather than a terminal state`).toContain(batch.state);
|
|
114
|
+
}, { timeout: 180000 });
|
|
115
|
+
});
|
|
116
|
+
(0, api_1.test)('rejects an incident-resolution batch with an empty filter', async ({ api, }) => {
|
|
117
|
+
const response = await api.post('/process-instances/incident-resolution', {
|
|
118
|
+
body: { filter: {} },
|
|
119
|
+
tenant: 'none',
|
|
120
|
+
});
|
|
121
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|