@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.
Files changed (85) hide show
  1. package/dist/fixtures/api.d.ts +15 -0
  2. package/dist/fixtures/api.js +27 -0
  3. package/dist/pages/SM-8.10/NavigationPage.d.ts +3 -2
  4. package/dist/pages/SM-8.10/NavigationPage.js +24 -34
  5. package/dist/pages/SM-8.10/ProcessApplicationPage.js +7 -0
  6. package/dist/tests/api/v2/authorization/authorization-api-tests.spec.d.ts +1 -0
  7. package/dist/tests/api/v2/authorization/authorization-api-tests.spec.js +247 -0
  8. package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.d.ts +18 -0
  9. package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.js +208 -0
  10. package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.d.ts +1 -0
  11. package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.js +264 -0
  12. package/dist/tests/api/v2/cluster/cluster-api-tests.spec.d.ts +1 -0
  13. package/dist/tests/api/v2/cluster/cluster-api-tests.spec.js +86 -0
  14. package/dist/tests/api/v2/decision/decision-api-tests.spec.d.ts +1 -0
  15. package/dist/tests/api/v2/decision/decision-api-tests.spec.js +233 -0
  16. package/dist/tests/api/v2/deployment/deployment-api-tests.spec.d.ts +1 -0
  17. package/dist/tests/api/v2/deployment/deployment-api-tests.spec.js +147 -0
  18. package/dist/tests/api/v2/document/document-api-tests.spec.d.ts +9 -0
  19. package/dist/tests/api/v2/document/document-api-tests.spec.js +111 -0
  20. package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.d.ts +1 -0
  21. package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.js +127 -0
  22. package/dist/tests/api/v2/flows/end-to-end-flows.spec.d.ts +9 -0
  23. package/dist/tests/api/v2/flows/end-to-end-flows.spec.js +204 -0
  24. package/dist/tests/api/v2/form/form-api-tests.spec.d.ts +1 -0
  25. package/dist/tests/api/v2/form/form-api-tests.spec.js +88 -0
  26. package/dist/tests/api/v2/identity/group-api-tests.spec.d.ts +1 -0
  27. package/dist/tests/api/v2/identity/group-api-tests.spec.js +172 -0
  28. package/dist/tests/api/v2/identity/membership-api-tests.spec.d.ts +10 -0
  29. package/dist/tests/api/v2/identity/membership-api-tests.spec.js +164 -0
  30. package/dist/tests/api/v2/identity/role-api-tests.spec.d.ts +1 -0
  31. package/dist/tests/api/v2/identity/role-api-tests.spec.js +212 -0
  32. package/dist/tests/api/v2/identity/tenant-api-tests.spec.d.ts +1 -0
  33. package/dist/tests/api/v2/identity/tenant-api-tests.spec.js +319 -0
  34. package/dist/tests/api/v2/identity/user-api-tests.spec.d.ts +1 -0
  35. package/dist/tests/api/v2/identity/user-api-tests.spec.js +148 -0
  36. package/dist/tests/api/v2/job/job-api-tests.spec.d.ts +1 -0
  37. package/dist/tests/api/v2/job/job-api-tests.spec.js +260 -0
  38. package/dist/tests/api/v2/message/message-api-tests.spec.d.ts +1 -0
  39. package/dist/tests/api/v2/message/message-api-tests.spec.js +219 -0
  40. package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.d.ts +1 -0
  41. package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.js +118 -0
  42. package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.d.ts +1 -0
  43. package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.js +335 -0
  44. package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.d.ts +1 -0
  45. package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.js +123 -0
  46. package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.d.ts +1 -0
  47. package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.js +174 -0
  48. package/dist/tests/api/v2/system/clock-api-tests.spec.d.ts +19 -0
  49. package/dist/tests/api/v2/system/clock-api-tests.spec.js +62 -0
  50. package/dist/tests/api/v2/system/system-api-tests.spec.d.ts +1 -0
  51. package/dist/tests/api/v2/system/system-api-tests.spec.js +183 -0
  52. package/dist/tests/api/v2/user-task/user-task-api-tests.spec.d.ts +1 -0
  53. package/dist/tests/api/v2/user-task/user-task-api-tests.spec.js +258 -0
  54. package/dist/utils/api/assertions.d.ts +50 -0
  55. package/dist/utils/api/assertions.js +142 -0
  56. package/dist/utils/api/auth.d.ts +27 -0
  57. package/dist/utils/api/auth.js +137 -0
  58. package/dist/utils/api/beans.d.ts +46 -0
  59. package/dist/utils/api/beans.js +87 -0
  60. package/dist/utils/api/bpmn.d.ts +50 -0
  61. package/dist/utils/api/bpmn.js +217 -0
  62. package/dist/utils/api/capabilities.d.ts +90 -0
  63. package/dist/utils/api/capabilities.js +198 -0
  64. package/dist/utils/api/cleanup.d.ts +24 -0
  65. package/dist/utils/api/cleanup.js +120 -0
  66. package/dist/utils/api/config.d.ts +103 -0
  67. package/dist/utils/api/config.js +62 -0
  68. package/dist/utils/api/deployments.d.ts +45 -0
  69. package/dist/utils/api/deployments.js +120 -0
  70. package/dist/utils/api/eventually.d.ts +36 -0
  71. package/dist/utils/api/eventually.js +54 -0
  72. package/dist/utils/api/http.d.ts +56 -0
  73. package/dist/utils/api/http.js +244 -0
  74. package/dist/utils/api/identity.d.ts +29 -0
  75. package/dist/utils/api/identity.js +43 -0
  76. package/dist/utils/api/keys.d.ts +37 -0
  77. package/dist/utils/api/keys.js +40 -0
  78. package/dist/utils/api/paths.d.ts +25 -0
  79. package/dist/utils/api/paths.js +167 -0
  80. package/dist/utils/api/tenancy.d.ts +31 -0
  81. package/dist/utils/api/tenancy.js +65 -0
  82. package/package.json +1 -1
  83. package/playwright.config.ts +14 -2
  84. package/dist/resources/test-api-v2-complete.sh +0 -12246
  85. package/resources/test-api-v2-complete.sh +0 -12246
