Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ import type { KbnClient, ScoutLogger } from '../../../../../../common';
import { measurePerformanceAsync } from '../../../../../../common';

export interface SpacesApiService {
create: (space: { id: string; name: string }) => Promise<void>;
create: (space: { id: string; name?: string; disabledFeatures?: string[] }) => Promise<void>;
delete: (id: string) => Promise<void>;
}

export const getSpacesApiHelper = (log: ScoutLogger, kbnClient: KbnClient): SpacesApiService => {
return {
create: async (space: { id: string; name: string }) => {
await measurePerformanceAsync(log, `spacesApi.create(${space.id})`, async () => {
create: async ({ id, name = id, disabledFeatures = [] }) => {
await measurePerformanceAsync(log, `spacesApi.create(${id})`, async () => {
await kbnClient.request({
method: 'POST',
path: '/api/spaces/space',
body: space,
body: { id, name, disabledFeatures },
});
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ Custom asymmetric matchers for use with `toMatchObject`:

- **`expect.toBeGreaterThan(n)`** - Matches if value > n
- **`expect.toBeLessThan(n)`** - Matches if value < n
- **`expect.stringContaining(s)`** - Matches a string that contains the given substring

```typescript
expect(response).toMatchObject({
body: {
count: expect.toBeGreaterThan(0),
limit: expect.toBeLessThan(100),
message: expect.stringContaining('method [get] exists'),
},
});
```
Expand Down Expand Up @@ -87,6 +89,7 @@ The following [Playwright matchers](https://playwright.dev/docs/api/class-generi

- `expect.arrayContaining(array)` - array contains all expected elements
- `expect.objectContaining(object)` - object contains expected properties
- `expect.stringContaining(string)` - string contains the expected substring

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ describe('custom asymmetric matchers', () => {
});
});

describe('expect.stringContaining()', () => {
it('matches strings that contain the expected substring', () => {
expect(() =>
apiExpect({ message: 'hello world' }).toMatchObject({
message: apiExpect.stringContaining('hello'),
})
).not.toThrow();
expect(() =>
apiExpect({ message: 'hello world' }).toMatchObject({
message: apiExpect.stringContaining('missing'),
})
).toThrow();
});

it('does not match non-string values', () => {
expect(() =>
apiExpect({ code: 404 }).toMatchObject({ code: apiExpect.stringContaining('4') })
).toThrow();
});
});

it('supports nesting and combining with Playwright matchers', () => {
const response = {
data: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ export const asymmetricMatchers: AsymmetricMatchers = {
/** Matches an object that contains and matches all of the properties in the expected object */
objectContaining: <T extends Record<string, unknown>>(expected: T) =>
baseExpect.objectContaining(expected),

/** Matches a string that contains the expected substring */
stringContaining: (expected: string) =>
createAsymmetricMatcher(
(actual) => typeof actual === 'string' && actual.includes(expected),
`stringContaining(${JSON.stringify(expected)})`
),
};
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,6 @@ export interface AsymmetricMatchers {
arrayContaining<T>(expected: T[]): AsymmetricMatcher;
/** Matches an object that contains and matches all of the properties in the expected object */
objectContaining<T extends Record<string, unknown>>(expected: T): AsymmetricMatcher;
/** Matches a string that contains the expected substring */
stringContaining(expected: string): AsymmetricMatcher;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ export const SERVICE_KEY_LEGACY = 'index_pattern';
export const SERVICE_KEY = 'data_view';

export const ID_OVER_MAX_LENGTH = 'x'.repeat(1759);

export const INTERNAL_COMMON_HEADERS = {
'kbn-xsrf': 'some-xsrf-token',
[X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'kibana',
[ELASTIC_HTTP_VERSION_HEADER]: '1',
};

export const FIELDS_FOR_WILDCARD_PATH = 'internal/data_views/_fields_for_wildcard';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 { apiTest, tags, type RoleApiCredentials } from '@kbn/scout';
import { expect } from '@kbn/scout/api';
import {
ES_ARCHIVE_BASIC_INDEX,
FIELDS_FOR_WILDCARD_PATH,
INTERNAL_COMMON_HEADERS,
} from '../../fixtures/constants';

apiTest.describe(
'rollup data views - fields for wildcard',
{
tag: [
...tags.serverless.observability.complete,
...tags.serverless.search,
...tags.serverless.security.complete,
],
},
() => {
let adminApiCredentials: RoleApiCredentials;

apiTest.beforeAll(async ({ requestAuth, esArchiver }) => {
adminApiCredentials = await requestAuth.getApiKey('admin');
await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX);
});

apiTest(
'returns 200 and best effort response despite lack of rollup support',
async ({ apiClient }) => {
const response = await apiClient.get(
`${FIELDS_FOR_WILDCARD_PATH}?pattern=basic_index&type=rollup&rollup_index=bar`,
{
headers: {
...INTERNAL_COMMON_HEADERS,
...adminApiCredentials.apiKeyHeader,
},
}
);

expect(response).toHaveStatusCode(200);
expect(response.body.fields).toHaveLength(5);
}
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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 { apiTest, tags, type RoleApiCredentials } from '@kbn/scout';
import { expect } from '@kbn/scout/api';
import {
COMMON_HEADERS,
DATA_VIEW_PATH,
ES_ARCHIVE_BASIC_INDEX,
SERVICE_KEY,
} from '../fixtures/constants';

apiTest.describe(
'scripted fields disabled',
{
tag: [
...tags.serverless.observability.complete,
...tags.serverless.search,
...tags.serverless.security.complete,
],
},
() => {
let adminApiCredentials: RoleApiCredentials;
let createdDataViewId: string | undefined;

apiTest.beforeAll(async ({ requestAuth, esArchiver, kbnClient }) => {
adminApiCredentials = await requestAuth.getApiKey('admin');
await kbnClient.savedObjects.cleanStandardList();
await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX);
});

apiTest.afterEach(async ({ apiServices }) => {
if (createdDataViewId) {
await apiServices.dataViews.delete(createdDataViewId);
createdDataViewId = undefined;
}
});

apiTest('scripted fields are ignored when disabled', async ({ apiClient }) => {
const response = await apiClient.post(DATA_VIEW_PATH, {
headers: {
...COMMON_HEADERS,
...adminApiCredentials.apiKeyHeader,
},
body: {
[SERVICE_KEY]: {
title: 'basic_index',
fields: {
foo_scripted: {
name: 'foo_scripted',
type: 'string',
scripted: true,
script: "doc['field_name'].value",
},
},
},
},
});

expect(response).toHaveStatusCode(200);
createdDataViewId = response.body[SERVICE_KEY].id;
expect(response.body[SERVICE_KEY].fields.foo_scripted).toBeUndefined();
});
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ export const COMMON_HEADERS = {
'x-elastic-internal-origin': 'kibana',
'Content-Type': 'application/json;charset=UTF-8',
};

export const COMMON_HEADERS_NO_INTERNAL = {
'kbn-xsrf': 'some-xsrf-token',
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ export interface SpacesRequestAuthFixture extends RequestAuthFixture {
getSavedObjectsManagementApiKey: () => Promise<RoleApiCredentials>;
}

export interface SpacesApiFixtures {
export const apiTest = base.extend<{
requestAuth: SpacesRequestAuthFixture;
}

export const apiTest = base.extend<SpacesApiFixtures>({
}>({
requestAuth: async ({ requestAuth }, use) => {
const getSavedObjectsManagementApiKey = async (): Promise<RoleApiCredentials> => {
return await requestAuth.getApiKeyForCustomRole({
Expand All @@ -34,17 +32,13 @@ export const apiTest = base.extend<SpacesApiFixtures>({
feature: {
savedObjectsManagement: ['all'],
},
spaces: ['*'], // Access to all spaces
spaces: ['*'],
},
],
});
};

const extendedRequestAuth: SpacesRequestAuthFixture = {
...requestAuth,
getSavedObjectsManagementApiKey,
};

await use(extendedRequestAuth);
const extended: SpacesRequestAuthFixture = { ...requestAuth, getSavedObjectsManagementApiKey };
await use(extended);
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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 { tags } from '@kbn/scout';
import { expect } from '@kbn/scout/api';

import { COMMON_HEADERS } from '../constants';
import { apiTest } from '../fixtures';

apiTest.describe(
'Get active space',
{
tag: tags.deploymentAgnostic,
},
() => {
let cookieHeader: Record<string, string>;

apiTest.beforeAll(async ({ apiServices, samlAuth }) => {
({ cookieHeader } = await samlAuth.asInteractiveUser('admin'));
await apiServices.spaces.create({ id: 'foo-space' });
});

apiTest.afterAll(async ({ apiServices }) => {
await apiServices.spaces.delete('foo-space');
});

apiTest('returns the default space', async ({ apiClient }) => {
const response = await apiClient.get('internal/spaces/_active_space', {
headers: { ...COMMON_HEADERS, ...cookieHeader },
});

expect(response).toHaveStatusCode(200);
const { id, name, _reserved } = response.body;
expect({ id, name, _reserved }).toStrictEqual({
id: 'default',
name: 'Default',
_reserved: true,
});
});

apiTest('returns the default space when explicitly referenced', async ({ apiClient }) => {
const response = await apiClient.get('s/default/internal/spaces/_active_space', {
headers: { ...COMMON_HEADERS, ...cookieHeader },
});

expect(response).toHaveStatusCode(200);
const { id, name, _reserved } = response.body;
expect({ id, name, _reserved }).toStrictEqual({
id: 'default',
name: 'Default',
_reserved: true,
});
});

apiTest('returns the foo space', async ({ apiClient }) => {
const { body } = await apiClient.get('s/foo-space/internal/spaces/_active_space', {
headers: { ...COMMON_HEADERS, ...cookieHeader },
});

expect(body).toMatchObject({
id: 'foo-space',
name: 'foo-space',
disabledFeatures: [],
});
});

apiTest('returns 404 when the space is not found', async ({ apiClient }) => {
const response = await apiClient.get('s/not-found-space/internal/spaces/_active_space', {
headers: { ...COMMON_HEADERS, ...cookieHeader },
});

expect(response).toHaveStatusCode(404);
expect(response.body).toStrictEqual({
statusCode: 404,
error: 'Not Found',
message: 'Saved object [space/not-found-space] not found',
});
});
}
);
Loading
Loading