@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,258 @@
|
|
|
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
|
+
/** Starts a process holding one Zeebe user task and returns that task's key. */
|
|
12
|
+
async function startUserTask(api, resources) {
|
|
13
|
+
const processId = (0, bpmn_1.uniqueId)('user-task-api');
|
|
14
|
+
await (0, deployments_1.deployProcess)(api, resources, processId, (0, bpmn_1.linearProcess)(processId, bpmn_1.element.userTask()));
|
|
15
|
+
const start = await api.post('/process-instances', {
|
|
16
|
+
body: { processDefinitionId: processId },
|
|
17
|
+
tenant: 'root',
|
|
18
|
+
});
|
|
19
|
+
await (0, assertions_1.assertStatusIn)(start, [200], 'instance creation is synchronous');
|
|
20
|
+
const instance = (await start.json());
|
|
21
|
+
const processInstanceKey = String(instance.processInstanceKey);
|
|
22
|
+
let tasks;
|
|
23
|
+
try {
|
|
24
|
+
tasks = await (0, eventually_1.eventuallyGet)(async () => {
|
|
25
|
+
const response = await api.post('/user-tasks/search', {
|
|
26
|
+
body: { filter: { processInstanceKey }, page: { limit: 10 } },
|
|
27
|
+
});
|
|
28
|
+
const page = await (0, assertions_1.assertPaginated)(response);
|
|
29
|
+
return page.items;
|
|
30
|
+
}, (items) => items.length > 0, { description: `user tasks for instance ${processInstanceKey}` });
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// An empty result cannot say *why* it is empty, and that is the whole
|
|
34
|
+
// question when this fails: did the engine never create the task (our
|
|
35
|
+
// model is wrong), or did it create one that was never exported (the
|
|
36
|
+
// cluster is wrong)? The element instances answer it — they come from a
|
|
37
|
+
// different index, so a `task` element sitting in the instance while the
|
|
38
|
+
// user-task search stays empty means the record exists and only its
|
|
39
|
+
// export is missing.
|
|
40
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}\n` +
|
|
41
|
+
(await describeInstance(api, processInstanceKey, processId)));
|
|
42
|
+
}
|
|
43
|
+
return { userTaskKey: String(tasks[0].userTaskKey), processInstanceKey };
|
|
44
|
+
}
|
|
45
|
+
/** Where the instance actually got to, for the failure message above. */
|
|
46
|
+
async function describeInstance(api, processInstanceKey, processId) {
|
|
47
|
+
try {
|
|
48
|
+
const instances = await api.post('/process-instances/search', {
|
|
49
|
+
body: { filter: { processInstanceKey } },
|
|
50
|
+
});
|
|
51
|
+
const elements = await api.post('/element-instances/search', {
|
|
52
|
+
body: { filter: { processInstanceKey }, page: { limit: 20 } },
|
|
53
|
+
});
|
|
54
|
+
// The definition was deployed moments before the instance started, so it
|
|
55
|
+
// is the control: both travel the same exporter to the same secondary
|
|
56
|
+
// storage. A definition that arrived while its instance did not is a
|
|
57
|
+
// fault specific to this instance; neither arriving means export was
|
|
58
|
+
// behind or stopped for everything, which is a cluster fault rather than
|
|
59
|
+
// anything these tests can assert around. Run 35696766504 failed 12 tests
|
|
60
|
+
// without recording which of the two it was.
|
|
61
|
+
const definitions = await api.post('/process-definitions/search', {
|
|
62
|
+
body: { filter: { processDefinitionId: processId }, page: { limit: 1 } },
|
|
63
|
+
});
|
|
64
|
+
const instanceJson = (await instances.json());
|
|
65
|
+
const elementJson = (await elements.json());
|
|
66
|
+
const definitionJson = (await definitions.json());
|
|
67
|
+
const state = instanceJson.items?.[0]?.state ?? '<no instance found>';
|
|
68
|
+
const active = (elementJson.items ?? []).map((item) => `${item.elementId}:${item.type}=${item.state}`);
|
|
69
|
+
const exported = (definitionJson.items ?? []).length > 0;
|
|
70
|
+
return (`Instance ${processInstanceKey} state=${state}; ` +
|
|
71
|
+
`element instances: ${active.length > 0 ? active.join(', ') : '<none>'}; ` +
|
|
72
|
+
`its definition ${processId} ` +
|
|
73
|
+
`${exported
|
|
74
|
+
? 'DID reach secondary storage (export is live, so this instance is the fault)'
|
|
75
|
+
: 'did NOT reach secondary storage either (export is behind or stopped cluster-wide)'}`);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return `Could not describe instance ${processInstanceKey}: ${error instanceof Error ? error.message : String(error)}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
api_1.test.describe('User tasks', () => {
|
|
82
|
+
(0, api_1.test)('finds the task created by a running instance', async ({ api, resources, }) => {
|
|
83
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
84
|
+
resources.track('processInstance', processInstanceKey);
|
|
85
|
+
const response = await api.post('/user-tasks/search', {
|
|
86
|
+
body: { filter: { processInstanceKey } },
|
|
87
|
+
});
|
|
88
|
+
const page = await (0, assertions_1.assertPaginated)(response, { exactItems: 1 });
|
|
89
|
+
const task = page.items[0];
|
|
90
|
+
(0, test_1.expect)(String(task.userTaskKey)).toBe(userTaskKey);
|
|
91
|
+
(0, test_1.expect)(task.state).toBe('CREATED');
|
|
92
|
+
});
|
|
93
|
+
(0, api_1.test)('returns a task by key', async ({ api, resources }) => {
|
|
94
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
95
|
+
resources.track('processInstance', processInstanceKey);
|
|
96
|
+
const response = await api.get('/user-tasks/{userTaskKey}', {
|
|
97
|
+
params: { userTaskKey },
|
|
98
|
+
});
|
|
99
|
+
const task = (await (0, assertions_1.assertOk)(response));
|
|
100
|
+
(0, assertions_1.assertRequiredFields)(task, ['userTaskKey', 'state', 'elementId']);
|
|
101
|
+
(0, test_1.expect)(task.elementId).toBe('task');
|
|
102
|
+
(0, test_1.expect)(task.state).toBe('CREATED');
|
|
103
|
+
});
|
|
104
|
+
(0, api_1.test)('assigns a task to a user', async ({ api, resources }) => {
|
|
105
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
106
|
+
resources.track('processInstance', processInstanceKey);
|
|
107
|
+
const response = await api.post('/user-tasks/{userTaskKey}/assignment', {
|
|
108
|
+
params: { userTaskKey },
|
|
109
|
+
body: { assignee: 'demo', allowOverride: true },
|
|
110
|
+
tenant: 'none',
|
|
111
|
+
});
|
|
112
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'assignment is acknowledged');
|
|
113
|
+
await (0, eventually_1.eventually)(async () => {
|
|
114
|
+
const lookup = await api.get('/user-tasks/{userTaskKey}', {
|
|
115
|
+
params: { userTaskKey },
|
|
116
|
+
});
|
|
117
|
+
const task = (await (0, assertions_1.assertOk)(lookup));
|
|
118
|
+
// The shell suite asserted the 204 and moved on, so an assignment that
|
|
119
|
+
// was accepted but never applied counted as a pass.
|
|
120
|
+
(0, test_1.expect)(task.assignee).toBe('demo');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
(0, api_1.test)('unassigns an assigned task', async ({ api, resources }) => {
|
|
124
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
125
|
+
resources.track('processInstance', processInstanceKey);
|
|
126
|
+
await api.post('/user-tasks/{userTaskKey}/assignment', {
|
|
127
|
+
params: { userTaskKey },
|
|
128
|
+
body: { assignee: 'demo', allowOverride: true },
|
|
129
|
+
tenant: 'none',
|
|
130
|
+
});
|
|
131
|
+
const response = await api.delete('/user-tasks/{userTaskKey}/assignee', {
|
|
132
|
+
params: { userTaskKey },
|
|
133
|
+
tenant: 'none',
|
|
134
|
+
});
|
|
135
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'unassignment is acknowledged');
|
|
136
|
+
await (0, eventually_1.eventually)(async () => {
|
|
137
|
+
const lookup = await api.get('/user-tasks/{userTaskKey}', {
|
|
138
|
+
params: { userTaskKey },
|
|
139
|
+
});
|
|
140
|
+
const task = (await (0, assertions_1.assertOk)(lookup));
|
|
141
|
+
(0, test_1.expect)(task.assignee ?? null).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
(0, api_1.test)('updates a task through a patch', async ({ api, resources }) => {
|
|
145
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
146
|
+
resources.track('processInstance', processInstanceKey);
|
|
147
|
+
const response = await api.patch('/user-tasks/{userTaskKey}', {
|
|
148
|
+
params: { userTaskKey },
|
|
149
|
+
body: { changeset: { priority: 75 } },
|
|
150
|
+
tenant: 'none',
|
|
151
|
+
});
|
|
152
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'the patch is acknowledged');
|
|
153
|
+
await (0, eventually_1.eventually)(async () => {
|
|
154
|
+
const lookup = await api.get('/user-tasks/{userTaskKey}', {
|
|
155
|
+
params: { userTaskKey },
|
|
156
|
+
});
|
|
157
|
+
const task = (await (0, assertions_1.assertOk)(lookup));
|
|
158
|
+
(0, test_1.expect)(task.priority).toBe(75);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
(0, api_1.test)('completes a task and finishes the instance', async ({ api, resources, }) => {
|
|
162
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
163
|
+
const response = await api.post('/user-tasks/{userTaskKey}/completion', {
|
|
164
|
+
params: { userTaskKey },
|
|
165
|
+
body: { variables: { approved: true } },
|
|
166
|
+
tenant: 'none',
|
|
167
|
+
});
|
|
168
|
+
await (0, assertions_1.assertStatusIn)(response, [204], 'completion is acknowledged');
|
|
169
|
+
await (0, eventually_1.eventually)(async () => {
|
|
170
|
+
const search = await api.post('/process-instances/search', {
|
|
171
|
+
body: { filter: { processInstanceKey } },
|
|
172
|
+
});
|
|
173
|
+
const page = await (0, assertions_1.assertPaginated)(search, { exactItems: 1 });
|
|
174
|
+
(0, test_1.expect)(page.items[0].state).toBe('COMPLETED');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
(0, api_1.test)('searches the variables visible to a task', async ({ api, resources }) => {
|
|
178
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
179
|
+
resources.track('processInstance', processInstanceKey);
|
|
180
|
+
const response = await api.post('/user-tasks/{userTaskKey}/variables/search', { params: { userTaskKey }, body: { filter: {} } });
|
|
181
|
+
await (0, assertions_1.assertPaginated)(response);
|
|
182
|
+
});
|
|
183
|
+
(0, api_1.test)('reports no linked form for a task without one', async ({ api, resources, }) => {
|
|
184
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
185
|
+
resources.track('processInstance', processInstanceKey);
|
|
186
|
+
const response = await api.get('/user-tasks/{userTaskKey}/form', {
|
|
187
|
+
params: { userTaskKey },
|
|
188
|
+
});
|
|
189
|
+
await (0, assertions_1.assertStatusIn)(response, [204, 404], 'a task with no linked form reports 204 on 8.8+ and 404 on earlier minors');
|
|
190
|
+
});
|
|
191
|
+
(0, api_1.test)('rejects assigning a completed task', async ({ api, resources }) => {
|
|
192
|
+
const { userTaskKey } = await startUserTask(api, resources);
|
|
193
|
+
await api.post('/user-tasks/{userTaskKey}/completion', {
|
|
194
|
+
params: { userTaskKey },
|
|
195
|
+
body: { variables: {} },
|
|
196
|
+
tenant: 'none',
|
|
197
|
+
});
|
|
198
|
+
await (0, eventually_1.eventually)(async () => {
|
|
199
|
+
const response = await api.post('/user-tasks/{userTaskKey}/assignment', {
|
|
200
|
+
params: { userTaskKey },
|
|
201
|
+
body: { assignee: 'demo', allowOverride: true },
|
|
202
|
+
tenant: 'none',
|
|
203
|
+
});
|
|
204
|
+
await (0, assertions_1.assertStatusIn)(response, [404, 409], 'a completed task is either gone or in an invalid state, depending on minor version');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
(0, api_1.test)('rejects a lookup for an absent task key', async ({ api }) => {
|
|
208
|
+
const response = await api.get('/user-tasks/{userTaskKey}', {
|
|
209
|
+
params: { userTaskKey: keys_1.ABSENT_KEY },
|
|
210
|
+
});
|
|
211
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
212
|
+
});
|
|
213
|
+
(0, api_1.test)('rejects completing an absent task', async ({ api }) => {
|
|
214
|
+
const response = await api.post('/user-tasks/{userTaskKey}/completion', {
|
|
215
|
+
params: { userTaskKey: keys_1.ABSENT_ROUTABLE_KEY },
|
|
216
|
+
body: { variables: {} },
|
|
217
|
+
tenant: 'none',
|
|
218
|
+
});
|
|
219
|
+
await (0, assertions_1.assertNotFound)(response);
|
|
220
|
+
});
|
|
221
|
+
(0, api_1.test)('rejects a patch carrying no changeset', async ({ api, resources }) => {
|
|
222
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
223
|
+
resources.track('processInstance', processInstanceKey);
|
|
224
|
+
const response = await api.patch('/user-tasks/{userTaskKey}', {
|
|
225
|
+
params: { userTaskKey },
|
|
226
|
+
body: {},
|
|
227
|
+
tenant: 'none',
|
|
228
|
+
});
|
|
229
|
+
await (0, assertions_1.assertBadRequest)(response);
|
|
230
|
+
});
|
|
231
|
+
(0, api_1.test)('rejects an unauthenticated task search', async ({ api }) => {
|
|
232
|
+
api_1.test.skip(capabilities_1.unauthenticatedAccessAllowed !== false, capabilities_1.unauthenticatedAccessAllowed === false
|
|
233
|
+
? ''
|
|
234
|
+
: capabilities_1.unauthenticatedAccessAllowed);
|
|
235
|
+
const response = await api.post('/user-tasks/search', {
|
|
236
|
+
body: { filter: {} },
|
|
237
|
+
authorization: null,
|
|
238
|
+
});
|
|
239
|
+
await (0, assertions_1.assertUnauthorized)(response);
|
|
240
|
+
});
|
|
241
|
+
(0, api_1.test)('rejects assigning a task that is already assigned without override', async ({ api, resources, }) => {
|
|
242
|
+
const { userTaskKey, processInstanceKey } = await startUserTask(api, resources);
|
|
243
|
+
resources.track('processInstance', processInstanceKey);
|
|
244
|
+
await api.post('/user-tasks/{userTaskKey}/assignment', {
|
|
245
|
+
params: { userTaskKey },
|
|
246
|
+
body: { assignee: 'demo', allowOverride: true },
|
|
247
|
+
tenant: 'none',
|
|
248
|
+
});
|
|
249
|
+
await (0, eventually_1.eventually)(async () => {
|
|
250
|
+
const response = await api.post('/user-tasks/{userTaskKey}/assignment', {
|
|
251
|
+
params: { userTaskKey },
|
|
252
|
+
body: { assignee: 'someone-else', allowOverride: false },
|
|
253
|
+
tenant: 'none',
|
|
254
|
+
});
|
|
255
|
+
await (0, assertions_1.assertConflict)(response);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion vocabulary for REST v2 responses.
|
|
3
|
+
*
|
|
4
|
+
* Each helper asserts the status *and* the problem body the API documents for
|
|
5
|
+
* it, so a response that is right by accident fails. The legacy shell suite
|
|
6
|
+
* asserted status codes only, and accepted lists such as `200 201 403` that
|
|
7
|
+
* passed whether the call succeeded or was rejected outright.
|
|
8
|
+
*
|
|
9
|
+
* Where a status genuinely varies — across versions, or because an operation is
|
|
10
|
+
* synchronous on one path and accepted-for-later on another — use
|
|
11
|
+
* `assertStatusIn`, which requires the reason in writing at the call site.
|
|
12
|
+
*/
|
|
13
|
+
import { type APIResponse } from '@playwright/test';
|
|
14
|
+
/** Fields every paginated search response carries. */
|
|
15
|
+
export declare const PAGINATED_FIELDS: readonly ["items", "page"];
|
|
16
|
+
export declare function assertStatus(response: APIResponse, expected: number): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Accepts any of `expected`. `reason` documents why the endpoint may answer
|
|
19
|
+
* with more than one status and is printed on failure — it is required so that
|
|
20
|
+
* widening an assertion is a deliberate, reviewable act.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertStatusIn(response: APIResponse, expected: readonly number[], reason: string): Promise<void>;
|
|
23
|
+
export declare function assertRequiredFields(payload: unknown, required: readonly string[]): void;
|
|
24
|
+
export declare function assertEqualsForKeys(payload: unknown, expected: Record<string, unknown>, keys: readonly string[]): void;
|
|
25
|
+
export declare function assertOk(response: APIResponse): Promise<unknown>;
|
|
26
|
+
export declare function assertCreated(response: APIResponse): Promise<unknown>;
|
|
27
|
+
export declare function assertNoContent(response: APIResponse): Promise<void>;
|
|
28
|
+
export declare function assertUnauthorized(response: APIResponse): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* A 403 does not carry one fixed problem title. The authorization layer sends
|
|
31
|
+
* `FORBIDDEN`, while the filter that closes `/v2/users/**` on an OIDC cluster
|
|
32
|
+
* sends `Access issue` — both are the API refusing the call. The status and
|
|
33
|
+
* the detail carry the meaning, so the title is matched loosely and the
|
|
34
|
+
* caller pins the specific refusal through `detail`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertForbidden(response: APIResponse, detail?: string | RegExp): Promise<void>;
|
|
37
|
+
export declare function assertNotFound(response: APIResponse, detail?: string | RegExp): Promise<void>;
|
|
38
|
+
export declare function assertConflict(response: APIResponse, detail?: string | RegExp): Promise<void>;
|
|
39
|
+
export declare function assertBadRequest(response: APIResponse, detail?: string | RegExp, title?: string | RegExp): Promise<void>;
|
|
40
|
+
export declare function assertPaginated(response: APIResponse, options?: {
|
|
41
|
+
minItems?: number;
|
|
42
|
+
exactItems?: number;
|
|
43
|
+
minTotalItems?: number;
|
|
44
|
+
exactTotalItems?: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
items: unknown[];
|
|
47
|
+
page: {
|
|
48
|
+
totalItems: number;
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Assertion vocabulary for REST v2 responses.
|
|
4
|
+
*
|
|
5
|
+
* Each helper asserts the status *and* the problem body the API documents for
|
|
6
|
+
* it, so a response that is right by accident fails. The legacy shell suite
|
|
7
|
+
* asserted status codes only, and accepted lists such as `200 201 403` that
|
|
8
|
+
* passed whether the call succeeded or was rejected outright.
|
|
9
|
+
*
|
|
10
|
+
* Where a status genuinely varies — across versions, or because an operation is
|
|
11
|
+
* synchronous on one path and accepted-for-later on another — use
|
|
12
|
+
* `assertStatusIn`, which requires the reason in writing at the call site.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.assertPaginated = exports.assertBadRequest = exports.assertConflict = exports.assertNotFound = exports.assertForbidden = exports.assertUnauthorized = exports.assertNoContent = exports.assertCreated = exports.assertOk = exports.assertEqualsForKeys = exports.assertRequiredFields = exports.assertStatusIn = exports.assertStatus = exports.PAGINATED_FIELDS = void 0;
|
|
16
|
+
const test_1 = require("@playwright/test");
|
|
17
|
+
/** Fields every RFC 7807 problem body from the API carries. */
|
|
18
|
+
const PROBLEM_FIELDS = ['title', 'detail'];
|
|
19
|
+
/** Fields every paginated search response carries. */
|
|
20
|
+
exports.PAGINATED_FIELDS = ['items', 'page'];
|
|
21
|
+
async function describe(response) {
|
|
22
|
+
const body = await response.text().catch(() => '<unreadable body>');
|
|
23
|
+
return `${response.url()} -> ${response.status()}\n${body}`;
|
|
24
|
+
}
|
|
25
|
+
async function assertStatus(response, expected) {
|
|
26
|
+
if (response.status() !== expected) {
|
|
27
|
+
throw new Error(`Expected status ${expected}, got ${response.status()}.\n${await describe(response)}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.assertStatus = assertStatus;
|
|
31
|
+
/**
|
|
32
|
+
* Accepts any of `expected`. `reason` documents why the endpoint may answer
|
|
33
|
+
* with more than one status and is printed on failure — it is required so that
|
|
34
|
+
* widening an assertion is a deliberate, reviewable act.
|
|
35
|
+
*/
|
|
36
|
+
async function assertStatusIn(response, expected, reason) {
|
|
37
|
+
if (!expected.includes(response.status())) {
|
|
38
|
+
throw new Error(`Expected one of [${expected.join(', ')}] (${reason}), got ${response.status()}.\n${await describe(response)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.assertStatusIn = assertStatusIn;
|
|
42
|
+
function assertRequiredFields(payload, required) {
|
|
43
|
+
(0, test_1.expect)(payload, 'response body should be an object').toBeTruthy();
|
|
44
|
+
const record = payload;
|
|
45
|
+
const missing = required.filter((field) => !Object.prototype.hasOwnProperty.call(record, field) ||
|
|
46
|
+
record[field] === undefined);
|
|
47
|
+
if (missing.length > 0) {
|
|
48
|
+
throw new Error(`Response is missing required field(s): ${missing.join(', ')}\n` +
|
|
49
|
+
`Body: ${JSON.stringify(payload, null, 2)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.assertRequiredFields = assertRequiredFields;
|
|
53
|
+
function assertEqualsForKeys(payload, expected, keys) {
|
|
54
|
+
const record = payload;
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
(0, test_1.expect)(record?.[key], `field "${key}"`).toEqual(expected[key]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.assertEqualsForKeys = assertEqualsForKeys;
|
|
60
|
+
async function assertProblem(response, status, title, detail) {
|
|
61
|
+
await assertStatus(response, status);
|
|
62
|
+
const json = await response.json();
|
|
63
|
+
assertRequiredFields(json, PROBLEM_FIELDS);
|
|
64
|
+
if (typeof title === 'string') {
|
|
65
|
+
(0, test_1.expect)(json.title, `problem title for ${response.url()}`).toBe(title);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
(0, test_1.expect)(String(json.title)).toMatch(title);
|
|
69
|
+
}
|
|
70
|
+
if (detail !== undefined) {
|
|
71
|
+
(0, test_1.expect)(String(json.detail)).toMatch(typeof detail === 'string'
|
|
72
|
+
? new RegExp(detail.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')
|
|
73
|
+
: detail);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function assertOk(response) {
|
|
77
|
+
await assertStatus(response, 200);
|
|
78
|
+
return response.json();
|
|
79
|
+
}
|
|
80
|
+
exports.assertOk = assertOk;
|
|
81
|
+
async function assertCreated(response) {
|
|
82
|
+
await assertStatus(response, 201);
|
|
83
|
+
return response.json();
|
|
84
|
+
}
|
|
85
|
+
exports.assertCreated = assertCreated;
|
|
86
|
+
async function assertNoContent(response) {
|
|
87
|
+
await assertStatus(response, 204);
|
|
88
|
+
}
|
|
89
|
+
exports.assertNoContent = assertNoContent;
|
|
90
|
+
async function assertUnauthorized(response) {
|
|
91
|
+
await assertStatus(response, 401);
|
|
92
|
+
const json = await response.json().catch(() => undefined);
|
|
93
|
+
// 401 may be produced by the ingress or the security filter chain before the
|
|
94
|
+
// API's problem-body writer runs, so only assert the body when there is one.
|
|
95
|
+
if (json) {
|
|
96
|
+
(0, test_1.expect)(String(json.title)).toMatch(/unauthorized/i);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.assertUnauthorized = assertUnauthorized;
|
|
100
|
+
/**
|
|
101
|
+
* A 403 does not carry one fixed problem title. The authorization layer sends
|
|
102
|
+
* `FORBIDDEN`, while the filter that closes `/v2/users/**` on an OIDC cluster
|
|
103
|
+
* sends `Access issue` — both are the API refusing the call. The status and
|
|
104
|
+
* the detail carry the meaning, so the title is matched loosely and the
|
|
105
|
+
* caller pins the specific refusal through `detail`.
|
|
106
|
+
*/
|
|
107
|
+
async function assertForbidden(response, detail) {
|
|
108
|
+
await assertProblem(response, 403, /forbidden|access issue/i, detail);
|
|
109
|
+
}
|
|
110
|
+
exports.assertForbidden = assertForbidden;
|
|
111
|
+
async function assertNotFound(response, detail) {
|
|
112
|
+
await assertProblem(response, 404, /not[_ ]?found/i, detail);
|
|
113
|
+
}
|
|
114
|
+
exports.assertNotFound = assertNotFound;
|
|
115
|
+
async function assertConflict(response, detail) {
|
|
116
|
+
await assertProblem(response, 409, /already[_ ]exists|invalid[_ ]state/i, detail);
|
|
117
|
+
}
|
|
118
|
+
exports.assertConflict = assertConflict;
|
|
119
|
+
async function assertBadRequest(response, detail, title = /invalid[_ ]argument|bad request/i) {
|
|
120
|
+
await assertProblem(response, 400, title, detail);
|
|
121
|
+
}
|
|
122
|
+
exports.assertBadRequest = assertBadRequest;
|
|
123
|
+
async function assertPaginated(response, options = {}) {
|
|
124
|
+
await assertStatus(response, 200);
|
|
125
|
+
const json = await response.json();
|
|
126
|
+
assertRequiredFields(json, exports.PAGINATED_FIELDS);
|
|
127
|
+
(0, test_1.expect)(Array.isArray(json.items), 'items should be an array').toBe(true);
|
|
128
|
+
if (options.minItems !== undefined) {
|
|
129
|
+
(0, test_1.expect)(json.items.length).toBeGreaterThanOrEqual(options.minItems);
|
|
130
|
+
}
|
|
131
|
+
if (options.exactItems !== undefined) {
|
|
132
|
+
(0, test_1.expect)(json.items.length).toBe(options.exactItems);
|
|
133
|
+
}
|
|
134
|
+
if (options.minTotalItems !== undefined) {
|
|
135
|
+
(0, test_1.expect)(json.page.totalItems).toBeGreaterThanOrEqual(options.minTotalItems);
|
|
136
|
+
}
|
|
137
|
+
if (options.exactTotalItems !== undefined) {
|
|
138
|
+
(0, test_1.expect)(json.page.totalItems).toBe(options.exactTotalItems);
|
|
139
|
+
}
|
|
140
|
+
return json;
|
|
141
|
+
}
|
|
142
|
+
exports.assertPaginated = assertPaginated;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication strategies for the Orchestration Cluster REST v2 API.
|
|
3
|
+
*
|
|
4
|
+
* The orchestration-cluster suite in `camunda/camunda` hardcodes Basic auth, so
|
|
5
|
+
* it cannot drive an OIDC-secured cluster. Helm-upgrade runs are OIDC, which is
|
|
6
|
+
* why this layer exists: it resolves an `Authorization` header from whichever
|
|
7
|
+
* strategy the environment selects, and refreshes bearer tokens before they
|
|
8
|
+
* expire so a long suite does not start 401-ing part way through.
|
|
9
|
+
*/
|
|
10
|
+
import type { APIRequestContext } from '@playwright/test';
|
|
11
|
+
import { type ApiConfig } from './config';
|
|
12
|
+
/**
|
|
13
|
+
* The `Authorization` header for the suite's principal, refreshed when stale.
|
|
14
|
+
* Throws rather than returning an unauthenticated header, so a broken token
|
|
15
|
+
* endpoint surfaces as a setup error instead of 300 misleading 401 assertions.
|
|
16
|
+
*/
|
|
17
|
+
export declare function authHeader(request: APIRequestContext, config?: ApiConfig): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* A bearer header for a different OAuth2 client, used by the authorization
|
|
20
|
+
* enforcement tests to act as an under-privileged principal. Returns undefined
|
|
21
|
+
* when the client cannot authenticate, which those tests assert on directly.
|
|
22
|
+
*/
|
|
23
|
+
export declare function clientAuthHeader(request: APIRequestContext, clientId: string, clientSecret: string, config?: ApiConfig): Promise<string | undefined>;
|
|
24
|
+
/** A bearer header obtained via the password grant, for per-user assertions. */
|
|
25
|
+
export declare function userAuthHeader(request: APIRequestContext, username: string, password: string, config?: ApiConfig): Promise<string | undefined>;
|
|
26
|
+
/** Drops cached tokens. Used by tests that deliberately invalidate a principal. */
|
|
27
|
+
export declare function clearTokenCache(): void;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication strategies for the Orchestration Cluster REST v2 API.
|
|
4
|
+
*
|
|
5
|
+
* The orchestration-cluster suite in `camunda/camunda` hardcodes Basic auth, so
|
|
6
|
+
* it cannot drive an OIDC-secured cluster. Helm-upgrade runs are OIDC, which is
|
|
7
|
+
* why this layer exists: it resolves an `Authorization` header from whichever
|
|
8
|
+
* strategy the environment selects, and refreshes bearer tokens before they
|
|
9
|
+
* expire so a long suite does not start 401-ing part way through.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.clearTokenCache = exports.userAuthHeader = exports.clientAuthHeader = exports.authHeader = void 0;
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
/**
|
|
15
|
+
* Re-fetch this long before the server-stated expiry. A request issued at the
|
|
16
|
+
* very edge of a token's lifetime can still arrive expired, and the suite runs
|
|
17
|
+
* long enough for that to be a routine occurrence rather than a rare race.
|
|
18
|
+
*/
|
|
19
|
+
const EXPIRY_MARGIN_MS = 60000;
|
|
20
|
+
/** Fallback lifetime when the token endpoint omits `expires_in`. */
|
|
21
|
+
const DEFAULT_TOKEN_TTL_MS = 300000;
|
|
22
|
+
const tokenCache = new Map();
|
|
23
|
+
function cacheKey(clientId, subject = '') {
|
|
24
|
+
return `${clientId}::${subject}`;
|
|
25
|
+
}
|
|
26
|
+
function basicHeader(username, password) {
|
|
27
|
+
return `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
|
|
28
|
+
}
|
|
29
|
+
async function fetchToken(request, tokenUrl, form) {
|
|
30
|
+
let response;
|
|
31
|
+
try {
|
|
32
|
+
response = await request.post(tokenUrl, {
|
|
33
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
34
|
+
form,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// An unreachable token endpoint is the single most likely reason a whole
|
|
39
|
+
// run fails: on a Helm cluster the identity provider may not be serving
|
|
40
|
+
// yet. Reported raw, it surfaces as a Playwright stack inside auth.ts on
|
|
41
|
+
// every one of the suite's tests, which says nothing about the cause.
|
|
42
|
+
console.error(`Token endpoint ${tokenUrl} is unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
if (!response.ok()) {
|
|
46
|
+
const body = await response.text().catch(() => '<no-body>');
|
|
47
|
+
console.error(`Token request to ${tokenUrl} failed with ${response.status()}: ${body}`);
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const json = (await response.json());
|
|
51
|
+
if (!json.access_token) {
|
|
52
|
+
console.error(`Token response from ${tokenUrl} carried no access_token`);
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const ttl = json.expires_in ? json.expires_in * 1000 : DEFAULT_TOKEN_TTL_MS;
|
|
56
|
+
return {
|
|
57
|
+
header: `Bearer ${json.access_token}`,
|
|
58
|
+
expiresAt: Date.now() + Math.max(ttl - EXPIRY_MARGIN_MS, 0),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function clientCredentialsForm(oauth, clientId, clientSecret) {
|
|
62
|
+
const form = {
|
|
63
|
+
grant_type: 'client_credentials',
|
|
64
|
+
client_id: clientId,
|
|
65
|
+
client_secret: clientSecret,
|
|
66
|
+
};
|
|
67
|
+
if (oauth.audience)
|
|
68
|
+
form.audience = oauth.audience;
|
|
69
|
+
if (oauth.scope)
|
|
70
|
+
form.scope = oauth.scope;
|
|
71
|
+
return form;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The `Authorization` header for the suite's principal, refreshed when stale.
|
|
75
|
+
* Throws rather than returning an unauthenticated header, so a broken token
|
|
76
|
+
* endpoint surfaces as a setup error instead of 300 misleading 401 assertions.
|
|
77
|
+
*/
|
|
78
|
+
async function authHeader(request, config = config_1.apiConfig) {
|
|
79
|
+
if (config.authMethod === 'basic') {
|
|
80
|
+
return basicHeader(config.basic.username, config.basic.password);
|
|
81
|
+
}
|
|
82
|
+
const key = cacheKey(config.oauth.clientId);
|
|
83
|
+
const cached = tokenCache.get(key);
|
|
84
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
85
|
+
return cached.header;
|
|
86
|
+
}
|
|
87
|
+
const token = await fetchToken(request, config.oauth.tokenUrl, clientCredentialsForm(config.oauth, config.oauth.clientId, config.oauth.clientSecret));
|
|
88
|
+
if (!token) {
|
|
89
|
+
throw new Error(`Could not obtain an OAuth2 token for client "${config.oauth.clientId}" from ${config.oauth.tokenUrl}`);
|
|
90
|
+
}
|
|
91
|
+
tokenCache.set(key, token);
|
|
92
|
+
return token.header;
|
|
93
|
+
}
|
|
94
|
+
exports.authHeader = authHeader;
|
|
95
|
+
/**
|
|
96
|
+
* A bearer header for a different OAuth2 client, used by the authorization
|
|
97
|
+
* enforcement tests to act as an under-privileged principal. Returns undefined
|
|
98
|
+
* when the client cannot authenticate, which those tests assert on directly.
|
|
99
|
+
*/
|
|
100
|
+
async function clientAuthHeader(request, clientId, clientSecret, config = config_1.apiConfig) {
|
|
101
|
+
const key = cacheKey(clientId);
|
|
102
|
+
const cached = tokenCache.get(key);
|
|
103
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
104
|
+
return cached.header;
|
|
105
|
+
}
|
|
106
|
+
const token = await fetchToken(request, config.oauth.tokenUrl, clientCredentialsForm(config.oauth, clientId, clientSecret));
|
|
107
|
+
if (!token)
|
|
108
|
+
return undefined;
|
|
109
|
+
tokenCache.set(key, token);
|
|
110
|
+
return token.header;
|
|
111
|
+
}
|
|
112
|
+
exports.clientAuthHeader = clientAuthHeader;
|
|
113
|
+
/** A bearer header obtained via the password grant, for per-user assertions. */
|
|
114
|
+
async function userAuthHeader(request, username, password, config = config_1.apiConfig) {
|
|
115
|
+
const key = cacheKey(config.oauth.clientId, username);
|
|
116
|
+
const cached = tokenCache.get(key);
|
|
117
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
118
|
+
return cached.header;
|
|
119
|
+
}
|
|
120
|
+
const token = await fetchToken(request, config.oauth.tokenUrl, {
|
|
121
|
+
grant_type: 'password',
|
|
122
|
+
client_id: config.oauth.clientId,
|
|
123
|
+
client_secret: config.oauth.clientSecret,
|
|
124
|
+
username,
|
|
125
|
+
password,
|
|
126
|
+
});
|
|
127
|
+
if (!token)
|
|
128
|
+
return undefined;
|
|
129
|
+
tokenCache.set(key, token);
|
|
130
|
+
return token.header;
|
|
131
|
+
}
|
|
132
|
+
exports.userAuthHeader = userAuthHeader;
|
|
133
|
+
/** Drops cached tokens. Used by tests that deliberately invalidate a principal. */
|
|
134
|
+
function clearTokenCache() {
|
|
135
|
+
tokenCache.clear();
|
|
136
|
+
}
|
|
137
|
+
exports.clearTokenCache = clearTokenCache;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request bodies for identity resources.
|
|
3
|
+
*
|
|
4
|
+
* Every factory randomises its identifier. The shell suite seeded ids from
|
|
5
|
+
* `$(date +%s)-$$`, which collides between workers started in the same second
|
|
6
|
+
* and left tests fighting over the same role or group.
|
|
7
|
+
*/
|
|
8
|
+
export declare const roleRequiredFields: readonly ["roleId", "name", "description"];
|
|
9
|
+
export declare const groupRequiredFields: readonly ["groupId", "name"];
|
|
10
|
+
export declare const tenantRequiredFields: readonly ["tenantId", "name"];
|
|
11
|
+
export declare const userRequiredFields: readonly ["username", "name", "email"];
|
|
12
|
+
export declare const mappingRuleRequiredFields: readonly ["mappingRuleId", "claimName", "claimValue", "name"];
|
|
13
|
+
export declare function newRole(overrides?: Record<string, unknown>): {
|
|
14
|
+
roleId: string;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function newGroup(overrides?: Record<string, unknown>): {
|
|
19
|
+
groupId: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function newTenant(overrides?: Record<string, unknown>): {
|
|
24
|
+
tenantId: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
export declare function newUser(overrides?: Record<string, unknown>): {
|
|
29
|
+
username: string;
|
|
30
|
+
name: string;
|
|
31
|
+
email: string;
|
|
32
|
+
password: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function newMappingRule(overrides?: Record<string, unknown>): {
|
|
35
|
+
mappingRuleId: string;
|
|
36
|
+
claimName: string;
|
|
37
|
+
claimValue: string;
|
|
38
|
+
name: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function newAuthorization(overrides?: Record<string, unknown>): {
|
|
41
|
+
ownerId: string;
|
|
42
|
+
ownerType: string;
|
|
43
|
+
resourceId: string;
|
|
44
|
+
resourceType: string;
|
|
45
|
+
permissionTypes: string[];
|
|
46
|
+
};
|