-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix serverless cases tests for MKI runs (#166080)
## Summary This PR fixes the serverless cases tests for MKI runs against Observability and Security projects. ### The issues we were seeing There were actually two issues where things worked locally but not in MKI: * A hard-coded `elastic_serverless` user * Deleting from system indices directly ### How this PR solves them * Replace the hard-coded `elastic_serverless` user with the one obtained via the test config * Replace deletion from system indices with saved object API `clean` calls ### Other changes I've noticed, that the tests are using local helper methods, but observability and security helper methods were 99.9% the same code. In order to make the code easier to re-use and also allow usage of other services from within the helper methods, we recommend putting helper methods into services. I've refactored the code to do this: * Create a new serverless API integrations service `svlCases` * Combine `x-pack/test_serverless/api_integration/test_suites/observability/cases/helpers/api.ts` and `x-pack/test_serverless/api_integration/test_suites/security/cases/helpers/api.ts` into the `svlCases.api` service * Combine `x-pack/test_serverless/api_integration/test_suites/observability/cases/helpers/omit.ts` and `x-pack/test_serverless/api_integration/test_suites/security/cases/helpers/omit.ts` into the `svlCases.omit` service * Clean up dependencies a bit ( e.g. no need to pass `supertest` around since `svlCases.api` can load the supertest service on its own now)
- Loading branch information
Showing
14 changed files
with
389 additions
and
688 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
233 changes: 233 additions & 0 deletions
233
x-pack/test_serverless/api_integration/services/svl_cases/api.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
/* | ||
* 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 type SuperTest from 'supertest'; | ||
import { CASES_URL } from '@kbn/cases-plugin/common'; | ||
import { Case, CaseSeverity, CaseStatuses } from '@kbn/cases-plugin/common/types/domain'; | ||
import type { CasePostRequest } from '@kbn/cases-plugin/common/types/api'; | ||
import { ConnectorTypes } from '@kbn/cases-plugin/common/types/domain'; | ||
import { CasesFindResponse } from '@kbn/cases-plugin/common/types/api'; | ||
import { kbnTestConfig, kibanaTestSuperuserServerless } from '@kbn/test'; | ||
import { FtrProviderContext } from '../../ftr_provider_context'; | ||
|
||
export function SvlCasesApiServiceProvider({ getService }: FtrProviderContext) { | ||
const kbnServer = getService('kibanaServer'); | ||
const supertest = getService('supertest'); | ||
|
||
interface User { | ||
username: string; | ||
password: string; | ||
description?: string; | ||
roles: string[]; | ||
} | ||
|
||
const superUser: User = { | ||
username: 'superuser', | ||
password: 'superuser', | ||
roles: ['superuser'], | ||
}; | ||
|
||
const defaultUser = { | ||
email: null, | ||
full_name: null, | ||
username: kbnTestConfig.getUrlParts(kibanaTestSuperuserServerless).username, | ||
}; | ||
|
||
/** | ||
* A null filled user will occur when the security plugin is disabled | ||
*/ | ||
const nullUser = { email: null, full_name: null, username: null }; | ||
|
||
const findCommon = { | ||
page: 1, | ||
per_page: 20, | ||
total: 0, | ||
count_open_cases: 0, | ||
count_closed_cases: 0, | ||
count_in_progress_cases: 0, | ||
}; | ||
|
||
const findCasesResp: CasesFindResponse = { | ||
...findCommon, | ||
cases: [], | ||
}; | ||
|
||
return { | ||
setupAuth({ | ||
apiCall, | ||
headers, | ||
auth, | ||
}: { | ||
apiCall: SuperTest.Test; | ||
headers: Record<string, unknown>; | ||
auth?: { user: User; space: string | null } | null; | ||
}): SuperTest.Test { | ||
if (!Object.hasOwn(headers, 'Cookie') && auth != null) { | ||
return apiCall.auth(auth.user.username, auth.user.password); | ||
} | ||
|
||
return apiCall; | ||
}, | ||
|
||
getSpaceUrlPrefix(spaceId: string | undefined | null) { | ||
return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; | ||
}, | ||
|
||
async deleteAllCaseItems() { | ||
await Promise.all([ | ||
this.deleteCasesByESQuery(), | ||
this.deleteCasesUserActions(), | ||
this.deleteComments(), | ||
this.deleteConfiguration(), | ||
this.deleteMappings(), | ||
]); | ||
}, | ||
|
||
async deleteCasesUserActions(): Promise<void> { | ||
await kbnServer.savedObjects.clean({ types: ['cases-user-actions'] }); | ||
}, | ||
|
||
async deleteCasesByESQuery(): Promise<void> { | ||
await kbnServer.savedObjects.clean({ types: ['cases'] }); | ||
}, | ||
|
||
async deleteComments(): Promise<void> { | ||
await kbnServer.savedObjects.clean({ types: ['cases-comments'] }); | ||
}, | ||
|
||
async deleteConfiguration(): Promise<void> { | ||
await kbnServer.savedObjects.clean({ types: ['cases-configure'] }); | ||
}, | ||
|
||
async deleteMappings(): Promise<void> { | ||
await kbnServer.savedObjects.clean({ types: ['cases-connector-mappings'] }); | ||
}, | ||
|
||
/** | ||
* Return a request for creating a case. | ||
*/ | ||
getPostCaseRequest(owner: string, req?: Partial<CasePostRequest>): CasePostRequest { | ||
return { | ||
...this.getPostCaseReq(owner), | ||
...req, | ||
}; | ||
}, | ||
|
||
postCaseResp(owner: string, id?: string | null, req?: CasePostRequest): Partial<Case> { | ||
const request = req ?? this.getPostCaseReq(owner); | ||
return { | ||
...request, | ||
...(id != null ? { id } : {}), | ||
comments: [], | ||
duration: null, | ||
severity: request.severity ?? CaseSeverity.LOW, | ||
totalAlerts: 0, | ||
totalComment: 0, | ||
closed_by: null, | ||
created_by: defaultUser, | ||
external_service: null, | ||
status: CaseStatuses.open, | ||
updated_by: null, | ||
category: null, | ||
}; | ||
}, | ||
|
||
async createCase( | ||
params: CasePostRequest, | ||
expectedHttpCode: number = 200, | ||
auth: { user: User; space: string | null } | null = { user: superUser, space: null }, | ||
headers: Record<string, unknown> = {} | ||
): Promise<Case> { | ||
const apiCall = supertest.post(`${CASES_URL}`); | ||
|
||
this.setupAuth({ apiCall, headers, auth }); | ||
|
||
const response = await apiCall | ||
.set('kbn-xsrf', 'foo') | ||
.set('x-elastic-internal-origin', 'foo') | ||
.set(headers) | ||
.send(params) | ||
.expect(expectedHttpCode); | ||
|
||
return response.body; | ||
}, | ||
|
||
async findCases({ | ||
query = {}, | ||
expectedHttpCode = 200, | ||
auth = { user: superUser, space: null }, | ||
}: { | ||
query?: Record<string, unknown>; | ||
expectedHttpCode?: number; | ||
auth?: { user: User; space: string | null }; | ||
}): Promise<CasesFindResponse> { | ||
const { body: res } = await supertest | ||
.get(`${this.getSpaceUrlPrefix(auth.space)}${CASES_URL}/_find`) | ||
.auth(auth.user.username, auth.user.password) | ||
.query({ sortOrder: 'asc', ...query }) | ||
.set('kbn-xsrf', 'foo') | ||
.set('x-elastic-internal-origin', 'foo') | ||
.send() | ||
.expect(expectedHttpCode); | ||
|
||
return res; | ||
}, | ||
|
||
async getCase({ | ||
caseId, | ||
includeComments = false, | ||
expectedHttpCode = 200, | ||
auth = { user: superUser, space: null }, | ||
}: { | ||
caseId: string; | ||
includeComments?: boolean; | ||
expectedHttpCode?: number; | ||
auth?: { user: User; space: string | null }; | ||
}): Promise<Case> { | ||
const { body: theCase } = await supertest | ||
.get( | ||
`${this.getSpaceUrlPrefix( | ||
auth?.space | ||
)}${CASES_URL}/${caseId}?includeComments=${includeComments}` | ||
) | ||
.set('kbn-xsrf', 'foo') | ||
.set('x-elastic-internal-origin', 'foo') | ||
.auth(auth.user.username, auth.user.password) | ||
.expect(expectedHttpCode); | ||
|
||
return theCase; | ||
}, | ||
|
||
getFindCasesResp() { | ||
return findCasesResp; | ||
}, | ||
|
||
getPostCaseReq(owner: string): CasePostRequest { | ||
return { | ||
description: 'This is a brand new case of a bad meanie defacing data', | ||
title: 'Super Bad Observability Issue', | ||
tags: ['defacement'], | ||
severity: CaseSeverity.LOW, | ||
connector: { | ||
id: 'none', | ||
name: 'none', | ||
type: ConnectorTypes.none, | ||
fields: null, | ||
}, | ||
settings: { | ||
syncAlerts: true, | ||
}, | ||
owner, | ||
assignees: [], | ||
}; | ||
}, | ||
|
||
getNullUser() { | ||
return nullUser; | ||
}, | ||
}; | ||
} |
21 changes: 21 additions & 0 deletions
21
x-pack/test_serverless/api_integration/services/svl_cases/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* 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 { FtrProviderContext } from '../../ftr_provider_context'; | ||
|
||
import { SvlCasesApiServiceProvider } from './api'; | ||
import { SvlCasesOmitServiceProvider } from './omit'; | ||
|
||
export function SvlCasesServiceProvider(context: FtrProviderContext) { | ||
const api = SvlCasesApiServiceProvider(context); | ||
const omit = SvlCasesOmitServiceProvider(context); | ||
|
||
return { | ||
api, | ||
omit, | ||
}; | ||
} |
53 changes: 53 additions & 0 deletions
53
x-pack/test_serverless/api_integration/services/svl_cases/omit.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { Case, Attachment } from '@kbn/cases-plugin/common/types/domain'; | ||
import { omit } from 'lodash'; | ||
import { FtrProviderContext } from '../../ftr_provider_context'; | ||
|
||
export function SvlCasesOmitServiceProvider({}: FtrProviderContext) { | ||
interface CommonSavedObjectAttributes { | ||
id?: string | null; | ||
created_at?: string | null; | ||
updated_at?: string | null; | ||
version?: string | null; | ||
[key: string]: unknown; | ||
} | ||
|
||
const savedObjectCommonAttributes = ['created_at', 'updated_at', 'version', 'id']; | ||
|
||
return { | ||
removeServerGeneratedPropertiesFromObject<T extends object, K extends keyof T>( | ||
object: T, | ||
keys: K[] | ||
): Omit<T, K> { | ||
return omit<T, K>(object, keys); | ||
}, | ||
|
||
removeServerGeneratedPropertiesFromSavedObject<T extends CommonSavedObjectAttributes>( | ||
attributes: T, | ||
keys: Array<keyof T> = [] | ||
): Omit<T, typeof savedObjectCommonAttributes[number] | typeof keys[number]> { | ||
return this.removeServerGeneratedPropertiesFromObject(attributes, [ | ||
...savedObjectCommonAttributes, | ||
...keys, | ||
]); | ||
}, | ||
|
||
removeServerGeneratedPropertiesFromCase(theCase: Case): Partial<Case> { | ||
return this.removeServerGeneratedPropertiesFromSavedObject<Case>(theCase, ['closed_at']); | ||
}, | ||
|
||
removeServerGeneratedPropertiesFromComments( | ||
comments: Attachment[] | undefined | ||
): Array<Partial<Attachment>> | undefined { | ||
return comments?.map((comment) => { | ||
return this.removeServerGeneratedPropertiesFromSavedObject<Attachment>(comment, []); | ||
}); | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.