Skip to content

Commit 37ac7c7

Browse files
committed
adding ad remote index tests
Signed-off-by: Amit Galitzky <amgalitz@amazon.com>
1 parent 5fe4c27 commit 37ac7c7

File tree

4 files changed

+356
-1
lines changed

4 files changed

+356
-1
lines changed

.github/workflows/release-e2e-workflow-template.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ jobs:
120120
echo "cypress_version=$cypress_version_temp" >> $GITHUB_ENV
121121
- name: Cache Cypress
122122
id: cache-cypress
123-
uses: actions/cache@v1
123+
uses: actions/cache@v4
124124
with:
125125
path: ~/.cache/Cypress
126126
key: cypress-cache-v2-${{ runner.os }}-${{ hashFiles('**/package.json') }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{"index":{"_id": "id-1"}}
2+
{ "value": 1, "secondVal": 1, "timestamp": "2020-01-01"}
3+
{"index":{"_id": "id-2"}}
4+
{ "value": 2, "secondVal": 2, "timestamp": "2020-01-02"}
5+
{"index":{"_id": "id-3"}}
6+
{ "value": 3, "secondVal": 3, "timestamp": "2020-01-03"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import {
7+
AD_FIXTURE_BASE_PATH,
8+
AD_URL,
9+
BACKEND_BASE_PATH,
10+
} from '../../../utils/constants';
11+
import { selectTopItemFromFilter } from '../../../utils/helpers';
12+
13+
context('Create remote detector workflow', () => {
14+
const TEST_DETECTOR_REMOTE_NAME = 'test-detector-remote';
15+
const TEST_DETECTOR_DESCRIPTION = 'Some test detector description.';
16+
const TEST_FEATURE_NAME = 'test-feature';
17+
const TEST_TIMESTAMP_NAME = 'timestamp'; // coming from single_index_response.json fixture
18+
const TEST_INDEX_NAME = 'sample-ad-index';
19+
const TEST_SECOND_INDEX_NAME = 'sample-ad-index-two';
20+
const TEST_REMOTE_INDEX = 'followCluster:sample-ad-index';
21+
const remoteClusterSettings = {
22+
persistent: {
23+
'cluster.remote.followCluster': {
24+
seeds: ['127.0.0.1:9301'],
25+
},
26+
},
27+
};
28+
29+
// Clean up created resources
30+
afterEach(() => {
31+
cy.deleteAllIndices();
32+
cy.deleteADSystemIndices();
33+
});
34+
35+
describe('Remote cluster tests', () => {
36+
before(function () {
37+
cy.visit(AD_URL.OVERVIEW, { timeout: 10000 });
38+
// Use `cy.exec()` to check cluster health before running tests
39+
cy.exec(
40+
`
41+
node -e "
42+
require('http')
43+
.get('${Cypress.env(
44+
'remoteDataSourceNoAuthUrl'
45+
)}/_cluster/health', res => process.exit(res.statusCode === 200 ? 0 : 1))
46+
.on('error', () => process.exit(1));
47+
"`,
48+
{ failOnNonZeroExit: false }
49+
).then((result) => {
50+
if (result.code !== 0) {
51+
Cypress.log({
52+
name: 'Cluster Health',
53+
message:
54+
'Remote cluster is unavailable - skipping all remote detector tests',
55+
});
56+
this.skip(); // Skip the entire test suite
57+
}
58+
});
59+
});
60+
61+
// apply remote cluster settings after cluster health check passes
62+
before(function () {
63+
cy.visit(AD_URL.OVERVIEW, { timeout: 10000 });
64+
cy.request({
65+
method: 'PUT',
66+
url: `${BACKEND_BASE_PATH}/_cluster/settings`,
67+
headers: {
68+
'content-type': 'application/json',
69+
'osd-xsrf': true, // If your backend requires this header
70+
},
71+
body: remoteClusterSettings,
72+
}).then((response) => {
73+
cy.log('Cluster settings update response:', response);
74+
expect(response.status).to.eq(200);
75+
});
76+
cy.wait(5000);
77+
});
78+
79+
// Index some sample data in follower cluster (remote)
80+
beforeEach(() => {
81+
cy.visit(AD_URL.OVERVIEW, { timeout: 10000 });
82+
const remoteEndpointTestData = `${Cypress.env(
83+
'remoteDataSourceNoAuthUrl'
84+
)}/${TEST_INDEX_NAME}/_bulk`;
85+
const remoteEndpointTestDataTwo = `${Cypress.env(
86+
'remoteDataSourceNoAuthUrl'
87+
)}/${TEST_SECOND_INDEX_NAME}/_bulk`;
88+
89+
cy.fixture(AD_FIXTURE_BASE_PATH + 'sample_test_data.txt').then((data) => {
90+
cy.request(
91+
{
92+
method: 'POST',
93+
form: false,
94+
url: remoteEndpointTestData,
95+
headers: {
96+
'content-type': 'application/json;charset=UTF-8',
97+
'osd-xsrf': true,
98+
},
99+
body: data,
100+
},
101+
1000
102+
);
103+
});
104+
cy.fixture(AD_FIXTURE_BASE_PATH + 'sample_remote_test_data.txt').then(
105+
(data) => {
106+
cy.request(
107+
{
108+
method: 'POST',
109+
form: false,
110+
url: remoteEndpointTestDataTwo,
111+
headers: {
112+
'content-type': 'application/json;charset=UTF-8',
113+
'osd-xsrf': true,
114+
},
115+
body: data,
116+
},
117+
1000
118+
);
119+
}
120+
);
121+
cy.fixture(AD_FIXTURE_BASE_PATH + 'sample_test_data.txt').then((data) => {
122+
cy.request({
123+
method: 'POST',
124+
form: false,
125+
url: 'api/console/proxy',
126+
headers: {
127+
'content-type': 'application/json;charset=UTF-8',
128+
'osd-xsrf': true,
129+
},
130+
qs: {
131+
path: `${TEST_INDEX_NAME}/_bulk`,
132+
method: 'POST',
133+
},
134+
body: data,
135+
});
136+
});
137+
});
138+
139+
it('Full creation - based on remote index', () => {
140+
// Define detector step
141+
cy.visit(AD_URL.CREATE_AD);
142+
cy.getElementByTestId('defineOrEditDetectorTitle').should('exist');
143+
cy.getElementByTestId('detectorNameTextInput').type(
144+
TEST_DETECTOR_REMOTE_NAME
145+
);
146+
cy.getElementByTestId('detectorDescriptionTextInput').type(
147+
TEST_DETECTOR_DESCRIPTION
148+
);
149+
150+
cy.getElementByTestId('clustersFilter').click();
151+
cy.getElementByTestId('clustersFilter').click();
152+
cy.contains(
153+
'.euiComboBoxOption__content',
154+
'followCluster (Remote)'
155+
).click();
156+
157+
cy.wait(3000);
158+
159+
cy.getElementByTestId('indicesFilter').click();
160+
cy.wait(1000);
161+
cy.contains(
162+
'.euiComboBoxOption__content',
163+
'followCluster:sample-ad-index'
164+
).click();
165+
166+
selectTopItemFromFilter('timestampFilter', false);
167+
168+
cy.getElementByTestId('defineDetectorNextButton').click();
169+
// cy.wait(5000);
170+
cy.getElementByTestId('defineOrEditDetectorTitle').should('not.exist');
171+
cy.getElementByTestId('configureOrEditModelConfigurationTitle').should(
172+
'exist'
173+
);
174+
175+
// Configure model step
176+
cy.getElementByTestId('featureNameTextInput-0').type(TEST_FEATURE_NAME);
177+
selectTopItemFromFilter('featureFieldTextInput-0', false);
178+
cy.getElementByTestId('configureModelNextButton').click();
179+
cy.getElementByTestId('configureOrEditModelConfigurationTitle').should(
180+
'not.exist'
181+
);
182+
cy.getElementByTestId('detectorJobsTitle').should('exist');
183+
184+
// Set up detector jobs step
185+
cy.getElementByTestId('detectorJobsNextButton').click();
186+
cy.getElementByTestId('detectorJobsTitle').should('not.exist');
187+
cy.getElementByTestId('reviewAndCreateTitle').should('exist');
188+
189+
// Review and create step
190+
cy.getElementByTestId('detectorNameCell').contains(
191+
TEST_DETECTOR_REMOTE_NAME
192+
);
193+
cy.getElementByTestId('detectorDescriptionCell').contains(
194+
TEST_DETECTOR_DESCRIPTION
195+
);
196+
cy.getElementByTestId('indexNameCell').contains(TEST_REMOTE_INDEX);
197+
cy.getElementByTestId('timestampNameCell').contains(TEST_TIMESTAMP_NAME);
198+
cy.getElementByTestId('featureTable').contains(TEST_FEATURE_NAME);
199+
200+
cy.getElementByTestId('createDetectorButton').click();
201+
202+
cy.wait(5000);
203+
204+
// Lands on the config page by default.
205+
cy.getElementByTestId('detectorSettingsHeader').should('exist');
206+
cy.getElementByTestId('modelConfigurationHeader').should('exist');
207+
cy.getElementByTestId('detectorJobsHeader').should('exist');
208+
});
209+
210+
it('Full creation - based on multiple indexes', () => {
211+
// Define detector step
212+
cy.visit(AD_URL.CREATE_AD);
213+
cy.getElementByTestId('defineOrEditDetectorTitle').should('exist');
214+
cy.getElementByTestId('detectorNameTextInput').type(
215+
TEST_DETECTOR_REMOTE_NAME
216+
);
217+
cy.getElementByTestId('detectorDescriptionTextInput').type(
218+
TEST_DETECTOR_DESCRIPTION
219+
);
220+
221+
cy.getElementByTestId('clustersFilter').click();
222+
cy.getElementByTestId('clustersFilter').click();
223+
cy.contains(
224+
'.euiComboBoxOption__content',
225+
'followCluster (Remote)'
226+
).click();
227+
228+
cy.wait(3000);
229+
230+
cy.getElementByTestId('indicesFilter').click();
231+
cy.wait(1000);
232+
cy.contains(
233+
'.euiComboBoxOption__content',
234+
'followCluster:sample-ad-index'
235+
).click();
236+
cy.contains('.euiComboBoxOption__content', 'sample-ad-index').click();
237+
cy.contains(
238+
'.euiComboBoxOption__content',
239+
'followCluster:sample-ad-index-two'
240+
).click();
241+
242+
selectTopItemFromFilter('timestampFilter', false);
243+
244+
cy.getElementByTestId('defineDetectorNextButton').click();
245+
cy.getElementByTestId('defineOrEditDetectorTitle').should('not.exist');
246+
cy.getElementByTestId('configureOrEditModelConfigurationTitle').should(
247+
'exist'
248+
);
249+
250+
// Configure model step
251+
cy.getElementByTestId('featureNameTextInput-0').type(TEST_FEATURE_NAME);
252+
253+
// check fields from both indices are present
254+
cy.getElementByTestId('featureFieldTextInput-0').click();
255+
cy.wait(500);
256+
cy.get('.euiComboBoxOption__content')
257+
.contains('secondVal')
258+
.should('be.visible');
259+
cy.get('.euiComboBoxOption__content')
260+
.contains('value')
261+
.should('be.visible');
262+
cy.get('.euiComboBoxOption__content')
263+
.contains('value')
264+
.should('exist')
265+
.click({ force: true });
266+
267+
cy.getElementByTestId('configureModelNextButton').click();
268+
cy.getElementByTestId('configureOrEditModelConfigurationTitle').should(
269+
'not.exist'
270+
);
271+
cy.getElementByTestId('detectorJobsTitle').should('exist');
272+
273+
// Set up detector jobs step
274+
cy.getElementByTestId('detectorJobsNextButton').click();
275+
cy.getElementByTestId('detectorJobsTitle').should('not.exist');
276+
cy.getElementByTestId('reviewAndCreateTitle').should('exist');
277+
278+
// Review and create step
279+
cy.getElementByTestId('detectorNameCell').contains(
280+
TEST_DETECTOR_REMOTE_NAME
281+
);
282+
cy.getElementByTestId('detectorDescriptionCell').contains(
283+
TEST_DETECTOR_DESCRIPTION
284+
);
285+
cy.getElementByTestId('indexNameCell').contains(TEST_INDEX_NAME);
286+
cy.getElementByTestId('timestampNameCell').contains(TEST_TIMESTAMP_NAME);
287+
cy.getElementByTestId('featureTable').contains(TEST_FEATURE_NAME);
288+
289+
// verify "Data connection" is visible
290+
cy.getElementByTestId('indexNameCellViewAllLink').contains('View all');
291+
cy.getElementByTestId('indexNameCellViewAllLink')
292+
.contains('View all')
293+
.click({ force: true });
294+
295+
cy.wait(500);
296+
297+
cy.contains('span.euiTableCellContent__text', 'Data connection').should(
298+
'be.visible'
299+
);
300+
cy.contains('followCluster (Remote)').should('be.visible');
301+
cy.contains('leaderCluster (Local)').should('be.visible');
302+
cy.contains('sample-ad-index').should('be.visible');
303+
304+
cy.getElementByTestId('euiFlyoutCloseButton').click();
305+
cy.wait(500);
306+
307+
cy.getElementByTestId('createDetectorButton').click();
308+
309+
cy.wait(3500);
310+
311+
// Lands on the config page by default.
312+
cy.getElementByTestId('detectorSettingsHeader').should('exist');
313+
cy.getElementByTestId('modelConfigurationHeader').should('exist');
314+
cy.getElementByTestId('detectorJobsHeader').should('exist');
315+
316+
cy.getElementByTestId('indexNameCellViewAllLink').contains('View all');
317+
318+
// verify "Data connection" is visible on config page
319+
cy.getElementByTestId('indexNameCellViewAllLink').contains('View all');
320+
cy.getElementByTestId('indexNameCellViewAllLink')
321+
.contains('View all')
322+
.click({ force: true });
323+
324+
cy.wait(500);
325+
326+
cy.contains('span.euiTableCellContent__text', 'Data connection').should(
327+
'be.visible'
328+
);
329+
cy.contains('followCluster (Remote)').should('be.visible');
330+
cy.contains('leaderCluster (Local)').should('be.visible');
331+
cy.contains('sample-ad-index').should('be.visible');
332+
});
333+
});
334+
});

cypress/utils/commands.js

+15
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,21 @@ Cypress.Commands.add(
292292
}
293293
);
294294

295+
Cypress.Commands.add('checkClusterHealth', () => {
296+
return cy
297+
.request({
298+
method: 'GET',
299+
url: `${Cypress.env('remoteDataSourceNoAuthUrl')}/_cluster/health`,
300+
failOnStatusCode: false,
301+
})
302+
.then((response) => {
303+
return response.status === 200;
304+
})
305+
.catch(() => {
306+
return false;
307+
});
308+
});
309+
295310
Cypress.Commands.add('createIndex', (index, policyID = null, settings = {}) => {
296311
cy.request('PUT', `${Cypress.env('openSearchUrl')}/${index}`, settings);
297312
if (policyID != null) {

0 commit comments

Comments
 (0)