@@ -0,0 +1,174 @@
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
+ async function startWithVariables(api, resources, variables) {
11
+ const processId = (0, bpmn_1.uniqueId)('variable-api');
12
+ await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask((0, bpmn_1.uniqueId)('job'))));
13
+ const start = await api.post('/process-instances', {
14
+ body: { processDefinitionId: processId, variables },
15
+ tenant: 'root',
16
+ });
17
+ await (0, assertions_1.assertStatusIn)(start, [200], 'instance creation is synchronous');
18
+ const instance = (await start.json());
19
+ return String(instance.processInstanceKey);
20
+ }
21
+ api_1.test.describe('Variables', () => {
22
+ (0, api_1.test)('finds a variable set at instance start', async ({ api, resources }) => {
23
+ const processInstanceKey = await startWithVariables(api, resources, {
24
+ customerId: 'cust-1',
25
+ });
26
+ resources.track('processInstance', processInstanceKey);
27
+ await (0, eventually_1.eventually)(async () => {
28
+ const response = await api.post('/variables/search', {
29
+ body: {
30
+ filter: { processInstanceKey, name: 'customerId' },
31
+ page: { limit: 10 },
32
+ },
33
+ });
34
+ const page = await (0, assertions_1.assertPaginated)(response, { exactItems: 1 });
35
+ const item = page.items[0];
36
+ (0, test_1.expect)(item.name).toBe('customerId');
37
+ (0, test_1.expect)(item.value).toBe('"cust-1"');
38
+ });
39
+ });
40
+ (0, api_1.test)('returns a variable by key', async ({ api, resources }) => {
41
+ const processInstanceKey = await startWithVariables(api, resources, {
42
+ total: 17,
43
+ });
44
+ resources.track('processInstance', processInstanceKey);
45
+ const items = await (0, eventually_1.eventuallyGet)(async () => {
46
+ const response = await api.post('/variables/search', {
47
+ body: {
48
+ filter: { processInstanceKey, name: 'total' },
49
+ page: { limit: 10 },
50
+ },
51
+ });
52
+ return (await (0, assertions_1.assertPaginated)(response)).items;
53
+ }, (found) => found.length > 0, { description: 'variables' });
54
+ const response = await api.get('/variables/{variableKey}', {
55
+ params: { variableKey: String(items[0].variableKey) },
56
+ });
57
+ const json = (await (0, assertions_1.assertOk)(response));
58
+ (0, assertions_1.assertRequiredFields)(json, ['variableKey', 'name', 'value', 'scopeKey']);
59
+ (0, test_1.expect)(json.name).toBe('total');
60
+ });
61
+ (0, api_1.test)('rejects a lookup for an absent variable key', async ({ api }) => {
62
+ const response = await api.get('/variables/{variableKey}', {
63
+ params: { variableKey: keys_1.ABSENT_KEY },
64
+ });
65
+ await (0, assertions_1.assertNotFound)(response);
66
+ });
67
+ });
68
+ api_1.test.describe('Incidents', () => {
69
+ /**
70
+ * Drives a service task job to zero retries, which is how the engine is made
71
+ * to raise an incident deterministically. The shell suite instead searched
72
+ * the cluster for whatever incident happened to exist, so its incident tests
73
+ * asserted against unrelated runs and passed with `exactItems: 0`.
74
+ */
75
+ async function raiseIncident(api, resources) {
76
+ const processId = (0, bpmn_1.uniqueId)('incident-api');
77
+ const jobType = (0, bpmn_1.uniqueId)('incident-job');
78
+ await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.serviceTask(jobType)));
79
+ const start = await api.post('/process-instances', {
80
+ body: { processDefinitionId: processId },
81
+ tenant: 'root',
82
+ });
83
+ await (0, assertions_1.assertStatusIn)(start, [200], 'instance creation is synchronous');
84
+ const instance = (await start.json());
85
+ const processInstanceKey = String(instance.processInstanceKey);
86
+ const jobs = await (0, eventually_1.eventuallyGet)(async () => {
87
+ const response = await api.post('/jobs/activation', {
88
+ body: {
89
+ type: jobType,
90
+ worker: 'api-v2-suite',
91
+ maxJobsToActivate: 1,
92
+ timeout: 60000,
93
+ },
94
+ tenant: 'tenantIds',
95
+ });
96
+ const json = (await response.json());
97
+ return json.jobs ?? [];
98
+ }, (found) => found.length > 0, { description: 'activated job' });
99
+ await api.post('/jobs/{jobKey}/failure', {
100
+ params: { jobKey: String(jobs[0].jobKey) },
101
+ body: { errorMessage: 'no retries left', retries: 0, retryBackOff: 0 },
102
+ tenant: 'none',
103
+ });
104
+ const incidents = await (0, eventually_1.eventuallyGet)(async () => {
105
+ const response = await api.post('/incidents/search', {
106
+ body: { filter: { processInstanceKey }, page: { limit: 10 } },
107
+ });
108
+ return (await (0, assertions_1.assertPaginated)(response)).items;
109
+ }, (found) => found.length > 0, { description: 'incidents' });
110
+ return { processInstanceKey, incidentKey: String(incidents[0].incidentKey) };
111
+ }
112
+ (0, api_1.test)('raises an incident for a job with no retries left', async ({ api, resources, }) => {
113
+ const { processInstanceKey, incidentKey } = await raiseIncident(api, resources);
114
+ resources.track('processInstance', processInstanceKey);
115
+ // `PENDING` is a real state in `IncidentStateEnum`, not an error: an
116
+ // incident is exported as PENDING and becomes ACTIVE once the engine has
117
+ // finished raising it. Reading once caught the earlier state.
118
+ await (0, eventually_1.eventually)(async () => {
119
+ const response = await api.get('/incidents/{incidentKey}', {
120
+ params: { incidentKey },
121
+ });
122
+ const incident = (await (0, assertions_1.assertOk)(response));
123
+ (0, assertions_1.assertRequiredFields)(incident, [
124
+ 'incidentKey',
125
+ 'processInstanceKey',
126
+ 'errorType',
127
+ 'state',
128
+ ]);
129
+ (0, test_1.expect)(incident.state).toBe('ACTIVE');
130
+ (0, test_1.expect)(String(incident.processInstanceKey)).toBe(processInstanceKey);
131
+ });
132
+ });
133
+ (0, api_1.test)('resolves an incident once its job has retries again', async ({ api, resources, }) => {
134
+ const { processInstanceKey, incidentKey } = await raiseIncident(api, resources);
135
+ resources.track('processInstance', processInstanceKey);
136
+ const incident = (await (await api.get('/incidents/{incidentKey}', { params: { incidentKey } })).json());
137
+ // Resolution retries the element, so the job needs retries first —
138
+ // otherwise the engine immediately raises the incident again.
139
+ await api.patch('/jobs/{jobKey}', {
140
+ params: { jobKey: String(incident.jobKey) },
141
+ body: { changeset: { retries: 3 } },
142
+ tenant: 'none',
143
+ });
144
+ const response = await api.post('/incidents/{incidentKey}/resolution', {
145
+ params: { incidentKey },
146
+ body: {},
147
+ tenant: 'none',
148
+ });
149
+ await (0, assertions_1.assertStatusIn)(response, [204], 'resolution is acknowledged once the command is written');
150
+ await (0, eventually_1.eventually)(async () => {
151
+ const search = await api.post('/incidents/search', {
152
+ body: {
153
+ filter: { processInstanceKey, state: 'ACTIVE' },
154
+ page: { limit: 10 },
155
+ },
156
+ });
157
+ await (0, assertions_1.assertPaginated)(search, { exactItems: 0 });
158
+ });
159
+ });
160
+ (0, api_1.test)('rejects a lookup for an absent incident key', async ({ api }) => {
161
+ const response = await api.get('/incidents/{incidentKey}', {
162
+ params: { incidentKey: keys_1.ABSENT_KEY },
163
+ });
164
+ await (0, assertions_1.assertNotFound)(response);
165
+ });
166
+ (0, api_1.test)('rejects resolving an absent incident', async ({ api }) => {
167
+ const response = await api.post('/incidents/{incidentKey}/resolution', {
168
+ params: { incidentKey: keys_1.ABSENT_ROUTABLE_KEY },
169
+ body: {},
170
+ tenant: 'none',
171
+ });
172
+ await (0, assertions_1.assertNotFound)(response);
173
+ });
174
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Cluster clock endpoints.
3
+ *
4
+ * In its own file because it is its own Playwright project: pinning the clock
5
+ * moves time for **every** caller on the cluster, so these tests cannot share
6
+ * the parallel window with the rest of the suite. See the `clock` project in
7
+ * `playwright.api.config.ts` for why a project is what isolates them and what
8
+ * that costs.
9
+ *
10
+ * `mode: 'serial'` is still needed, but only for the reason it can deliver:
11
+ * ordering the tests *within this file* so two of them do not pin and reset
12
+ * against each other.
13
+ *
14
+ * The shell suite ran these from inside its EXIT trap, after the process exit
15
+ * code had already been computed — so a clock assertion could not fail the
16
+ * run, and a crash before the trap left the cluster clock pinned for whatever
17
+ * ran next.
18
+ */
19
+ export {};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Cluster clock endpoints.
4
+ *
5
+ * In its own file because it is its own Playwright project: pinning the clock
6
+ * moves time for **every** caller on the cluster, so these tests cannot share
7
+ * the parallel window with the rest of the suite. See the `clock` project in
8
+ * `playwright.api.config.ts` for why a project is what isolates them and what
9
+ * that costs.
10
+ *
11
+ * `mode: 'serial'` is still needed, but only for the reason it can deliver:
12
+ * ordering the tests *within this file* so two of them do not pin and reset
13
+ * against each other.
14
+ *
15
+ * The shell suite ran these from inside its EXIT trap, after the process exit
16
+ * code had already been computed — so a clock assertion could not fail the
17
+ * run, and a crash before the trap left the cluster clock pinned for whatever
18
+ * ran next.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ const api_1 = require("../../../../fixtures/api");
22
+ const assertions_1 = require("../../../../utils/api/assertions");
23
+ const PINNED = Date.parse('2029-01-01T00:00:00Z');
24
+ api_1.test.describe('Clock', () => {
25
+ api_1.test.describe.configure({ mode: 'serial' });
26
+ // Asserted, not fire-and-forget: a reset that answers 4xx/5xx leaves the
27
+ // cluster pinned for everything that follows, and an unchecked response
28
+ // would let the run continue and fail somewhere with no connection to the
29
+ // clock.
30
+ api_1.test.afterEach(async ({ api }) => {
31
+ const response = await api.post('/clock/reset', {
32
+ body: {},
33
+ tenant: 'none',
34
+ });
35
+ await (0, assertions_1.assertNoContent)(response);
36
+ });
37
+ (0, api_1.test)('pins the cluster clock to a timestamp', async ({ api }) => {
38
+ const response = await api.put('/clock', {
39
+ body: { timestamp: PINNED },
40
+ tenant: 'none',
41
+ });
42
+ await (0, assertions_1.assertNoContent)(response);
43
+ });
44
+ (0, api_1.test)('resets the cluster clock', async ({ api }) => {
45
+ await api.put('/clock', {
46
+ body: { timestamp: PINNED },
47
+ tenant: 'none',
48
+ });
49
+ const response = await api.post('/clock/reset', {
50
+ body: {},
51
+ tenant: 'none',
52
+ });
53
+ await (0, assertions_1.assertNoContent)(response);
54
+ });
55
+ (0, api_1.test)('rejects a malformed timestamp', async ({ api }) => {
56
+ const response = await api.put('/clock', {
57
+ body: { timestamp: 'not-a-timestamp' },
58
+ tenant: 'none',
59
+ });
60
+ await (0, assertions_1.assertBadRequest)(response);
61
+ });
62
+ });
@@ -0,0 +1,183 @@
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 bpmn_1 = require("../../../../utils/api/bpmn");
7
+ const keys_1 = require("../../../../utils/api/keys");
8
+ const config_1 = require("../../../../utils/api/config");
9
+ const capabilities_1 = require("../../../../utils/api/capabilities");
10
+ api_1.test.describe('Usage metrics', () => {
11
+ const startTime = '2020-01-01T00:00:00.000Z';
12
+ const endTime = '2030-01-01T00:00:00.000Z';
13
+ (0, api_1.test)('reports usage metrics for a time range', async ({ api }) => {
14
+ const response = await api.get('/system/usage-metrics', {
15
+ tenant: 'none',
16
+ query: { startTime, endTime },
17
+ });
18
+ const json = (await (0, assertions_1.assertOk)(response));
19
+ (0, assertions_1.assertRequiredFields)(json, ['processInstances', 'decisionInstances']);
20
+ });
21
+ (0, api_1.test)('rejects a request with no time range', async ({ api }) => {
22
+ const response = await api.get('/system/usage-metrics', { tenant: 'none' });
23
+ await (0, assertions_1.assertBadRequest)(response);
24
+ });
25
+ (0, api_1.test)('rejects a malformed timestamp', async ({ api }) => {
26
+ const response = await api.get('/system/usage-metrics', {
27
+ tenant: 'none',
28
+ query: { startTime: 'not-a-date', endTime },
29
+ });
30
+ await (0, assertions_1.assertBadRequest)(response);
31
+ });
32
+ (0, api_1.test)('rejects a range whose end precedes its start', async ({ api }) => {
33
+ const response = await api.get('/system/usage-metrics', {
34
+ tenant: 'none',
35
+ query: { startTime: endTime, endTime: startTime },
36
+ });
37
+ await (0, assertions_1.assertBadRequest)(response);
38
+ });
39
+ });
40
+ // The Clock tests used to live here. They moved to `clock-api-tests.spec.ts`
41
+ // because they pin cluster-wide state and need their own Playwright project to
42
+ // stay out of the parallel window — `mode: 'serial'` here only ordered them
43
+ // against each other, never against the other files.
44
+ api_1.test.describe('Identity read models', () => {
45
+ (0, api_1.test)('lists tenants', async ({ api }) => {
46
+ const response = await api.post('/tenants/search', {
47
+ body: { filter: {}, page: { limit: 10 } },
48
+ tenant: 'none',
49
+ });
50
+ await (0, assertions_1.assertPaginated)(response);
51
+ });
52
+ (0, api_1.test)('lists roles', async ({ api }) => {
53
+ const response = await api.post('/roles/search', {
54
+ body: { filter: {}, page: { limit: 10 } },
55
+ tenant: 'none',
56
+ });
57
+ await (0, assertions_1.assertPaginated)(response);
58
+ });
59
+ (0, api_1.test)('lists groups', async ({ api }) => {
60
+ const response = await api.post('/groups/search', {
61
+ body: { filter: {}, page: { limit: 10 } },
62
+ tenant: 'none',
63
+ });
64
+ await (0, assertions_1.assertPaginated)(response);
65
+ });
66
+ (0, api_1.test)('lists mapping rules', async ({ api }) => {
67
+ const response = await api.post('/mapping-rules/search', {
68
+ body: { filter: {}, page: { limit: 10 } },
69
+ tenant: 'none',
70
+ });
71
+ await (0, assertions_1.assertPaginated)(response);
72
+ });
73
+ (0, api_1.test)('lists users', async ({ api }) => {
74
+ api_1.test.skip(capabilities_1.usersApiUnavailable !== false, capabilities_1.usersApiUnavailable === false ? '' : capabilities_1.usersApiUnavailable);
75
+ const response = await api.post('/users/search', {
76
+ body: { filter: {}, page: { limit: 10 } },
77
+ tenant: 'none',
78
+ });
79
+ await (0, assertions_1.assertPaginated)(response);
80
+ });
81
+ });
82
+ /**
83
+ * Holds the capability gate used across the identity specs to its word.
84
+ *
85
+ * Whichever way the cluster is configured, exactly one of these two runs and
86
+ * asserts what that configuration means for `/v2/users/**`. If the gate were
87
+ * ever wrong — a cluster that does serve the API but is read as OIDC — the
88
+ * matching test here fails, so a mis-set gate costs a red run rather than
89
+ * silently removing the user coverage it controls.
90
+ */
91
+ api_1.test.describe('Users API availability', () => {
92
+ (0, api_1.test)('is closed with a stated reason when the cluster is in OIDC mode', async ({ api, }) => {
93
+ api_1.test.skip(capabilities_1.usersApiUnavailable === false, 'the cluster serves the Users API');
94
+ const response = await api.post('/users/search', {
95
+ body: { filter: {}, page: { limit: 10 } },
96
+ tenant: 'none',
97
+ });
98
+ await (0, assertions_1.assertForbidden)(response, /users api is disabled/i);
99
+ });
100
+ (0, api_1.test)('is served when the cluster manages users locally', async ({ api }) => {
101
+ api_1.test.skip(capabilities_1.usersApiUnavailable !== false, 'the cluster is in OIDC mode');
102
+ const response = await api.post('/users/search', {
103
+ body: { filter: {}, page: { limit: 10 } },
104
+ tenant: 'none',
105
+ });
106
+ await (0, assertions_1.assertPaginated)(response);
107
+ });
108
+ });
109
+ /**
110
+ * Reports whether this cluster enforces authorization.
111
+ *
112
+ * The authorization *enforcement* spec needs two things: a second principal
113
+ * that holds no roles, and a cluster that actually checks permissions. It
114
+ * gates on the first. This records the second, because the SM QA clusters do
115
+ * not enforce — `test`, the client this suite authenticates as, is not one of
116
+ * the three admin mappings the chart values initialise, yet nothing refuses
117
+ * it. Without this test that fact lives only in a comment, and a cluster that
118
+ * silently started or stopped enforcing would look identical.
119
+ */
120
+ api_1.test.describe('Authorization enforcement', () => {
121
+ (0, api_1.test)('reports whether the cluster enforces authorization', async ({ api, }, testInfo) => {
122
+ const enabled = await (0, capabilities_1.readAuthorizationsEnabled)(api);
123
+ // `enabled` comes back undefined for two unrelated reasons, and naming
124
+ // the wrong one sends the reader somewhere there is nothing to find.
125
+ // Before 8.10 the endpoint is not published at all. From 8.10 it is, but
126
+ // it answers for the *calling principal*, so a cluster that serves /v2
127
+ // without credentials has no principal for it to answer for: on c8Run
128
+ // `/v2/authentication/me` refuses an anonymous caller even though the
129
+ // rest of the tree does not. Either way enforcement being unreadable is
130
+ // not the same as enforcement being off.
131
+ api_1.test.skip(enabled === undefined, (0, config_1.atLeastVersion)('8.10')
132
+ ? 'this cluster does not authenticate the caller, so ' +
133
+ '/v2/authentication/me/authorizations/search has no principal to ' +
134
+ 'report for — the enforcement state cannot be read here, which ' +
135
+ 'leaves this deployment without the safety net this test is (see ' +
136
+ 'tests/api/README.md)'
137
+ : 'the cluster does not publish ' +
138
+ '/v2/authentication/me/authorizations/search (pre-8.10)');
139
+ testInfo.annotations.push({
140
+ type: 'authorization enforcement',
141
+ description: enabled
142
+ ? 'enabled — an unprivileged principal would be refused, so the ' +
143
+ 'enforcement spec is meaningful once one is configured'
144
+ : 'DISABLED — every principal is permitted, so authorization ' +
145
+ 'enforcement cannot be observed on this cluster no matter what ' +
146
+ 'second principal is supplied (see tests/api/README.md)',
147
+ });
148
+ if (!enabled) {
149
+ console.warn('Authorization enforcement is DISABLED on this cluster: every ' +
150
+ 'principal is permitted, so the enforcement spec could not observe ' +
151
+ 'a refusal even with SECONDARY_CLIENT_ID set. See ' +
152
+ 'tests/api/README.md for what closing that gap requires.');
153
+ }
154
+ // Belt and braces: `readAuthorizationsEnabled` already throws on a 200
155
+ // whose `authorizationsEnabled` is not a boolean, and on any status that
156
+ // is neither the answer nor one of the two that mean the cluster cannot
157
+ // give it. This keeps the contract visible in the test that depends on it.
158
+ (0, test_1.expect)(typeof enabled).toBe('boolean');
159
+ });
160
+ });
161
+ api_1.test.describe('Ad-hoc sub-process activities', () => {
162
+ (0, api_1.test)('rejects activating activities on an absent sub-process', async ({ api, }) => {
163
+ const response = await api.post('/element-instances/ad-hoc-activities/{adHocSubProcessInstanceKey}/activation', {
164
+ params: { adHocSubProcessInstanceKey: keys_1.ABSENT_ROUTABLE_KEY },
165
+ body: { elements: [{ elementId: (0, bpmn_1.uniqueId)('activity') }] },
166
+ tenant: 'none',
167
+ });
168
+ await (0, assertions_1.assertStatusIn)(response, [404], 'an absent ad-hoc sub-process instance is reported as not found');
169
+ });
170
+ (0, api_1.test)('rejects an activation request carrying no elements', async ({ api }) => {
171
+ const response = await api.post('/element-instances/ad-hoc-activities/{adHocSubProcessInstanceKey}/activation', {
172
+ params: { adHocSubProcessInstanceKey: keys_1.ABSENT_ROUTABLE_KEY },
173
+ body: {},
174
+ tenant: 'none',
175
+ });
176
+ // Validation precedes the existence check, so this is a 400 rather than
177
+ // the 404 above.
178
+ (0, test_1.expect)([400, 404]).toContain(response.status());
179
+ if (response.status() === 400) {
180
+ await (0, assertions_1.assertBadRequest)(response);
181
+ }
182
+ });
183
+ });