@camunda/e2e-test-suite 0.0.1640 → 0.0.1642

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.
@@ -51,7 +51,7 @@
51
51
  target="vectorStore.amazonManagedOpensearch.serverUrl" />
52
52
  <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_REGION}}"
53
53
  target="vectorStore.amazonManagedOpensearch.region" />
54
- <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_INDEX_NAME}}"
54
+ <zeebe:input source="=dbVectorIndexName"
55
55
  target="vectorStore.amazonManagedOpensearch.indexName" />
56
56
  <zeebe:input source="PlainText"
57
57
  target="vectorDatabaseConnectorOperation.documentSource" />
@@ -108,7 +108,7 @@
108
108
  target="vectorStore.amazonManagedOpensearch.serverUrl" />
109
109
  <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_REGION}}"
110
110
  target="vectorStore.amazonManagedOpensearch.region" />
111
- <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_INDEX_NAME}}"
111
+ <zeebe:input source="=dbVectorIndexName"
112
112
  target="vectorStore.amazonManagedOpensearch.indexName" />
113
113
  </zeebe:ioMapping>
114
114
  <zeebe:taskHeaders>
@@ -5,6 +5,7 @@ const UtilitiesPage_1 = require("../../pages/8.9/UtilitiesPage");
5
5
  const _setup_1 = require("../../test-setup.js");
6
6
  const apiHelpers_1 = require("../../utils/apiHelpers");
7
7
  const sleep_1 = require("../../utils/sleep");
8
+ const awsOpenSearch_1 = require("../../utils/awsOpenSearch");
8
9
  const users_1 = require("../../utils/users");
9
10
  const testUser = (0, users_1.getTestUser)('fourteenthUser');
10
11
  let authToken;
@@ -29,6 +30,25 @@ async function waitForUserTaskKeys(processInstanceKey, maxAttempts = 120, retryD
29
30
  .map(String);
30
31
  }
31
32
  }
33
+ // A connector job that exhausted its retries raises an incident, which
34
+ // never resolves on its own, so stop polling and surface the cause.
35
+ const incidentResponse = await apiRequestContext.post((0, apiHelpers_1.buildZeebeApiUrl)('/v2/incidents/search'), {
36
+ headers: { Authorization: authToken },
37
+ data: {
38
+ filter: {
39
+ processInstanceKey: String(processInstanceKey),
40
+ state: 'ACTIVE',
41
+ },
42
+ },
43
+ });
44
+ if (incidentResponse.status() === 200) {
45
+ const incident = (await incidentResponse.json())?.items?.[0];
46
+ if (incident != null) {
47
+ throw new Error(`Process instance ${processInstanceKey} has an incident on ` +
48
+ `${incident.elementId} (${incident.errorType}): ` +
49
+ `${incident.errorMessage}`);
50
+ }
51
+ }
32
52
  if (attempt < maxAttempts) {
33
53
  await (0, sleep_1.sleep)(retryDelayMs);
34
54
  }
@@ -75,6 +95,7 @@ const dbVectorTestCases = [
75
95
  embedding_store: 'Amazon OpenSearch',
76
96
  bpmnPath: './resources/connectors/db_vector_connector/db-vector-plain-text_openai_amazon_opensearch.bpmn',
77
97
  processName: 'db_vector_process_OpenAI_OpenSearch',
98
+ usesRunScopedOpenSearchIndex: true,
78
99
  },
79
100
  ];
