Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Synthetics] migrate first set of tests #198950

Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ca12729
migrate first set of tests
dominiqueclarke Nov 5, 2024
5719991
add additional deployment agnostic tests
dominiqueclarke Nov 7, 2024
54cfd8b
add additional tests
dominiqueclarke Nov 7, 2024
378e585
add additional tests
dominiqueclarke Nov 8, 2024
5dd98c4
merge main
dominiqueclarke Nov 11, 2024
67ad892
Merge branch 'main' of https://github.com/elastic/kibana into chore/s…
dominiqueclarke Nov 13, 2024
7561381
adjust tests
dominiqueclarke Nov 14, 2024
2753191
Update x-pack/test/api_integration/apis/synthetics/add_monitor_privat…
dominiqueclarke Nov 14, 2024
cd199d2
Update x-pack/test/api_integration/apis/synthetics/add_monitor_privat…
dominiqueclarke Nov 14, 2024
835999c
adjust types
dominiqueclarke Nov 25, 2024
9f6932e
Merge branch 'chore/synthetics-deployment-agnostic' of github.com:dom…
dominiqueclarke Nov 25, 2024
c79ce67
merge main
dominiqueclarke Nov 25, 2024
54674a8
adjust types
dominiqueclarke Nov 25, 2024
9a35f07
Update x-pack/test/api_integration/deployment_agnostic/apis/observabi…
dominiqueclarke Nov 26, 2024
4c4a86c
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 26, 2024
a3f500b
update test
dominiqueclarke Nov 27, 2024
f139c1d
Merge branch 'chore/synthetics-deployment-agnostic' of github.com:dom…
dominiqueclarke Nov 27, 2024
75dfa79
adjust enablement tests
dominiqueclarke Nov 27, 2024
915132a
adjust test
dominiqueclarke Nov 27, 2024
aafbab4
Merge branch 'main' into chore/synthetics-deployment-agnostic
shahzad31 Dec 2, 2024
3521fb9
update readme
shahzad31 Dec 2, 2024
1edb90a
adjust types
dominiqueclarke Dec 2, 2024
3434a89
Merge branch 'chore/synthetics-deployment-agnostic' of github.com:dom…
dominiqueclarke Dec 2, 2024
c8623c4
add codeowners
dominiqueclarke Dec 2, 2024
80e24dc
Merge branch 'main' into chore/synthetics-deployment-agnostic
dominiqueclarke Dec 3, 2024
70ad1af
adjust subset of tests to use private locations only
dominiqueclarke Dec 7, 2024
9f8ab54
Merge branch 'chore/synthetics-deployment-agnostic' of github.com:dom…
dominiqueclarke Dec 7, 2024
2dc3537
adjust additional tests
dominiqueclarke Dec 8, 2024
d79879a
adjust tests for MKI
dominiqueclarke Dec 10, 2024
7cd6037
Merge branch 'main' into chore/synthetics-deployment-agnostic
dominiqueclarke Dec 10, 2024
7bce1ca
Merge branch 'main' into chore/synthetics-deployment-agnostic
dominiqueclarke Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions x-pack/plugins/observability_solution/synthetics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,27 @@ From the `~/x-pack` directory:
Start the server: `node scripts/functional_tests_server --config test/accessibility/config.ts`

Run the uptime `a11y` tests: `node scripts/functional_test_runner.js --config test/accessibility/config.ts --grep=uptime`


## Deployment agnostic API Integration Tests
The Synthetics tests are located under `x-pack/test/api_integration/deployment_agnostic/apis/observability/synthetics` folder. In order to run the SLO tests of your interest, you can grep accordingly. Use the commands below to run all SLO tests (`grep=SyntheticsAPITests`) on stateful or serverless.

### Stateful

```
# start server
node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts
# run tests
node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep=SyntheticsAPITests
```

### Serverless

```
# start server
node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts
# run tests
node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep=SyntheticsAPITests
```
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,10 @@ export class SyntheticsService {
return this.server.coreStart?.elasticsearch.client.asInternalUser;
}

