@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.
Files changed (83) 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/ModelerCreatePage.js +5 -4
  4. package/dist/tests/api/v2/authorization/authorization-api-tests.spec.d.ts +1 -0
  5. package/dist/tests/api/v2/authorization/authorization-api-tests.spec.js +247 -0
  6. package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.d.ts +18 -0
  7. package/dist/tests/api/v2/authorization/authorization-enforcement-api-tests.spec.js +208 -0
  8. package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.d.ts +1 -0
  9. package/dist/tests/api/v2/batch-operation/batch-operation-api-tests.spec.js +264 -0
  10. package/dist/tests/api/v2/cluster/cluster-api-tests.spec.d.ts +1 -0
  11. package/dist/tests/api/v2/cluster/cluster-api-tests.spec.js +86 -0
  12. package/dist/tests/api/v2/decision/decision-api-tests.spec.d.ts +1 -0
  13. package/dist/tests/api/v2/decision/decision-api-tests.spec.js +233 -0
  14. package/dist/tests/api/v2/deployment/deployment-api-tests.spec.d.ts +1 -0
  15. package/dist/tests/api/v2/deployment/deployment-api-tests.spec.js +147 -0
  16. package/dist/tests/api/v2/document/document-api-tests.spec.d.ts +9 -0
  17. package/dist/tests/api/v2/document/document-api-tests.spec.js +111 -0
  18. package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.d.ts +1 -0
  19. package/dist/tests/api/v2/element-instance/element-instance-api-tests.spec.js +127 -0
  20. package/dist/tests/api/v2/flows/end-to-end-flows.spec.d.ts +9 -0
  21. package/dist/tests/api/v2/flows/end-to-end-flows.spec.js +204 -0
  22. package/dist/tests/api/v2/form/form-api-tests.spec.d.ts +1 -0
  23. package/dist/tests/api/v2/form/form-api-tests.spec.js +88 -0
  24. package/dist/tests/api/v2/identity/group-api-tests.spec.d.ts +1 -0
  25. package/dist/tests/api/v2/identity/group-api-tests.spec.js +172 -0
  26. package/dist/tests/api/v2/identity/membership-api-tests.spec.d.ts +10 -0
  27. package/dist/tests/api/v2/identity/membership-api-tests.spec.js +164 -0
  28. package/dist/tests/api/v2/identity/role-api-tests.spec.d.ts +1 -0
  29. package/dist/tests/api/v2/identity/role-api-tests.spec.js +212 -0
  30. package/dist/tests/api/v2/identity/tenant-api-tests.spec.d.ts +1 -0
  31. package/dist/tests/api/v2/identity/tenant-api-tests.spec.js +319 -0
  32. package/dist/tests/api/v2/identity/user-api-tests.spec.d.ts +1 -0
  33. package/dist/tests/api/v2/identity/user-api-tests.spec.js +148 -0
  34. package/dist/tests/api/v2/job/job-api-tests.spec.d.ts +1 -0
  35. package/dist/tests/api/v2/job/job-api-tests.spec.js +260 -0
  36. package/dist/tests/api/v2/message/message-api-tests.spec.d.ts +1 -0
  37. package/dist/tests/api/v2/message/message-api-tests.spec.js +219 -0
  38. package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.d.ts +1 -0
  39. package/dist/tests/api/v2/process-definition/process-definition-api-tests.spec.js +118 -0
  40. package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.d.ts +1 -0
  41. package/dist/tests/api/v2/process-instance/process-instance-api-tests.spec.js +335 -0
  42. package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.d.ts +1 -0
  43. package/dist/tests/api/v2/runtime/batch-modification-api-tests.spec.js +123 -0
  44. package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.d.ts +1 -0
  45. package/dist/tests/api/v2/runtime/variable-incident-api-tests.spec.js +174 -0
  46. package/dist/tests/api/v2/system/clock-api-tests.spec.d.ts +19 -0
  47. package/dist/tests/api/v2/system/clock-api-tests.spec.js +62 -0
  48. package/dist/tests/api/v2/system/system-api-tests.spec.d.ts +1 -0
  49. package/dist/tests/api/v2/system/system-api-tests.spec.js +183 -0
  50. package/dist/tests/api/v2/user-task/user-task-api-tests.spec.d.ts +1 -0
  51. package/dist/tests/api/v2/user-task/user-task-api-tests.spec.js +258 -0
  52. package/dist/utils/api/assertions.d.ts +50 -0
  53. package/dist/utils/api/assertions.js +142 -0
  54. package/dist/utils/api/auth.d.ts +27 -0
  55. package/dist/utils/api/auth.js +137 -0
  56. package/dist/utils/api/beans.d.ts +46 -0
  57. package/dist/utils/api/beans.js +87 -0
  58. package/dist/utils/api/bpmn.d.ts +50 -0
  59. package/dist/utils/api/bpmn.js +217 -0
  60. package/dist/utils/api/capabilities.d.ts +90 -0
  61. package/dist/utils/api/capabilities.js +198 -0
  62. package/dist/utils/api/cleanup.d.ts +24 -0
  63. package/dist/utils/api/cleanup.js +120 -0
  64. package/dist/utils/api/config.d.ts +103 -0
  65. package/dist/utils/api/config.js +62 -0
  66. package/dist/utils/api/deployments.d.ts +45 -0
  67. package/dist/utils/api/deployments.js +120 -0
  68. package/dist/utils/api/eventually.d.ts +36 -0
  69. package/dist/utils/api/eventually.js +54 -0
  70. package/dist/utils/api/http.d.ts +56 -0
  71. package/dist/utils/api/http.js +244 -0
  72. package/dist/utils/api/identity.d.ts +29 -0
  73. package/dist/utils/api/identity.js +43 -0
  74. package/dist/utils/api/keys.d.ts +37 -0
  75. package/dist/utils/api/keys.js +40 -0
  76. package/dist/utils/api/paths.d.ts +25 -0
  77. package/dist/utils/api/paths.js +167 -0
  78. package/dist/utils/api/tenancy.d.ts +31 -0
  79. package/dist/utils/api/tenancy.js +65 -0
  80. package/package.json +1 -1
  81. package/playwright.config.ts +14 -2
  82. package/dist/resources/test-api-v2-complete.sh +0 -12246
  83. 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
+ }