80
101
  const aiAgentSubProcessTestCases = [
@@ -199,36 +220,51 @@ _8_9_1.test.describe('Agentic Orchestation User Flows', () => {
199
220
  for (const params of dbVectorTestCases) {
200
221
  (0, _8_9_1.test)(`DB Vector connector embed via ${params.embedding_provider} and stored on ${params.embedding_store} with source as plain text`, async ({ appsPage, operateHomePage, operateProcessInstancePage, operateProcessesPage, taskPanelPage, taskDetailsPage, }) => {
201
222
  _8_9_1.test.slow();
202
- const processKey = await (0, apiHelpers_1.deployProcess)(params.bpmnPath, authToken);
203
- if (processKey == null) {
204
- throw new Error('Failed to deploy process or missing processDefinitionKey');
223
+ // Retrieval scans every document in the index, so a store shared
224
+ // across runs grows until search times out. Each attempt gets its own.
225
+ const indexName = params.usesRunScopedOpenSearchIndex
226
+ ? (0, awsOpenSearch_1.createRunIndexName)()
227
+ : undefined;
228
+ if (indexName != null) {
229
+ await (0, awsOpenSearch_1.deleteStaleRunIndices)();
230
+ }
231
+ try {
232
+ const processKey = await (0, apiHelpers_1.deployProcess)(params.bpmnPath, authToken);
233
+ if (processKey == null) {
234
+ throw new Error('Failed to deploy process or missing processDefinitionKey');
235
+ }
236
+ const instanceKey = await (0, apiHelpers_1.createProcessInstance)(String(processKey), authToken, undefined, undefined, indexName != null ? { dbVectorIndexName: indexName } : undefined);
237
+ await _8_9_1.test.step('User can access the process on operate', async () => {
238
+ await operateHomePage.clickProcessesTab();
239
+ await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', String(instanceKey));
240
+ await operateProcessesPage.clickProcessInstanceLink(params.processName);
241
+ await operateProcessInstancePage.assertActiveTokenIsPresent();
242
+ });
243
+ await _8_9_1.test.step('User can view the agent feedback on tasklist', async () => {
244
+ const taskKeys = await waitForUserTaskKeys(instanceKey);
245
+ await appsPage.clickCamundaApps();
246
+ await appsPage.clickTasklist(clusterName);
247
+ await (0, sleep_1.sleep)(30000);
248
+ await taskPanelPage.openTask(params.processName, taskKeys);
249
+ await taskDetailsPage.clickAssignToMeButton();
250
+ await taskDetailsPage.assertAIResponse(regexTestCases[0].regex_db_vector);
251
+ await taskDetailsPage.clickCompleteTaskButton();
252
+ });
253
+ await _8_9_1.test.step('User can assert the process is completed on Operate', async () => {
254
+ await appsPage.clickCamundaApps();
255
+ await appsPage.clickOperate(clusterName);
256
+ await operateHomePage.clickProcessesTab();
257
+ await operateProcessesPage.clickProcessCompletedCheckbox();
258
+ await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', String(instanceKey));
259
+ await operateProcessesPage.clickProcessInstanceLink(params.processName);
260
+ await operateProcessInstancePage.assertProcessCompleteStatusWithRetry();
261
+ });
262
+ }
263
+ finally {
264
+ if (indexName != null) {
265
+ await (0, awsOpenSearch_1.deleteOpenSearchIndex)(indexName);
266
+ }
205
267
  }
206
- const instanceKey = await (0, apiHelpers_1.createProcessInstance)(String(processKey), authToken);
207
- await _8_9_1.test.step('User can access the process on operate', async () => {
208
- await operateHomePage.clickProcessesTab();
209
- await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', String(instanceKey));
210
- await operateProcessesPage.clickProcessInstanceLink(params.processName);
211
- await operateProcessInstancePage.assertActiveTokenIsPresent();
212
- });
213
- await _8_9_1.test.step('User can view the agent feedback on tasklist', async () => {
214
- const taskKeys = await waitForUserTaskKeys(instanceKey);
215
- await appsPage.clickCamundaApps();
216
- await appsPage.clickTasklist(clusterName);
217
- await (0, sleep_1.sleep)(30000);
218
- await taskPanelPage.openTask(params.processName, taskKeys);
219
- await taskDetailsPage.clickAssignToMeButton();
220
- await taskDetailsPage.assertAIResponse(regexTestCases[0].regex_db_vector);
221
- await taskDetailsPage.clickCompleteTaskButton();
222
- });
223
- await _8_9_1.test.step('User can assert the process is completed on Operate', async () => {
224
- await appsPage.clickCamundaApps();
225
- await appsPage.clickOperate(clusterName);
226
- await operateHomePage.clickProcessesTab();
227
- await operateProcessesPage.clickProcessCompletedCheckbox();
228
- await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', String(instanceKey));
229
- await operateProcessesPage.clickProcessInstanceLink(params.processName);
230
- await operateProcessInstancePage.assertProcessCompleteStatusWithRetry();
231
- });
232
268
  });
233
269
  }
234
270
  });
@@ -0,0 +1,4 @@
1
+ declare function createRunIndexName(): string;
2
+ declare function deleteOpenSearchIndex(indexName: string): Promise<void>;
3
+ declare function deleteStaleRunIndices(): Promise<void>;
4
+ export { createRunIndexName, deleteOpenSearchIndex, deleteStaleRunIndices };
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
4
+ * one or more contributor license agreements. See the NOTICE file distributed
5
+ * with this work for additional information regarding copyright ownership.
6
+ * Licensed under the Camunda License 1.0. You may not use this file
7
+ * except in compliance with the Camunda License 1.0.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.deleteStaleRunIndices = exports.deleteOpenSearchIndex = exports.createRunIndexName = void 0;
11
+ const crypto_1 = require("crypto");
12
+ const RUN_INDEX_PREFIX = 'qa-db-vector-e2e-run-';
13
+ const STALE_INDEX_AGE_MS = 24 * 60 * 60 * 1000;
14
+ function getConfig() {
15
+ const { DB_VECTOR_AWS_OPENSEARCH_SERVER_URL: serverUrl, DB_VECTOR_AWS_OPENSEARCH_REGION: region, DB_VECTOR_AWS_OPENSEARCH_ACCESS_KEY: accessKey, DB_VECTOR_AWS_OPENSEARCH_SECRET_KEY: secretKey, } = process.env;
16
+ if (!serverUrl || !region || !accessKey || !secretKey) {
17
+ return null;
18
+ }
19
+ const url = /^https?:\/\//.test(serverUrl)
20
+ ? serverUrl
21
+ : `https://${serverUrl}`;
22
+ return { baseUrl: new URL(url), region, accessKey, secretKey };
23
+ }
24
+ const sha256 = (data) => (0, crypto_1.createHash)('sha256').update(data, 'utf8').digest('hex');
25
+ const hmac = (key, data) => (0, crypto_1.createHmac)('sha256', key).update(data, 'utf8').digest();
26
+ // AWS Signature V4 for the `es` service, with no query string and no body.
27
+ async function signedRequest(config, method, path) {
28
+ const amzDate = new Date().toISOString().replace(/[:-]|\.\d{3}/g, '');
29
+ const dateStamp = amzDate.slice(0, 8);
30
+ const host = config.baseUrl.host;
31
+ const payloadHash = sha256('');
32
+ // SigV4 needs RFC 3986 encoding, which encodeURIComponent stops short of.
33
+ const encodedPath = path
34
+ .split('/')
35
+ .map((segment) => encodeURIComponent(decodeURIComponent(segment)).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`))
36
+ .join('/');
37
+ const canonicalRequest = [
38
+ method,
39
+ encodedPath,
40
+ '',
41
+ `host:${host}\nx-amz-content-sha256:${payloadHash}\nx-amz-date:${amzDate}\n`,
42
+ 'host;x-amz-content-sha256;x-amz-date',
43
+ payloadHash,
44
+ ].join('\n');
45
+ const scope = `${dateStamp}/${config.region}/es/aws4_request`;
46
+ const stringToSign = [
47
+ 'AWS4-HMAC-SHA256',
48
+ amzDate,
49
+ scope,
50
+ sha256(canonicalRequest),
51
+ ].join('\n');
52
+ let signingKey = hmac(`AWS4${config.secretKey}`, dateStamp);
53
+ for (const part of [config.region, 'es', 'aws4_request']) {
54
+ signingKey = hmac(signingKey, part);
55
+ }
56
+ const signature = (0, crypto_1.createHmac)('sha256', signingKey)
57
+ .update(stringToSign, 'utf8')
58
+ .digest('hex');
59
+ return fetch(new URL(path, config.baseUrl), {
60
+ method,
61
+ headers: {
62
+ 'x-amz-date': amzDate,
63
+ 'x-amz-content-sha256': payloadHash,
64
+ Authorization: `AWS4-HMAC-SHA256 Credential=${config.accessKey}/${scope}, ` +
65
+ `SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=${signature}`,
66
+ },
67
+ signal: AbortSignal.timeout(60000),
68
+ });
69
+ }
70
+ function createRunIndexName() {
71
+ const suffix = Math.random().toString(36).slice(2, 8);
72
+ return `${RUN_INDEX_PREFIX}${Date.now()}-${suffix}`;
73
+ }
74
+ exports.createRunIndexName = createRunIndexName;
75
+ async function deleteOpenSearchIndex(indexName) {
76
+ const config = getConfig();
77
+ if (config == null) {
78
+ console.warn(`DB_VECTOR_AWS_OPENSEARCH_* not set; cannot delete index ${indexName}`);
79
+ return;
80
+ }
81
+ const response = await signedRequest(config, 'DELETE', `/${encodeURIComponent(indexName)}`);
82
+ if (!response.ok && response.status !== 404) {
83
+ console.warn(`Deleting OpenSearch index ${indexName} returned ${response.status}: ` +
84
+ (await response.text()));
85
+ }
86
+ }
87
+ exports.deleteOpenSearchIndex = deleteOpenSearchIndex;
88
+ // Removes run indices left behind by runs that were cancelled or ran without
89
+ // the cleanup credentials. Index names carry their creation time.
90
+ async function deleteStaleRunIndices() {
91
+ const config = getConfig();
92
+ if (config == null) {
93
+ return;
94
+ }
95
+ const response = await signedRequest(config, 'GET', `/_cat/indices/${RUN_INDEX_PREFIX}*`);
96
+ if (!response.ok) {
97
+ console.warn(`Listing OpenSearch run indices returned ${response.status}`);
98
+ return;
99
+ }
100
+ const now = Date.now();
101
+ const staleIndices = (await response.text())
102
+ .split('\n')
103
+ .map((line) => line.trim().split(/\s+/)[2])
104
+ .filter((name) => name?.startsWith(RUN_INDEX_PREFIX))
105
+ .filter((name) => {
106
+ const createdAt = Number(name.slice(RUN_INDEX_PREFIX.length).split('-')[0]);
107
+ return Number.isFinite(createdAt) && now - createdAt > STALE_INDEX_AGE_MS;
108
+ });
109
+ for (const name of staleIndices) {
110
+ await deleteOpenSearchIndex(name);
111
+ }
112
+ }
113
+ exports.deleteStaleRunIndices = deleteStaleRunIndices;
@@ -9,7 +9,7 @@ const STABLE_CHANNEL_NAME = 'Stable';
9
9
  const GCP_REGION_ID = 'a6fadf1d-b2a0-44ab-b301-2f9aaf6681dc';
10
10
  const AWS_REGION_ID = '30fcdd4f-05d7-1c7a-54df-8d9defbd250f';
11
11
  const GENERATION_MAP = {
12
- '8.11': '709832f0-eac4-4965-8b55-12088f4a0c38',
12
+ '8.11': '291459ac-6dd3-409e-b837-48ef4d697e5f',
13
13
  '8.10': 'db2e0d30-1e25-4926-8040-70350edbaefb',
14
14
  '8.9': '32478fdf-dbea-47b9-abe2-e9cbfc6c4324',
15
15
  '8.8': '7ff71498-dc2d-486f-9baa-e46b7b503c89',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1640",
3
+ "version": "0.0.1642",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",
@@ -51,7 +51,7 @@
51
51
  target="vectorStore.amazonManagedOpensearch.serverUrl" />
52
52
  <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_REGION}}"
53
53
  target="vectorStore.amazonManagedOpensearch.region" />
54
- <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_INDEX_NAME}}"
54
+ <zeebe:input source="=dbVectorIndexName"
55
55
  target="vectorStore.amazonManagedOpensearch.indexName" />
56
56
  <zeebe:input source="PlainText"
57
57
  target="vectorDatabaseConnectorOperation.documentSource" />
@@ -108,7 +108,7 @@
108
108
  target="vectorStore.amazonManagedOpensearch.serverUrl" />
109
109
  <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_REGION}}"
110
110
  target="vectorStore.amazonManagedOpensearch.region" />
111
- <zeebe:input source="{{secrets.DB_VECTOR_AWS_OPENSEARCH_INDEX_NAME}}"
111
+ <zeebe:input source="=dbVectorIndexName"
112
112
  target="vectorStore.amazonManagedOpensearch.indexName" />
113
113
  </zeebe:ioMapping>
114
114
  <zeebe:taskHeaders>