@camunda/e2e-test-suite 0.0.1242 → 0.0.1244
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fixtures/api.d.ts +15 -0
- package/dist/fixtures/api.js +27 -0
- package/dist/pages/SM-8.10/ModelerCreatePage.js +5 -4
- package/dist/tests/api/v2/authorization/authorization-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/authorization/authorization-api-tests.spec.js +247 -0
- package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.d.ts +18 -0
- package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.js +208 -0
- package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.js +264 -0
- package/dist/tests/api/v2/cluster/cluster-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/cluster/cluster-api-tests.spec.js +86 -0
- package/dist/tests/api/v2/decision/decision-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/decision/decision-api-tests.spec.js +233 -0
- package/dist/tests/api/v2/deployment/deployment-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/deployment/deployment-api-tests.spec.js +147 -0
- package/dist/tests/api/v2/document/document-api-tests.spec.d.ts +9 -0
- package/dist/tests/api/v2/document/document-api-tests.spec.js +111 -0
- package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.js +127 -0
- package/dist/tests/api/v2/flows/end-to-end-flows.spec.d.ts +9 -0
- package/dist/tests/api/v2/flows/end-to-end-flows.spec.js +204 -0
- package/dist/tests/api/v2/form/form-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/form/form-api-tests.spec.js +88 -0
- package/dist/tests/api/v2/identity/group-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/group-api-tests.spec.js +172 -0
- package/dist/tests/api/v2/identity/membership-api-tests.spec.d.ts +10 -0
- package/dist/tests/api/v2/identity/membership-api-tests.spec.js +164 -0
- package/dist/tests/api/v2/identity/role-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/role-api-tests.spec.js +212 -0
- package/dist/tests/api/v2/identity/tenant-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/tenant-api-tests.spec.js +319 -0
- package/dist/tests/api/v2/identity/user-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/identity/user-api-tests.spec.js +148 -0
- package/dist/tests/api/v2/job/job-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/job/job-api-tests.spec.js +260 -0
- package/dist/tests/api/v2/message/message-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/message/message-api-tests.spec.js +219 -0
- package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.js +118 -0
- package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.js +335 -0
- package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.js +123 -0
- package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.js +174 -0
- package/dist/tests/api/v2/system/clock-api-tests.spec.d.ts +19 -0
- package/dist/tests/api/v2/system/clock-api-tests.spec.js +62 -0
- package/dist/tests/api/v2/system/system-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/system/system-api-tests.spec.js +183 -0
- package/dist/tests/api/v2/user-task/user-task-api-tests.spec.d.ts +1 -0
- package/dist/tests/api/v2/user-task/user-task-api-tests.spec.js +258 -0
- package/dist/utils/api/assertions.d.ts +50 -0
- package/dist/utils/api/assertions.js +142 -0
- package/dist/utils/api/auth.d.ts +27 -0
- package/dist/utils/api/auth.js +137 -0
- package/dist/utils/api/beans.d.ts +46 -0
- package/dist/utils/api/beans.js +87 -0
- package/dist/utils/api/bpmn.d.ts +50 -0
- package/dist/utils/api/bpmn.js +217 -0
- package/dist/utils/api/capabilities.d.ts +90 -0
- package/dist/utils/api/capabilities.js +198 -0
- package/dist/utils/api/cleanup.d.ts +24 -0
- package/dist/utils/api/cleanup.js +120 -0
- package/dist/utils/api/config.d.ts +103 -0
- package/dist/utils/api/config.js +62 -0
- package/dist/utils/api/deployments.d.ts +45 -0
- package/dist/utils/api/deployments.js +120 -0
- package/dist/utils/api/eventually.d.ts +36 -0
- package/dist/utils/api/eventually.js +54 -0
- package/dist/utils/api/http.d.ts +56 -0
- package/dist/utils/api/http.js +244 -0
- package/dist/utils/api/identity.d.ts +29 -0
- package/dist/utils/api/identity.js +43 -0
- package/dist/utils/api/keys.d.ts +37 -0
- package/dist/utils/api/keys.js +40 -0
- package/dist/utils/api/paths.d.ts +25 -0
- package/dist/utils/api/paths.js +167 -0
- package/dist/utils/api/tenancy.d.ts +31 -0
- package/dist/utils/api/tenancy.js +65 -0
- package/package.json +1 -1
- package/playwright.config.ts +14 -2
- package/dist/resources/test-api-v2-complete.sh +0 -12246
- package/resources/test-api-v2-complete.sh +0 -12246
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment configuration for the Orchestration Cluster REST v2 API suite.
|
|
3
|
+
*
|
|
4
|
+
* The variable names mirror those the legacy `resources/test-api-v2-complete.sh`
|
|
5
|
+
* consumed, so the CI workflows that export them keep working unchanged.
|
|
6
|
+
*/
|
|
7
|
+
export type AuthMethod = 'oauth2' | 'basic';
|
|
8
|
+
export interface OAuthConfig {
|
|
9
|
+
tokenUrl: string;
|
|
10
|
+
clientId: string;
|
|
11
|
+
clientSecret: string;
|
|
12
|
+
audience: string;
|
|
13
|
+
scope: string;
|
|
14
|
+
}
|
|
15
|
+
export interface BasicAuthConfig {
|
|
16
|
+
username: string;
|
|
17
|
+
password: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ApiConfig {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
authMethod: AuthMethod;
|
|
22
|
+
oauth: OAuthConfig;
|
|
23
|
+
basic: BasicAuthConfig;
|
|
24
|
+
/** Multi-tenancy enabled on the cluster under test (the `MT` env var). */
|
|
25
|
+
multiTenancy: boolean;
|
|
26
|
+
/** Tenant applied to every tenant-aware request when `multiTenancy` is on. */
|
|
27
|
+
tenantId: string;
|
|
28
|
+
/** Minor version under test, e.g. `8.8`. Gates version-specific expectations. */
|
|
29
|
+
zeebeVersion: string;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the cluster serves `/v2/users/**` at all.
|
|
32
|
+
*
|
|
33
|
+
* In OIDC mode the identity provider owns the user registry, so the
|
|
34
|
+
* orchestration cluster registers a filter that answers every request under
|
|
35
|
+
* `/v2/users/**` with 403 and the detail "Users API is disabled because the
|
|
36
|
+
* application is configured in OIDC mode" — see `ApiFiltersConfiguration` in
|
|
37
|
+
* camunda/camunda, gated on `@ConditionalOnAuthenticationMethod(OIDC)`.
|
|
38
|
+
* There is no local user to create, so a spec that needs one cannot run.
|
|
39
|
+
*
|
|
40
|
+
* This is read from configuration rather than inferred from a 403 at
|
|
41
|
+
* runtime, deliberately: sniffing the status would also swallow a genuine
|
|
42
|
+
* permission regression, which is the failure this suite exists to catch.
|
|
43
|
+
*/
|
|
44
|
+
usersApiEnabled: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the cluster checks permissions at all (`AUTHORIZATIONS_ENABLED`).
|
|
47
|
+
*
|
|
48
|
+
* An unprivileged principal proves nothing on a cluster that permits
|
|
49
|
+
* everyone: the enforcement tests would fail with "expected 403, got 200",
|
|
50
|
+
* which is the regression they exist to detect rather than a real one. So
|
|
51
|
+
* the gate needs this as well as a principal.
|
|
52
|
+
*
|
|
53
|
+
* Defaults to `true` — assume a cluster enforces unless its workflow says
|
|
54
|
+
* otherwise. The opposite default would silently drop enforcement coverage
|
|
55
|
+
* on every deployment that never sets the variable, and quietly losing the
|
|
56
|
+
* security tests is worse than a loud failure. A deployment that is known
|
|
57
|
+
* not to enforce declares it: `c8Run` serves its whole v2 API
|
|
58
|
+
* unauthenticated, which `tests/c8Run-8.10/api-tests-v2.spec.ts` relies on.
|
|
59
|
+
*
|
|
60
|
+
* `readAuthorizationsEnabled` in `capabilities.ts` reports what the cluster
|
|
61
|
+
* actually answers, so a wrong value here shows up in the run rather than
|
|
62
|
+
* hiding.
|
|
63
|
+
*/
|
|
64
|
+
authorizationsEnabled: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the cluster requires authentication at all
|
|
67
|
+
* (`AUTHENTICATION_REQUIRED`).
|
|
68
|
+
*
|
|
69
|
+
* Distinct from `authorizationsEnabled`, which is about what a *known*
|
|
70
|
+
* principal may do. This is about whether a caller has to be one: c8Run
|
|
71
|
+
* answers its whole `/v2` tree without credentials, which
|
|
72
|
+
* `tests/c8Run-8.10/api-tests-v2.spec.ts` relies on, so a request sent with
|
|
73
|
+
* no `Authorization` header is served normally and the 401 the suite
|
|
74
|
+
* asserts cannot happen.
|
|
75
|
+
*
|
|
76
|
+
* Defaults to `true`, for the same reason as `authorizationsEnabled`: a
|
|
77
|
+
* deployment that authenticates should not have to declare it, and the
|
|
78
|
+
* opposite default would silently drop every unauthenticated-rejection test
|
|
79
|
+
* wherever the variable is unset.
|
|
80
|
+
*/
|
|
81
|
+
authenticationRequired: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* A second OAuth2 client used as the *unprivileged* principal by the
|
|
84
|
+
* authorization enforcement tests.
|
|
85
|
+
*
|
|
86
|
+
* Enforcement can only be observed with a principal other than the suite's
|
|
87
|
+
* own admin. Where local users exist the tests create one; in OIDC mode
|
|
88
|
+
* they cannot, and the cluster's principals are identity-provider clients
|
|
89
|
+
* that this suite has no way to provision. Supplying one here is what lets
|
|
90
|
+
* enforcement run on an OIDC cluster.
|
|
91
|
+
*/
|
|
92
|
+
secondaryClient: {
|
|
93
|
+
clientId: string;
|
|
94
|
+
clientSecret: string;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export declare function loadApiConfig(): ApiConfig;
|
|
98
|
+
export declare const apiConfig: ApiConfig;
|
|
99
|
+
/**
|
|
100
|
+
* True when the cluster runs at or above `minor`, e.g. `atLeastVersion('8.9')`.
|
|
101
|
+
* Used to gate expectations for endpoints that only exist from a given release.
|
|
102
|
+
*/
|
|
103
|
+
export declare function atLeastVersion(minor: string): boolean;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Environment configuration for the Orchestration Cluster REST v2 API suite.
|
|
4
|
+
*
|
|
5
|
+
* The variable names mirror those the legacy `resources/test-api-v2-complete.sh`
|
|
6
|
+
* consumed, so the CI workflows that export them keep working unchanged.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.atLeastVersion = exports.apiConfig = exports.loadApiConfig = void 0;
|
|
10
|
+
function env(name, fallback = '') {
|
|
11
|
+
const value = process.env[name];
|
|
12
|
+
return value === undefined || value === '' ? fallback : value;
|
|
13
|
+
}
|
|
14
|
+
function loadApiConfig() {
|
|
15
|
+
const hostPrefix = env('HOST_PREFIX', 'qa-update-mt');
|
|
16
|
+
const clientId = env('CLIENT_ID', 'demo');
|
|
17
|
+
const authMethod = env('AUTH_METHOD', 'oauth2');
|
|
18
|
+
return {
|
|
19
|
+
baseUrl: env('BASE_URL', `https://${hostPrefix}.ci.distro.ultrawombat.com/orchestration`).replace(/\/+$/, ''),
|
|
20
|
+
authMethod,
|
|
21
|
+
oauth: {
|
|
22
|
+
tokenUrl: env('TOKEN_URL', `https://${hostPrefix}.ci.distro.ultrawombat.com/auth/realms/camunda-platform/protocol/openid-connect/token`),
|
|
23
|
+
clientId,
|
|
24
|
+
clientSecret: env('CLIENT_SECRET', clientId),
|
|
25
|
+
audience: env('AUDIENCE', 'orchestration-api'),
|
|
26
|
+
scope: env('SCOPE'),
|
|
27
|
+
},
|
|
28
|
+
basic: {
|
|
29
|
+
username: env('BASIC_AUTH_USER'),
|
|
30
|
+
password: env('BASIC_AUTH_PASSWORD'),
|
|
31
|
+
},
|
|
32
|
+
multiTenancy: env('MT', 'true') === 'true',
|
|
33
|
+
tenantId: env('TENANT_ID', '<default>'),
|
|
34
|
+
zeebeVersion: env('ZEEBE_VERSION', '8.8'),
|
|
35
|
+
// The SM Helm flows this suite runs in deploy Keycloak and drive the API
|
|
36
|
+
// with client credentials, so `oauth2` means the cluster is in OIDC mode.
|
|
37
|
+
// `USERS_API_ENABLED` overrides that for a deployment where the two come
|
|
38
|
+
// apart, rather than leaving the suite to guess.
|
|
39
|
+
usersApiEnabled: env('USERS_API_ENABLED', String(authMethod !== 'oauth2')) === 'true',
|
|
40
|
+
authorizationsEnabled: env('AUTHORIZATIONS_ENABLED', 'true') === 'true',
|
|
41
|
+
authenticationRequired: env('AUTHENTICATION_REQUIRED', 'true') === 'true',
|
|
42
|
+
secondaryClient: {
|
|
43
|
+
clientId: env('SECONDARY_CLIENT_ID'),
|
|
44
|
+
clientSecret: env('SECONDARY_CLIENT_SECRET'),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
exports.loadApiConfig = loadApiConfig;
|
|
49
|
+
exports.apiConfig = loadApiConfig();
|
|
50
|
+
/**
|
|
51
|
+
* True when the cluster runs at or above `minor`, e.g. `atLeastVersion('8.9')`.
|
|
52
|
+
* Used to gate expectations for endpoints that only exist from a given release.
|
|
53
|
+
*/
|
|
54
|
+
function atLeastVersion(minor) {
|
|
55
|
+
const parse = (v) => v.split('.').map((part) => parseInt(part, 10));
|
|
56
|
+
const [major, minorPart] = parse(exports.apiConfig.zeebeVersion);
|
|
57
|
+
const [reqMajor, reqMinor] = parse(minor);
|
|
58
|
+
if (Number.isNaN(major) || Number.isNaN(minorPart))
|
|
59
|
+
return true;
|
|
60
|
+
return major > reqMajor || (major === reqMajor && minorPart >= reqMinor);
|
|
61
|
+
}
|
|
62
|
+
exports.atLeastVersion = atLeastVersion;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment helpers.
|
|
3
|
+
*
|
|
4
|
+
* Deployment is the setup step for most of this suite, so it asserts hard: a
|
|
5
|
+
* silent deployment failure in the shell suite left every downstream assertion
|
|
6
|
+
* comparing against an empty key, which then "passed" against a 404-tolerant
|
|
7
|
+
* expectation.
|
|
8
|
+
*/
|
|
9
|
+
import { type APIResponse } from '@playwright/test';
|
|
10
|
+
import type { ApiClient } from './http';
|
|
11
|
+
import type { ResourceTracker } from './cleanup';
|
|
12
|
+
export interface DeployedProcess {
|
|
13
|
+
processDefinitionKey: string;
|
|
14
|
+
processDefinitionId: string;
|
|
15
|
+
version: number;
|
|
16
|
+
deploymentKey: string;
|
|
17
|
+
}
|
|
18
|
+
export interface DeployedDecision {
|
|
19
|
+
decisionDefinitionKey: string;
|
|
20
|
+
decisionDefinitionId: string;
|
|
21
|
+
decisionRequirementsKey: string;
|
|
22
|
+
}
|
|
23
|
+
interface DeploymentResource {
|
|
24
|
+
name: string;
|
|
25
|
+
content: string;
|
|
26
|
+
mimeType?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Uploads resources to `/v2/deployments` as multipart form data, and tracks
|
|
30
|
+
* whatever they deployed for cleanup.
|
|
31
|
+
*
|
|
32
|
+
* On a multi-tenant cluster the tenant is a form field rather than a query
|
|
33
|
+
* parameter. The shell suite wrote the tenant to a temp file and used curl's
|
|
34
|
+
* `-F name=<file` syntax, because the default tenant id `<default>` starts with
|
|
35
|
+
* `<` and curl would otherwise read it as a filename; Playwright takes the
|
|
36
|
+
* value directly, so that whole dance disappears.
|
|
37
|
+
*
|
|
38
|
+
* `resources` is the spec's `ResourceTracker` fixture and is required rather
|
|
39
|
+
* than optional on purpose: an optional tracker is one a new spec forgets, and
|
|
40
|
+
* nothing in the run would report the leak.
|
|
41
|
+
*/
|
|
42
|
+
export declare function deployResources(api: ApiClient, resources: ResourceTracker, files: DeploymentResource[]): Promise<APIResponse>;
|
|
43
|
+
export declare function deployProcess(api: ApiClient, resources: ResourceTracker, processId: string, xml: string): Promise<DeployedProcess>;
|
|
44
|
+
export declare function deployDecision(api: ApiClient, resources: ResourceTracker, decisionId: string, dmn: string): Promise<DeployedDecision>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Deployment helpers.
|
|
4
|
+
*
|
|
5
|
+
* Deployment is the setup step for most of this suite, so it asserts hard: a
|
|
6
|
+
* silent deployment failure in the shell suite left every downstream assertion
|
|
7
|
+
* comparing against an empty key, which then "passed" against a 404-tolerant
|
|
8
|
+
* expectation.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.deployDecision = exports.deployProcess = exports.deployResources = void 0;
|
|
12
|
+
const test_1 = require("@playwright/test");
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const assertions_1 = require("./assertions");
|
|
15
|
+
/**
|
|
16
|
+
* Registers every definition a deployment created so the spec's tracker
|
|
17
|
+
* deletes it.
|
|
18
|
+
*
|
|
19
|
+
* A definition is durable: it stays on the cluster and in its indices until
|
|
20
|
+
* something removes it, so an untracked deployment survives the run and the
|
|
21
|
+
* next nightly adds its own on top.
|
|
22
|
+
*
|
|
23
|
+
* Which key addresses the deletable resource differs by type. For a process it
|
|
24
|
+
* is the definition key. For a decision it is the *requirements* key: one DMN
|
|
25
|
+
* file deploys as a single DRD resource that may carry several decisions, and
|
|
26
|
+
* `/resources/{resourceKey}/deletion` addresses the resource, not a decision
|
|
27
|
+
* inside it — so tracking the decision keys instead would ask the cluster to
|
|
28
|
+
* delete the same resource once per decision.
|
|
29
|
+
*/
|
|
30
|
+
async function trackDeployment(resources, response) {
|
|
31
|
+
// Deployment failures are asserted on by their own tests; there is nothing
|
|
32
|
+
// to clean up when nothing was created.
|
|
33
|
+
if (response.status() !== 200)
|
|
34
|
+
return;
|
|
35
|
+
const json = (await response.json());
|
|
36
|
+
for (const entry of json.deployments ?? []) {
|
|
37
|
+
resources.track('resource', entry.processDefinition?.processDefinitionKey ??
|
|
38
|
+
entry.decisionRequirements?.decisionRequirementsKey ??
|
|
39
|
+
entry.form?.formKey);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Uploads resources to `/v2/deployments` as multipart form data, and tracks
|
|
44
|
+
* whatever they deployed for cleanup.
|
|
45
|
+
*
|
|
46
|
+
* On a multi-tenant cluster the tenant is a form field rather than a query
|
|
47
|
+
* parameter. The shell suite wrote the tenant to a temp file and used curl's
|
|
48
|
+
* `-F name=<file` syntax, because the default tenant id `<default>` starts with
|
|
49
|
+
* `<` and curl would otherwise read it as a filename; Playwright takes the
|
|
50
|
+
* value directly, so that whole dance disappears.
|
|
51
|
+
*
|
|
52
|
+
* `resources` is the spec's `ResourceTracker` fixture and is required rather
|
|
53
|
+
* than optional on purpose: an optional tracker is one a new spec forgets, and
|
|
54
|
+
* nothing in the run would report the leak.
|
|
55
|
+
*/
|
|
56
|
+
async function deployResources(api, resources, files) {
|
|
57
|
+
// Every file must be sent under the same field name: the endpoint binds
|
|
58
|
+
// `@RequestPart("resources") List<MultipartFile>` (`ResourceController` in
|
|
59
|
+
// camunda/camunda), so Spring collects the parts literally named
|
|
60
|
+
// `resources` and ignores any other name.
|
|
61
|
+
//
|
|
62
|
+
// Playwright's object form of `multipart` is keyed, so it cannot repeat a
|
|
63
|
+
// name — an earlier attempt to work around that by numbering the extra
|
|
64
|
+
// parts (`resources1`, `resources2`, …) did not fail, it silently deployed
|
|
65
|
+
// only the first file. `FormData` can repeat the name, so use it.
|
|
66
|
+
const form = new FormData();
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
form.append('resources', new Blob([file.content], {
|
|
69
|
+
type: file.mimeType ?? 'application/xml',
|
|
70
|
+
}), file.name);
|
|
71
|
+
}
|
|
72
|
+
if (config_1.apiConfig.multiTenancy) {
|
|
73
|
+
form.append('tenantId', config_1.apiConfig.tenantId);
|
|
74
|
+
}
|
|
75
|
+
const response = await api.post('/deployments', {
|
|
76
|
+
multipart: form,
|
|
77
|
+
tenant: 'none',
|
|
78
|
+
});
|
|
79
|
+
await trackDeployment(resources, response);
|
|
80
|
+
return response;
|
|
81
|
+
}
|
|
82
|
+
exports.deployResources = deployResources;
|
|
83
|
+
async function deployProcess(api, resources, processId, xml) {
|
|
84
|
+
const response = await deployResources(api, resources, [
|
|
85
|
+
{ name: `${processId}.bpmn`, content: xml },
|
|
86
|
+
]);
|
|
87
|
+
await (0, assertions_1.assertStatus)(response, 200);
|
|
88
|
+
const json = await response.json();
|
|
89
|
+
const deployment = (json.deployments ?? []).find((entry) => entry.processDefinition !== undefined);
|
|
90
|
+
(0, test_1.expect)(deployment, `deployment response carried no processDefinition: ${JSON.stringify(json)}`).toBeTruthy();
|
|
91
|
+
const definition = deployment.processDefinition;
|
|
92
|
+
return {
|
|
93
|
+
processDefinitionKey: String(definition.processDefinitionKey),
|
|
94
|
+
processDefinitionId: String(definition.processDefinitionId),
|
|
95
|
+
// The deployment result names this `processDefinitionVersion`, while the
|
|
96
|
+
// search and get-by-key results name the same number `version`. The two
|
|
97
|
+
// schemas genuinely differ (`DeploymentProcessResult` vs
|
|
98
|
+
// `ProcessDefinitionResult` in camunda/camunda), and reading `version`
|
|
99
|
+
// here yielded NaN, which then compared unequal to every real version.
|
|
100
|
+
version: Number(definition.processDefinitionVersion),
|
|
101
|
+
deploymentKey: String(json.deploymentKey),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
exports.deployProcess = deployProcess;
|
|
105
|
+
async function deployDecision(api, resources, decisionId, dmn) {
|
|
106
|
+
const response = await deployResources(api, resources, [
|
|
107
|
+
{ name: `${decisionId}.dmn`, content: dmn },
|
|
108
|
+
]);
|
|
109
|
+
await (0, assertions_1.assertStatus)(response, 200);
|
|
110
|
+
const json = await response.json();
|
|
111
|
+
const deployment = (json.deployments ?? []).find((entry) => entry.decisionDefinition !== undefined);
|
|
112
|
+
(0, test_1.expect)(deployment, `deployment response carried no decisionDefinition: ${JSON.stringify(json)}`).toBeTruthy();
|
|
113
|
+
const definition = deployment.decisionDefinition;
|
|
114
|
+
return {
|
|
115
|
+
decisionDefinitionKey: String(definition.decisionDefinitionKey),
|
|
116
|
+
decisionDefinitionId: String(definition.decisionDefinitionId),
|
|
117
|
+
decisionRequirementsKey: String(definition.decisionRequirementsKey),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
exports.deployDecision = deployDecision;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting helpers for the search index's eventual consistency.
|
|
3
|
+
*
|
|
4
|
+
* Writes go to the engine, reads come from the search store, and the exporter
|
|
5
|
+
* between them lags. The legacy shell suite bridged that with 81 fixed `sleep`
|
|
6
|
+
* calls totalling 181 seconds — which both slowed every green run and still
|
|
7
|
+
* flaked whenever the exporter was slower than the guess. These poll instead,
|
|
8
|
+
* so a fast cluster returns immediately and a slow one still passes.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Generous enough for exporter lag on a loaded Helm cluster, bounded enough
|
|
12
|
+
* that a genuinely missing record fails the test rather than the suite timeout.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SEARCH_TIMEOUT_MS = 60000;
|
|
15
|
+
export declare const SEARCH_INTERVALS_MS: number[];
|
|
16
|
+
export declare const searchAssertionOptions: {
|
|
17
|
+
timeout: number;
|
|
18
|
+
intervals: number[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Retries `assertion` until it stops throwing. Use for any read-back of
|
|
22
|
+
* something just written through the engine.
|
|
23
|
+
*/
|
|
24
|
+
export declare function eventually(assertion: () => Promise<void> | void, options?: {
|
|
25
|
+
timeout?: number;
|
|
26
|
+
intervals?: number[];
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Polls `produce` until `predicate` accepts its result, then returns it.
|
|
30
|
+
* Use when the test needs the value, not just the fact that it appeared.
|
|
31
|
+
*/
|
|
32
|
+
export declare function eventuallyGet<T>(produce: () => Promise<T>, predicate: (value: T) => boolean, options?: {
|
|
33
|
+
timeout?: number;
|
|
34
|
+
intervals?: number[];
|
|
35
|
+
description?: string;
|
|
36
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Waiting helpers for the search index's eventual consistency.
|
|
4
|
+
*
|
|
5
|
+
* Writes go to the engine, reads come from the search store, and the exporter
|
|
6
|
+
* between them lags. The legacy shell suite bridged that with 81 fixed `sleep`
|
|
7
|
+
* calls totalling 181 seconds — which both slowed every green run and still
|
|
8
|
+
* flaked whenever the exporter was slower than the guess. These poll instead,
|
|
9
|
+
* so a fast cluster returns immediately and a slow one still passes.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.eventuallyGet = exports.eventually = exports.searchAssertionOptions = exports.SEARCH_INTERVALS_MS = exports.SEARCH_TIMEOUT_MS = void 0;
|
|
13
|
+
const test_1 = require("@playwright/test");
|
|
14
|
+
/**
|
|
15
|
+
* Generous enough for exporter lag on a loaded Helm cluster, bounded enough
|
|
16
|
+
* that a genuinely missing record fails the test rather than the suite timeout.
|
|
17
|
+
*/
|
|
18
|
+
exports.SEARCH_TIMEOUT_MS = 60000;
|
|
19
|
+
exports.SEARCH_INTERVALS_MS = [500, 1000, 2000, 2000, 5000];
|
|
20
|
+
exports.searchAssertionOptions = {
|
|
21
|
+
timeout: exports.SEARCH_TIMEOUT_MS,
|
|
22
|
+
intervals: exports.SEARCH_INTERVALS_MS,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Retries `assertion` until it stops throwing. Use for any read-back of
|
|
26
|
+
* something just written through the engine.
|
|
27
|
+
*/
|
|
28
|
+
async function eventually(assertion, options = {}) {
|
|
29
|
+
await (0, test_1.expect)(async () => {
|
|
30
|
+
await assertion();
|
|
31
|
+
}).toPass({
|
|
32
|
+
timeout: options.timeout ?? exports.SEARCH_TIMEOUT_MS,
|
|
33
|
+
intervals: options.intervals ?? exports.SEARCH_INTERVALS_MS,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
exports.eventually = eventually;
|
|
37
|
+
/**
|
|
38
|
+
* Polls `produce` until `predicate` accepts its result, then returns it.
|
|
39
|
+
* Use when the test needs the value, not just the fact that it appeared.
|
|
40
|
+
*/
|
|
41
|
+
async function eventuallyGet(produce, predicate, options = {}) {
|
|
42
|
+
let last;
|
|
43
|
+
await (0, test_1.expect)(async () => {
|
|
44
|
+
last = await produce();
|
|
45
|
+
if (!predicate(last)) {
|
|
46
|
+
throw new Error(`${options.description ?? 'Value'} not ready yet: ${JSON.stringify(last)?.slice(0, 500)}`);
|
|
47
|
+
}
|
|
48
|
+
}).toPass({
|
|
49
|
+
timeout: options.timeout ?? exports.SEARCH_TIMEOUT_MS,
|
|
50
|
+
intervals: options.intervals ?? exports.SEARCH_INTERVALS_MS,
|
|
51
|
+
});
|
|
52
|
+
return last;
|
|
53
|
+
}
|
|
54
|
+
exports.eventuallyGet = eventuallyGet;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the Orchestration Cluster REST v2 API.
|
|
3
|
+
*
|
|
4
|
+
* Wraps Playwright's `APIRequestContext` with the three things a Helm-deployed
|
|
5
|
+
* cluster needs and a localhost one does not: pluggable auth (see `auth.ts`),
|
|
6
|
+
* tenant propagation (see `tenancy.ts`), and tolerance of the ingress dropping
|
|
7
|
+
* requests while orchestration pods roll during an upgrade.
|
|
8
|
+
*/
|
|
9
|
+
import type { APIRequestContext, APIResponse } from '@playwright/test';
|
|
10
|
+
import { type ApiConfig } from './config';
|
|
11
|
+
import type { ApiPath } from './paths';
|
|
12
|
+
import { type TenantMode } from './tenancy';
|
|
13
|
+
export type { ApiPath, TenantMode };
|
|
14
|
+
type QueryValue = string | number | boolean | undefined;
|
|
15
|
+
export interface RequestOptions {
|
|
16
|
+
/** Values for `{placeholder}` segments of the path template. */
|
|
17
|
+
params?: Record<string, string | number>;
|
|
18
|
+
query?: Record<string, QueryValue>;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
/** Where the tenant goes for this endpoint. Defaults to `auto`. */
|
|
21
|
+
tenant?: TenantMode;
|
|
22
|
+
/**
|
|
23
|
+
* Overrides the suite principal's `Authorization` header. Pass `null` to send
|
|
24
|
+
* no credentials at all, which the unauthenticated assertions rely on.
|
|
25
|
+
*/
|
|
26
|
+
authorization?: string | null;
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
/**
|
|
29
|
+
* Multipart body for deployment uploads. `FormData` rather than a plain
|
|
30
|
+
* object where a field name repeats — `/v2/deployments` takes every file
|
|
31
|
+
* under the single name `resources`, which a keyed object cannot express.
|
|
32
|
+
*/
|
|
33
|
+
multipart?: FormData | Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export declare class ApiClient {
|
|
36
|
+
private readonly request;
|
|
37
|
+
private readonly config;
|
|
38
|
+
constructor(request: APIRequestContext, config?: ApiConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Expands a path template against the shared `/v2` prefix.
|
|
41
|
+
*
|
|
42
|
+
* Throws on a missing placeholder rather than emitting a literal `{key}` in
|
|
43
|
+
* the URL: the gateway answers such a path with a plausible 404, so a test
|
|
44
|
+
* asserting 404 would pass without exercising anything.
|
|
45
|
+
*/
|
|
46
|
+
buildUrl(path: ApiPath, params?: Record<string, string | number>, query?: Record<string, QueryValue>): string;
|
|
47
|
+
private headersFor;
|
|
48
|
+
private send;
|
|
49
|
+
get(path: ApiPath, options?: RequestOptions): Promise<APIResponse>;
|
|
50
|
+
post(path: ApiPath, options?: RequestOptions): Promise<APIResponse>;
|
|
51
|
+
put(path: ApiPath, options?: RequestOptions): Promise<APIResponse>;
|
|
52
|
+
patch(path: ApiPath, options?: RequestOptions): Promise<APIResponse>;
|
|
53
|
+
delete(path: ApiPath, options?: RequestOptions): Promise<APIResponse>;
|
|
54
|
+
/** Raw request for the handful of cases needing a non-JSON content type. */
|
|
55
|
+
raw(method: 'get' | 'post' | 'put' | 'patch' | 'delete', path: ApiPath, options?: RequestOptions): Promise<APIResponse>;
|
|
56
|
+
}
|