Skip to content

Commit

Permalink
[8.x] [scout] adding unit tests (#204567) (#204980)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to `8.x`:
- [[scout] adding unit tests
(#204567)](#204567)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Dzmitry
Lemechko","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-19T16:36:07Z","message":"[scout]
adding unit tests (#204567)\n\n## Summary\r\n\r\nAdding tests and making
adjustments/fixes based on the findings.\r\n\r\nNote: no integration
tests were added to verify servers start as it is\r\nmostly equal to
`@kbn-test` functionality that has jest integration\r\ntests. We can add
it later, when Scout has specific logic.\r\n\r\nHow to run: `node
scripts/jest
--config\r\npackages/kbn-scout/jest.config.js`\r\n\r\nScope:\r\n\r\n```\r\n
PASS packages/kbn-scout/src/config/config.test.ts\r\n PASS
packages/kbn-scout/src/config/loader/read_config_file.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/get_config_file.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/load_servers_config.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/save_scout_test_config.test.ts\r\n
PASS packages/kbn-scout/src/playwright/config/create_config.test.ts\r\n
PASS
packages/kbn-scout/src/playwright/runner/config_validator.test.ts\r\n
PASS packages/kbn-scout/src/playwright/runner/flags.test.ts\r\n PASS
packages/kbn-scout/src/playwright/utils/runner_utils.test.ts\r\n PASS
packages/kbn-scout/src/servers/flags.test.ts\r\n```","sha":"2ba3247526331ec152f2d1e236c40635e86b3ce3","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","test:scout"],"title":"[scout]
adding unit
tests","number":204567,"url":"https://github.com/elastic/kibana/pull/204567","mergeCommit":{"message":"[scout]
adding unit tests (#204567)\n\n## Summary\r\n\r\nAdding tests and making
adjustments/fixes based on the findings.\r\n\r\nNote: no integration
tests were added to verify servers start as it is\r\nmostly equal to
`@kbn-test` functionality that has jest integration\r\ntests. We can add
it later, when Scout has specific logic.\r\n\r\nHow to run: `node
scripts/jest
--config\r\npackages/kbn-scout/jest.config.js`\r\n\r\nScope:\r\n\r\n```\r\n
PASS packages/kbn-scout/src/config/config.test.ts\r\n PASS
packages/kbn-scout/src/config/loader/read_config_file.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/get_config_file.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/load_servers_config.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/save_scout_test_config.test.ts\r\n
PASS packages/kbn-scout/src/playwright/config/create_config.test.ts\r\n
PASS
packages/kbn-scout/src/playwright/runner/config_validator.test.ts\r\n
PASS packages/kbn-scout/src/playwright/runner/flags.test.ts\r\n PASS
packages/kbn-scout/src/playwright/utils/runner_utils.test.ts\r\n PASS
packages/kbn-scout/src/servers/flags.test.ts\r\n```","sha":"2ba3247526331ec152f2d1e236c40635e86b3ce3"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/204567","number":204567,"mergeCommit":{"message":"[scout]
adding unit tests (#204567)\n\n## Summary\r\n\r\nAdding tests and making
adjustments/fixes based on the findings.\r\n\r\nNote: no integration
tests were added to verify servers start as it is\r\nmostly equal to
`@kbn-test` functionality that has jest integration\r\ntests. We can add
it later, when Scout has specific logic.\r\n\r\nHow to run: `node
scripts/jest
--config\r\npackages/kbn-scout/jest.config.js`\r\n\r\nScope:\r\n\r\n```\r\n
PASS packages/kbn-scout/src/config/config.test.ts\r\n PASS
packages/kbn-scout/src/config/loader/read_config_file.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/get_config_file.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/load_servers_config.test.ts\r\n PASS
packages/kbn-scout/src/config/utils/save_scout_test_config.test.ts\r\n
PASS packages/kbn-scout/src/playwright/config/create_config.test.ts\r\n
PASS
packages/kbn-scout/src/playwright/runner/config_validator.test.ts\r\n
PASS packages/kbn-scout/src/playwright/runner/flags.test.ts\r\n PASS
packages/kbn-scout/src/playwright/utils/runner_utils.test.ts\r\n PASS
packages/kbn-scout/src/servers/flags.test.ts\r\n```","sha":"2ba3247526331ec152f2d1e236c40635e86b3ce3"}}]}]
BACKPORT-->

Co-authored-by: Dzmitry Lemechko <[email protected]>
  • Loading branch information
kibanamachine and dmlemeshko authored Dec 19, 2024
1 parent d3a325a commit 2f17390
Show file tree
Hide file tree
Showing 43 changed files with 1,234 additions and 227 deletions.
6 changes: 6 additions & 0 deletions packages/kbn-scout/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ npx playwright test --config <plugin-path>/ui_tests/playwright.config.ts
We welcome contributions to improve and extend `kbn-scout`. This guide will help you get started, add new features, and align with existing project standards.
Make sure to run unit tests before opening the PR:
```bash
node scripts/jest --config packages/kbn-scout/jest.config.js
```
#### Setting Up the Environment
Ensure you have the latest local copy of the Kibana repository.
Expand Down
6 changes: 3 additions & 3 deletions packages/kbn-scout/src/common/services/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import { KbnClient, createEsClientForTesting } from '@kbn/test';
import type { ToolingLog } from '@kbn/tooling-log';
import { ScoutServerConfig } from '../../types';
import { ScoutTestConfig } from '../../types';
import { serviceLoadedMsg } from '../../playwright/utils';

interface ClientOptions {
Expand All @@ -29,7 +29,7 @@ function createClientUrlWithAuth({ serviceName, url, username, password, log }:
return clientUrl.toString();
}

export function createEsClient(config: ScoutServerConfig, log: ToolingLog) {
export function createEsClient(config: ScoutTestConfig, log: ToolingLog) {
const { username, password } = config.auth;
const elasticsearchUrl = createClientUrlWithAuth({
serviceName: 'Es',
Expand All @@ -45,7 +45,7 @@ export function createEsClient(config: ScoutServerConfig, log: ToolingLog) {
});
}

export function createKbnClient(config: ScoutServerConfig, log: ToolingLog) {
export function createKbnClient(config: ScoutTestConfig, log: ToolingLog) {
const kibanaUrl = createClientUrlWithAuth({
serviceName: 'Kbn',
url: config.hosts.kibana,
Expand Down
4 changes: 2 additions & 2 deletions packages/kbn-scout/src/common/services/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import path from 'path';
import fs from 'fs';
import { ToolingLog } from '@kbn/tooling-log';
import { ScoutServerConfig } from '../../types';
import { ScoutTestConfig } from '../../types';
import { serviceLoadedMsg } from '../../playwright/utils';

export function createScoutConfig(configDir: string, configName: string, log: ToolingLog) {
Expand All @@ -21,7 +21,7 @@ export function createScoutConfig(configDir: string, configName: string, log: To
const configPath = path.join(configDir, `${configName}.json`);
log.info(`Reading test servers confiuration from file: ${configPath}`);

const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as ScoutServerConfig;
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as ScoutTestConfig;

log.debug(serviceLoadedMsg('config'));

Expand Down
4 changes: 2 additions & 2 deletions packages/kbn-scout/src/common/services/kibana_url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import type { ToolingLog } from '@kbn/tooling-log';
import { ScoutServerConfig } from '../../types';
import { ScoutTestConfig } from '../../types';
import { serviceLoadedMsg } from '../../playwright/utils';

export interface PathOptions {
Expand Down Expand Up @@ -64,7 +64,7 @@ export class KibanaUrl {
}
}

export function createKbnUrl(scoutConfig: ScoutServerConfig, log: ToolingLog) {
export function createKbnUrl(scoutConfig: ScoutTestConfig, log: ToolingLog) {
const kbnUrl = new KibanaUrl(new URL(scoutConfig.hosts.kibana));

log.debug(serviceLoadedMsg('kbnUrl'));
Expand Down
8 changes: 4 additions & 4 deletions packages/kbn-scout/src/common/services/saml_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ import {
import { REPO_ROOT } from '@kbn/repo-info';
import { HostOptions, SamlSessionManager } from '@kbn/test';
import { ToolingLog } from '@kbn/tooling-log';
import { ScoutServerConfig } from '../../types';
import { ScoutTestConfig } from '../../types';
import { Protocol } from '../../playwright/types';
import { serviceLoadedMsg } from '../../playwright/utils';

const getResourceDirPath = (config: ScoutServerConfig) => {
const getResourceDirPath = (config: ScoutTestConfig) => {
return config.serverless
? path.resolve(SERVERLESS_ROLES_ROOT_PATH, config.projectType!)
: path.resolve(REPO_ROOT, STATEFUL_ROLES_ROOT_PATH);
};

const createKibanaHostOptions = (config: ScoutServerConfig): HostOptions => {
const createKibanaHostOptions = (config: ScoutTestConfig): HostOptions => {
const kibanaUrl = new URL(config.hosts.kibana);
kibanaUrl.username = config.auth.username;
kibanaUrl.password = config.auth.password;
Expand All @@ -42,7 +42,7 @@ const createKibanaHostOptions = (config: ScoutServerConfig): HostOptions => {
};

export const createSamlSessionManager = (
config: ScoutServerConfig,
config: ScoutTestConfig,
log: ToolingLog
): SamlSessionManager => {
const resourceDirPath = getResourceDirPath(config);
Expand Down
128 changes: 128 additions & 0 deletions packages/kbn-scout/src/config/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { Config } from './config';

describe('Config.getScoutTestConfig', () => {
it(`should return a properly structured 'ScoutTestConfig' object for 'stateful'`, async () => {
const config = new Config({
servers: {
elasticsearch: {
protocol: 'http',
hostname: 'localhost',
port: 9220,
username: 'kibana_system',
password: 'changeme',
},
kibana: {
protocol: 'http',
hostname: 'localhost',
port: 5620,
username: 'elastic',
password: 'changeme',
},
},
dockerServers: {},
esTestCluster: {
from: 'snapshot',
files: [],
serverArgs: [],
ssl: false,
},
kbnTestServer: {
buildArgs: [],
env: {},
sourceArgs: [],
serverArgs: [],
},
});

const scoutConfig = config.getScoutTestConfig();

const expectedConfig = {
serverless: false,
projectType: undefined,
isCloud: false,
license: 'trial',
cloudUsersFilePath: expect.stringContaining('.ftr/role_users.json'),
hosts: {
kibana: 'http://localhost:5620',
elasticsearch: 'http://localhost:9220',
},
auth: {
username: 'elastic',
password: 'changeme',
},
metadata: {
generatedOn: expect.any(String),
config: expect.any(Object),
},
};

expect(scoutConfig).toEqual(expectedConfig);
});

it(`should return a properly structured 'ScoutTestConfig' object for 'serverless=es'`, async () => {
const config = new Config({
serverless: true,
servers: {
elasticsearch: {
protocol: 'https',
hostname: 'localhost',
port: 9220,
username: 'elastic_serverless',
password: 'changeme',
},
kibana: {
protocol: 'http',
hostname: 'localhost',
port: 5620,
username: 'elastic_serverless',
password: 'changeme',
},
},
dockerServers: {},
esTestCluster: {
from: 'serverless',
files: [],
serverArgs: [],
ssl: true,
},
kbnTestServer: {
buildArgs: [],
env: {},
sourceArgs: [],
serverArgs: ['--serverless=es'],
},
});

const scoutConfig = config.getScoutTestConfig();
const expectedConfig = {
serverless: true,
projectType: 'es',
isCloud: false,
license: 'trial',
cloudUsersFilePath: expect.stringContaining('.ftr/role_users.json'),
hosts: {
kibana: 'http://localhost:5620',
elasticsearch: 'https://localhost:9220',
},
auth: {
username: 'elastic_serverless',
password: 'changeme',
},
metadata: {
generatedOn: expect.any(String),
config: expect.any(Object),
},
};

expect(scoutConfig).toEqual(expectedConfig);
});
});
11 changes: 6 additions & 5 deletions packages/kbn-scout/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import Path from 'path';
import { cloneDeepWith, get, has, toPath } from 'lodash';
import { REPO_ROOT } from '@kbn/repo-info';
import { schema } from './schema';
import { ScoutServerConfig } from '../types';
import { formatCurrentDate, getProjectType } from './utils';
import { ScoutServerConfig, ScoutTestConfig } from '../types';
import { formatCurrentDate, getProjectType } from './utils/utils';

const $values = Symbol('values');

export class Config {
private [$values]: Record<string, any>;
private [$values]: ScoutServerConfig;

constructor(data: Record<string, any>) {
constructor(data: ScoutServerConfig) {
const { error, value } = schema.validate(data, {
abortEarly: false,
});
Expand Down Expand Up @@ -104,13 +104,14 @@ export class Config {
});
}

public getTestServersConfig(): ScoutServerConfig {
public getScoutTestConfig(): ScoutTestConfig {
return {
serverless: this.get('serverless'),
projectType: this.get('serverless')
? getProjectType(this.get('kbnTestServer.serverArgs'))
: undefined,
isCloud: false,
license: this.get('esTestCluster.license'),
cloudUsersFilePath: Path.resolve(REPO_ROOT, '.ftr', 'role_users.json'),
hosts: {
kibana: Url.format({
Expand Down
5 changes: 2 additions & 3 deletions packages/kbn-scout/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export { loadConfig } from './loader/config_load';
export { getConfigFilePath } from './get_config_file';
export { loadServersConfig } from './utils';
export { readConfigFile } from './loader';
export { getConfigFilePath, loadServersConfig } from './utils';
export type { Config } from './config';
10 changes: 10 additions & 0 deletions packages/kbn-scout/src/config/loader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export { readConfigFile } from './read_config_file';
83 changes: 83 additions & 0 deletions packages/kbn-scout/src/config/loader/read_config_file.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import path from 'path';
import { Config } from '../config';
import { readConfigFile } from './read_config_file';

jest.mock('path', () => ({
resolve: jest.fn(),
}));

jest.mock('../config', () => ({
Config: jest.fn(),
}));

describe('readConfigFile', () => {
const configPath = '/mock/config/path';
const resolvedPath = '/resolved/config/path';
const mockPathResolve = path.resolve as jest.Mock;
const mockConfigConstructor = Config as jest.Mock;

beforeEach(() => {
jest.clearAllMocks();
jest.resetModules();
});

it(`should load and return a valid 'Config' instance when the config file exports 'servers'`, async () => {
const mockConfigModule = { servers: { host: 'localhost', port: 5601 } };

mockPathResolve.mockReturnValueOnce(resolvedPath);

jest.isolateModules(async () => {
jest.mock(resolvedPath, () => mockConfigModule, { virtual: true });
mockConfigConstructor.mockImplementation((servers) => ({ servers }));

const result = await readConfigFile(configPath);

expect(path.resolve).toHaveBeenCalledWith(configPath);
expect(result).toEqual({ servers: mockConfigModule.servers });
});
});

it(`should throw an error if the config file does not export 'servers'`, async () => {
const mockConfigModule = { otherProperty: 'value' };

mockPathResolve.mockReturnValueOnce(resolvedPath);

jest.isolateModules(async () => {
jest.mock(resolvedPath, () => mockConfigModule, { virtual: true });

await expect(readConfigFile(configPath)).rejects.toThrow(
`No 'servers' found in the config file at path: ${resolvedPath}`
);
expect(path.resolve).toHaveBeenCalledWith(configPath);
});
});

it('should throw an error if the config file cannot be loaded', async () => {
mockPathResolve.mockReturnValueOnce(resolvedPath);

jest.isolateModules(async () => {
const message = 'Module not found';
jest.mock(
resolvedPath,
() => {
throw new Error(message);
},
{ virtual: true }
);

await expect(readConfigFile(configPath)).rejects.toThrow(
`Failed to load config from ${configPath}: ${message}`
);
expect(path.resolve).toHaveBeenCalledWith(configPath);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import path from 'path';
import { Config } from '../config';
import { ScoutServerConfig } from '../../types';

/**
* Dynamically loads server configuration file in the "kbn-scout" framework. It reads
Expand All @@ -17,13 +18,13 @@ import { Config } from '../config';
* @param configPath Path to the configuration file to be loaded.
* @returns Config instance that is used to start local servers
*/
export const loadConfig = async (configPath: string): Promise<Config> => {
export const readConfigFile = async (configPath: string): Promise<Config> => {
try {
const absolutePath = path.resolve(configPath);
const configModule = await import(absolutePath);

if (configModule.servers) {
return new Config(configModule.servers);
return new Config(configModule.servers as ScoutServerConfig);
} else {
throw new Error(`No 'servers' found in the config file at path: ${absolutePath}`);
}
Expand Down
Loading

0 comments on commit 2f17390

Please sign in to comment.