async getOutput() {
async getOutput({ inspect }: { inspect: boolean } = { inspect: false }) {
const { apiKey, isValid } = await getAPIKeyForSyntheticsService({ server: this.server });
if (!isValid) {
// do not check for api key validity if inspecting
if (!isValid && !inspect) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed when I was adjusting the inspect tests, that they would sometime fail on api key not valid. No need to check api key when doing an inspect.

this.server.logger.error(
'API key is not valid. Cannot push monitor configuration to synthetics public testing locations'
);
Expand All @@ -330,7 +331,7 @@ export class SyntheticsService {
const monitors = this.formatConfigs(config);
const license = await this.getLicense();

const output = await this.getOutput();
const output = await this.getOutput({ inspect: true });
if (output) {
return await this.apiClient.inspect({
monitors,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { RoleCredentials, SamlAuthProviderType } from '@kbn/ftr-common-functional-services';
import epct from 'expect';
import moment from 'moment/moment';
import { v4 as uuidv4 } from 'uuid';
import { omit, omitBy } from 'lodash';
import {
ConfigKey,
MonitorTypeEnum,
HTTPFields,
} from '@kbn/synthetics-plugin/common/runtime_types';
import { formatKibanaNamespace } from '@kbn/synthetics-plugin/common/formatters';
import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import { DEFAULT_FIELDS } from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
import {
removeMonitorEmptyValues,
transformPublicKeys,
} from '@kbn/synthetics-plugin/server/routes/monitor_cruds/formatters/saved_object_to_monitor';
import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context';
import { getFixtureJson } from './helpers/get_fixture_json';
import { SyntheticsMonitorTestService } from '../../../services/synthetics_monitor';

export const addMonitorAPIHelper = async (
supertestAPI: any,
monitor: any,
statusCode = 200,
roleAuthc: RoleCredentials,
samlAuth: SamlAuthProviderType
) => {
const result = await supertestAPI
.post(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
.set(roleAuthc.apiKeyHeader)
.set(samlAuth.getInternalRequestHeader())
.send(monitor)
.expect(statusCode);

if (statusCode === 200) {
const { created_at: createdAt, updated_at: updatedAt, id, config_id: configId } = result.body;
expect(id).not.empty();
expect(configId).not.empty();
expect([createdAt, updatedAt].map((d) => moment(d).isValid())).eql([true, true]);
return {
rawBody: result.body,
body: {
...omit(result.body, ['created_at', 'updated_at', 'id', 'config_id', 'form_monitor_type']),
},
};
}
return result.body;
};

export const keyToOmitList = [
'created_at',
'updated_at',
'id',
'config_id',
'form_monitor_type',
'spaceId',
];

export const omitMonitorKeys = (monitor: any) => {
return omitBy(omit(transformPublicKeys(monitor), keyToOmitList), removeMonitorEmptyValues);
};

export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
describe('AddNewMonitorsUI', function () {
this.tags('skipCloud');

const supertestAPI = getService('supertestWithoutAuth');
const samlAuth = getService('samlAuth');
const kibanaServer = getService('kibanaServer');
const monitorTestService = new SyntheticsMonitorTestService(getService);

let _httpMonitorJson: HTTPFields;
let httpMonitorJson: HTTPFields;
let editorRoleAuthc: RoleCredentials;

const addMonitorAPI = async (monitor: any, statusCode = 200) => {
return addMonitorAPIHelper(supertestAPI, monitor, statusCode, editorRoleAuthc, samlAuth);
};

const deleteMonitor = async (
monitorId?: string | string[],
statusCode = 200,
spaceId?: string
) => {
return monitorTestService.deleteMonitor(editorRoleAuthc, monitorId, statusCode, spaceId);
};

before(async () => {
_httpMonitorJson = getFixtureJson('http_monitor');
await kibanaServer.savedObjects.cleanStandardList();
editorRoleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('editor');
});

beforeEach(async () => {
httpMonitorJson = _httpMonitorJson;
});

it('returns the newly added monitor', async () => {
const newMonitor = httpMonitorJson;

const { body: apiResponse } = await addMonitorAPI(newMonitor);

expect(apiResponse).eql(omitMonitorKeys(newMonitor));
});

it('returns bad request if payload is invalid for HTTP monitor', async () => {
// Delete a required property to make payload invalid
const newMonitor = { ...httpMonitorJson, 'check.request.headers': null };
await addMonitorAPI(newMonitor, 400);
});

it('returns bad request if monitor type is invalid', async () => {
const newMonitor = { ...httpMonitorJson, type: 'invalid-data-steam' };

const apiResponse = await addMonitorAPI(newMonitor, 400);

expect(apiResponse.message).eql('Invalid value "invalid-data-steam" supplied to "type"');
});
const localLoc = {
id: 'dev',
label: 'Dev Service',
geo: {
lat: 0,
lon: 0,
},
isServiceManaged: true,
};

it('can create valid monitors without all defaults', async () => {
// Delete a required property to make payload invalid
const newMonitor = {
name: 'Sample name',
type: 'http',
urls: 'https://elastic.co',
locations: [localLoc],
};

const { body: apiResponse } = await addMonitorAPI(newMonitor);

expect(apiResponse).eql(
omitMonitorKeys({
...DEFAULT_FIELDS[MonitorTypeEnum.HTTP],
...newMonitor,
})
);
});

it('can disable retries', async () => {
const maxAttempts = 1;
const newMonitor = {
max_attempts: maxAttempts,
urls: 'https://elastic.co',
name: `Sample name ${uuidv4()}`,
type: 'http',
locations: [localLoc],
};

const { body: apiResponse } = await addMonitorAPI(newMonitor);

epct(apiResponse).toEqual(epct.objectContaining({ retest_on_failure: false }));
});

it('can enable retries with max attempts', async () => {
const maxAttempts = 2;
const newMonitor = {
max_attempts: maxAttempts,
urls: 'https://elastic.co',
name: `Sample name ${uuidv4()}`,
type: 'http',
locations: [localLoc],
};

const { body: apiResponse } = await addMonitorAPI(newMonitor);

epct(apiResponse).toEqual(epct.objectContaining({ retest_on_failure: true }));
});

it('can enable retries', async () => {
const newMonitor = {
retest_on_failure: false,
urls: 'https://elastic.co',
name: `Sample name ${uuidv4()}`,
type: 'http',
locations: [localLoc],
};

const { body: apiResponse } = await addMonitorAPI(newMonitor);

epct(apiResponse).toEqual(epct.objectContaining({ retest_on_failure: false }));
});

it('cannot create a invalid monitor without a monitor type', async () => {
// Delete a required property to make payload invalid
const newMonitor = {
name: 'Sample name',
url: 'https://elastic.co',
locations: [localLoc],
};
await addMonitorAPI(newMonitor, 400);
});

it('omits unknown keys', async () => {
// Delete a required property to make payload invalid
const newMonitor = {
name: 'Sample name',
url: 'https://elastic.co',
unknownKey: 'unknownValue',
type: 'http',
locations: [localLoc],
};
const apiResponse = await addMonitorAPI(newMonitor, 400);
expect(apiResponse.message).not.to.have.keys(
'Invalid monitor key(s) for http type: unknownKey","attributes":{"details":"Invalid monitor key(s) for http type: unknownKey'
);
});

it('sets namespace to Kibana space when not set to a custom namespace', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
const EXPECTED_NAMESPACE = formatKibanaNamespace(SPACE_ID);
const monitor = {
...httpMonitorJson,
[ConfigKey.NAMESPACE]: 'default',
};
let monitorId = '';

try {
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });

const apiResponse = await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
.set(editorRoleAuthc.apiKeyHeader)
.set(samlAuth.getInternalRequestHeader())
.send(monitor)
.expect(200);
monitorId = apiResponse.body.id;
expect(apiResponse.body[ConfigKey.NAMESPACE]).eql(EXPECTED_NAMESPACE);
} finally {
await deleteMonitor(monitorId, 200, SPACE_ID);
}
});

it('preserves the passed namespace when preserve_namespace is passed', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
const monitor = {
...httpMonitorJson,
[ConfigKey.NAMESPACE]: 'default',
};
let monitorId = '';
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });

try {
const apiResponse = await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
.query({ preserve_namespace: true })
.set(editorRoleAuthc.apiKeyHeader)
.set(samlAuth.getInternalRequestHeader())
.send(monitor)
.expect(200);
monitorId = apiResponse.body.id;
expect(apiResponse.body[ConfigKey.NAMESPACE]).eql('default');
} finally {
await deleteMonitor(monitorId, 200, SPACE_ID);
}
});

it('sets namespace to custom namespace when set', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
const monitor = httpMonitorJson;
let monitorId = '';

try {
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });

const apiResponse = await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`)
.set(editorRoleAuthc.apiKeyHeader)
.set(samlAuth.getInternalRequestHeader())
.send(monitor)
.expect(200);
monitorId = apiResponse.body.id;
expect(apiResponse.body[ConfigKey.NAMESPACE]).eql(monitor[ConfigKey.NAMESPACE]);
} finally {
await deleteMonitor(monitorId, 200, SPACE_ID);
}
});
});
}
Loading