From dfa22012ef5c4773e495354500705f02ba91139c Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 7 Apr 2021 09:54:43 +0300 Subject: [PATCH 01/25] Move test to upper level --- .../{cases => }/comments/delete_comment.ts | 8 +++---- .../{cases => }/comments/find_comments.ts | 10 ++++---- .../{cases => }/comments/get_all_comments.ts | 10 ++++---- .../{cases => }/comments/get_comment.ts | 10 ++++---- .../common/{cases => }/comments/migrations.ts | 4 ++-- .../{cases => }/comments/patch_comment.ts | 10 ++++---- .../{cases => }/comments/post_comment.ts | 14 +++++------ .../security_and_spaces/tests/common/index.ts | 24 +++++++++---------- .../{cases => }/sub_cases/delete_sub_cases.ts | 12 +++++----- .../{cases => }/sub_cases/find_sub_cases.ts | 14 +++++------ .../{cases => }/sub_cases/get_sub_case.ts | 10 ++++---- .../{cases => }/sub_cases/patch_sub_cases.ts | 14 +++++------ .../user_actions/get_all_user_actions.ts | 14 ++++------- .../{cases => }/user_actions/migrations.ts | 4 ++-- 14 files changed, 77 insertions(+), 81 deletions(-) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/delete_comment.ts (95%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/find_comments.ts (93%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/get_all_comments.ts (91%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/get_comment.ts (88%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/migrations.ts (86%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/patch_comment.ts (97%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/comments/post_comment.ts (96%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/sub_cases/delete_sub_cases.ts (89%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/sub_cases/find_sub_cases.ts (97%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/sub_cases/get_sub_case.ts (93%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/sub_cases/patch_sub_cases.ts (96%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/user_actions/get_all_user_actions.ts (96%) rename x-pack/test/case_api_integration/security_and_spaces/tests/common/{cases => }/user_actions/migrations.ts (90%) diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/delete_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts similarity index 95% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/delete_comment.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts index c0e905f9ad201..1b3681b204f5c 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/delete_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts @@ -6,10 +6,10 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { postCaseReq, postCommentUserReq } from '../../../../../common/lib/mock'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, @@ -18,7 +18,7 @@ import { deleteCases, deleteCasesUserActions, deleteComments, -} from '../../../../../common/lib/utils'; +} from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/find_comments.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts similarity index 93% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/find_comments.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts index c3919516bb969..b18ea6cad2bce 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/find_comments.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts @@ -6,11 +6,11 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { CommentsResponse, CommentType } from '../../../../../../../plugins/cases/common/api'; -import { postCaseReq, postCommentUserReq } from '../../../../../common/lib/mock'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { CommentsResponse, CommentType } from '../../../../../../plugins/cases/common/api'; +import { postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, @@ -19,7 +19,7 @@ import { deleteCases, deleteCasesUserActions, deleteComments, -} from '../../../../../common/lib/utils'; +} from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/get_all_comments.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts similarity index 91% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/get_all_comments.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts index c91337bcbfeaf..f02022e70e330 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/get_all_comments.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts @@ -6,17 +6,17 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { postCaseReq, postCommentUserReq } from '../../../../../common/lib/mock'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, deleteAllCaseItems, deleteCaseAction, -} from '../../../../../common/lib/utils'; -import { CommentType } from '../../../../../../../plugins/cases/common/api'; +} from '../../../../common/lib/utils'; +import { CommentType } from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/get_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts similarity index 88% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/get_comment.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts index 1373d56c311c5..97face4c8734c 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/get_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts @@ -6,17 +6,17 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { postCaseReq, postCommentUserReq } from '../../../../../common/lib/mock'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, deleteAllCaseItems, deleteCaseAction, -} from '../../../../../common/lib/utils'; -import { CommentResponse, CommentType } from '../../../../../../../plugins/cases/common/api'; +} from '../../../../common/lib/utils'; +import { CommentResponse, CommentType } from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/migrations.ts similarity index 86% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/migrations.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/migrations.ts index 8ceb81017ecdb..50a219c5e84b3 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/migrations.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/patch_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts similarity index 97% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/patch_comment.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts index 3c0bdd4a14cee..69c3cb78ecc9e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/patch_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts @@ -7,16 +7,16 @@ import { omit } from 'lodash/fp'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { CaseResponse, CommentType } from '../../../../../../../plugins/cases/common/api'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { CaseResponse, CommentType } from '../../../../../../plugins/cases/common/api'; import { defaultUser, postCaseReq, postCommentUserReq, postCommentAlertReq, -} from '../../../../../common/lib/mock'; +} from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, @@ -25,7 +25,7 @@ import { deleteCases, deleteCasesUserActions, deleteComments, -} from '../../../../../common/lib/utils'; +} from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts similarity index 96% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/post_comment.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index cb7dd74d0f714..c0fedbe69c788 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -7,11 +7,11 @@ import { omit } from 'lodash/fp'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../../../plugins/security_solution/common/constants'; -import { CommentsResponse, CommentType } from '../../../../../../../plugins/cases/common/api'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../../plugins/security_solution/common/constants'; +import { CommentsResponse, CommentType } from '../../../../../../plugins/cases/common/api'; import { defaultUser, postCaseReq, @@ -19,7 +19,7 @@ import { postCommentAlertReq, postCollectionReq, postCommentGenAlertReq, -} from '../../../../../common/lib/mock'; +} from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, @@ -28,7 +28,7 @@ import { deleteCases, deleteCasesUserActions, deleteComments, -} from '../../../../../common/lib/utils'; +} from '../../../../common/lib/utils'; import { createSignalsIndex, deleteSignalsIndex, @@ -39,7 +39,7 @@ import { getSignalsByIds, createRule, getQuerySignalIds, -} from '../../../../../../detection_engine_api_integration/utils'; +} from '../../../../../detection_engine_api_integration/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts index ba5a865b35778..c6c68efd7a752 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts @@ -10,12 +10,12 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ loadTestFile }: FtrProviderContext): void => { describe('Common', function () { - loadTestFile(require.resolve('./cases/comments/delete_comment')); - loadTestFile(require.resolve('./cases/comments/find_comments')); - loadTestFile(require.resolve('./cases/comments/get_comment')); - loadTestFile(require.resolve('./cases/comments/get_all_comments')); - loadTestFile(require.resolve('./cases/comments/patch_comment')); - loadTestFile(require.resolve('./cases/comments/post_comment')); + loadTestFile(require.resolve('./comments/delete_comment')); + loadTestFile(require.resolve('./comments/find_comments')); + loadTestFile(require.resolve('./comments/get_comment')); + loadTestFile(require.resolve('./comments/get_all_comments')); + loadTestFile(require.resolve('./comments/patch_comment')); + loadTestFile(require.resolve('./comments/post_comment')); loadTestFile(require.resolve('./cases/delete_cases')); loadTestFile(require.resolve('./cases/find_cases')); loadTestFile(require.resolve('./cases/get_case')); @@ -24,20 +24,20 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./cases/reporters/get_reporters')); loadTestFile(require.resolve('./cases/status/get_status')); loadTestFile(require.resolve('./cases/tags/get_tags')); - loadTestFile(require.resolve('./cases/user_actions/get_all_user_actions')); + loadTestFile(require.resolve('./user_actions/get_all_user_actions')); loadTestFile(require.resolve('./configure/get_configure')); loadTestFile(require.resolve('./configure/get_connectors')); loadTestFile(require.resolve('./configure/patch_configure')); loadTestFile(require.resolve('./configure/post_configure')); loadTestFile(require.resolve('./connectors/case')); - loadTestFile(require.resolve('./cases/sub_cases/patch_sub_cases')); - loadTestFile(require.resolve('./cases/sub_cases/delete_sub_cases')); - loadTestFile(require.resolve('./cases/sub_cases/get_sub_case')); - loadTestFile(require.resolve('./cases/sub_cases/find_sub_cases')); + loadTestFile(require.resolve('./sub_cases/patch_sub_cases')); + loadTestFile(require.resolve('./sub_cases/delete_sub_cases')); + loadTestFile(require.resolve('./sub_cases/get_sub_case')); + loadTestFile(require.resolve('./sub_cases/find_sub_cases')); // Migrations loadTestFile(require.resolve('./cases/migrations')); loadTestFile(require.resolve('./configure/migrations')); - loadTestFile(require.resolve('./cases/user_actions/migrations')); + loadTestFile(require.resolve('./user_actions/migrations')); }); }; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/delete_sub_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/delete_sub_cases.ts similarity index 89% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/delete_sub_cases.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/delete_sub_cases.ts index bd3d9ff86d540..951db263a6c78 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/delete_sub_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/delete_sub_cases.ts @@ -5,21 +5,21 @@ * 2.0. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL, SUB_CASES_PATCH_DEL_URL, -} from '../../../../../../../plugins/cases/common/constants'; -import { postCommentUserReq } from '../../../../../common/lib/mock'; +} from '../../../../../../plugins/cases/common/constants'; +import { postCommentUserReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, deleteAllCaseItems, deleteCaseAction, -} from '../../../../../common/lib/utils'; -import { getSubCaseDetailsUrl } from '../../../../../../../plugins/cases/common/api/helpers'; -import { CaseResponse } from '../../../../../../../plugins/cases/common/api'; +} from '../../../../common/lib/utils'; +import { getSubCaseDetailsUrl } from '../../../../../../plugins/cases/common/api/helpers'; +import { CaseResponse } from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/find_sub_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/find_sub_cases.ts similarity index 97% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/find_sub_cases.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/find_sub_cases.ts index 466eca95b0d72..14c0460c7583b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/find_sub_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/find_sub_cases.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import type { ApiResponse, estypes } from '@elastic/elasticsearch'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { findSubCasesResp, postCollectionReq } from '../../../../../common/lib/mock'; +import { findSubCasesResp, postCollectionReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, @@ -17,19 +17,19 @@ import { deleteAllCaseItems, deleteCaseAction, setStatus, -} from '../../../../../common/lib/utils'; -import { getSubCasesUrl } from '../../../../../../../plugins/cases/common/api/helpers'; +} from '../../../../common/lib/utils'; +import { getSubCasesUrl } from '../../../../../../plugins/cases/common/api/helpers'; import { CaseResponse, CaseStatuses, CommentType, SubCasesFindResponse, -} from '../../../../../../../plugins/cases/common/api'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; +} from '../../../../../../plugins/cases/common/api'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { ContextTypeGeneratedAlertType, createAlertsString, -} from '../../../../../../../plugins/cases/server/connectors'; +} from '../../../../../../plugins/cases/server/connectors'; interface SubCaseAttributes { 'cases-sub-case': { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/get_sub_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts similarity index 93% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/get_sub_case.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts index 6a0d7f4dd042e..28e33df529cc6 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/get_sub_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { commentsResp, @@ -14,23 +14,23 @@ import { removeServerGeneratedPropertiesFromComments, removeServerGeneratedPropertiesFromSubCase, subCaseResp, -} from '../../../../../common/lib/mock'; +} from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, defaultCreateSubComment, deleteAllCaseItems, deleteCaseAction, -} from '../../../../../common/lib/utils'; +} from '../../../../common/lib/utils'; import { getCaseCommentsUrl, getSubCaseDetailsUrl, -} from '../../../../../../../plugins/cases/common/api/helpers'; +} from '../../../../../../plugins/cases/common/api/helpers'; import { AssociationType, CaseResponse, SubCaseResponse, -} from '../../../../../../../plugins/cases/common/api'; +} from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/patch_sub_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/patch_sub_cases.ts similarity index 96% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/patch_sub_cases.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/patch_sub_cases.ts index a4bd3ce187d0e..43526bca644db 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/sub_cases/patch_sub_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/patch_sub_cases.ts @@ -5,12 +5,12 @@ * 2.0. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL, SUB_CASES_PATCH_DEL_URL, -} from '../../../../../../../plugins/cases/common/constants'; +} from '../../../../../../plugins/cases/common/constants'; import { createCaseAction, createSubCase, @@ -18,15 +18,15 @@ import { deleteCaseAction, getSignalsWithES, setStatus, -} from '../../../../../common/lib/utils'; -import { getSubCaseDetailsUrl } from '../../../../../../../plugins/cases/common/api/helpers'; +} from '../../../../common/lib/utils'; +import { getSubCaseDetailsUrl } from '../../../../../../plugins/cases/common/api/helpers'; import { CaseStatuses, CommentType, SubCaseResponse, -} from '../../../../../../../plugins/cases/common/api'; -import { createAlertsString } from '../../../../../../../plugins/cases/server/connectors'; -import { postCaseReq, postCollectionReq } from '../../../../../common/lib/mock'; +} from '../../../../../../plugins/cases/common/api'; +import { createAlertsString } from '../../../../../../plugins/cases/server/connectors'; +import { postCaseReq, postCollectionReq } from '../../../../common/lib/mock'; const defaultSignalsIndex = '.siem-signals-default-000001'; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts similarity index 96% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/user_actions/get_all_user_actions.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 8f047602acc38..664ba29f4aef3 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -6,21 +6,17 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; -import { CommentType } from '../../../../../../../plugins/cases/common/api'; -import { - userActionPostResp, - postCaseReq, - postCommentUserReq, -} from '../../../../../common/lib/mock'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { CommentType } from '../../../../../../plugins/cases/common/api'; +import { userActionPostResp, postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; import { deleteCases, deleteCasesUserActions, deleteComments, deleteConfiguration, -} from '../../../../../common/lib/utils'; +} from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/user_actions/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts similarity index 90% rename from x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/user_actions/migrations.ts rename to x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts index 8bba29a56cd9d..e198260e88a9c 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts @@ -6,8 +6,8 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../../plugins/cases/common/constants'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { From d9641c0ed71fdad1e6c715e0e6e45a15b52d5ae5 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 7 Apr 2021 10:28:55 +0300 Subject: [PATCH 02/25] Improve addComment tests --- .../plugins/cases/common/api/cases/comment.ts | 1 + .../case_api_integration/common/lib/utils.ts | 42 ++++ .../tests/common/comments/post_comment.ts | 238 +++++++----------- 3 files changed, 135 insertions(+), 146 deletions(-) diff --git a/x-pack/plugins/cases/common/api/cases/comment.ts b/x-pack/plugins/cases/common/api/cases/comment.ts index 41ad0e87f14d2..4eb2ad1eadd6c 100644 --- a/x-pack/plugins/cases/common/api/cases/comment.ts +++ b/x-pack/plugins/cases/common/api/cases/comment.ts @@ -113,6 +113,7 @@ export const CommentsResponseRt = rt.type({ export const AllCommentsResponseRt = rt.array(CommentResponseRt); export type AttributesTypeAlerts = rt.TypeOf; +export type AttributesTypeUser = rt.TypeOf; export type CommentAttributes = rt.TypeOf; export type CommentRequest = rt.TypeOf; export type CommentResponse = rt.TypeOf; diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 82189c9d7abe3..61e344a5cf880 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -24,6 +24,7 @@ import { SubCasesResponse, CasesResponse, CasesFindResponse, + CommentRequest, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -348,6 +349,7 @@ export const deleteAllCaseItems = async (es: KibanaClient) => { deleteCasesUserActions(es), deleteComments(es), deleteConfiguration(es), + deleteMappings(es), ]); }; @@ -410,6 +412,17 @@ export const deleteConfiguration = async (es: KibanaClient): Promise => { }); }; +export const deleteMappings = async (es: KibanaClient): Promise => { + await es.deleteByQuery({ + index: '.kibana', + // @ts-expect-error @elastic/elasticsearch DeleteByQueryRequest doesn't accept q parameter + q: 'type:cases-connector-mappings', + wait_for_completion: true, + refresh: true, + body: {}, + }); +}; + export const getSpaceUrlPrefix = (spaceId: string) => { return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; }; @@ -468,3 +481,32 @@ export const ensureSavedObjectIsAuthorized = ( expect(cases.length).to.eql(numberOfExpectedCases); cases.forEach((theCase) => expect(owners.includes(theCase.owner)).to.be(true)); }; + +export const createCase = async ( + supertest: st.SuperTest, + params: CasePostRequest, + expectedHttpCode: number = 200 +): Promise => { + const { body: theCase } = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(params) + .expect(expectedHttpCode); + + return theCase; +}; + +export const createComment = async ( + supertest: st.SuperTest, + caseId: string, + params: CommentRequest, + expectedHttpCode: number = 200 +): Promise => { + const { body: theCase } = await supertest + .post(`${CASES_URL}/${caseId}/comments`) + .set('kbn-xsrf', 'true') + .send(params) + .expect(expectedHttpCode); + + return theCase; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index c0fedbe69c788..68d6c8b2ad039 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -11,7 +11,12 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../../plugins/security_solution/common/constants'; -import { CommentsResponse, CommentType } from '../../../../../../plugins/cases/common/api'; +import { + CommentsResponse, + CommentType, + AttributesTypeUser, + AttributesTypeAlerts, +} from '../../../../../../plugins/cases/common/api'; import { defaultUser, postCaseReq, @@ -28,6 +33,8 @@ import { deleteCases, deleteCasesUserActions, deleteComments, + createCase, + createComment, } from '../../../../common/lib/utils'; import { createSignalsIndex, @@ -55,94 +62,71 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should post a comment', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const comment = patchedCase.comments![0] as AttributesTypeUser; - expect(patchedCase.comments[0].type).to.eql(postCommentUserReq.type); - expect(patchedCase.comments[0].comment).to.eql(postCommentUserReq.comment); + expect(comment.type).to.eql(postCommentUserReq.type); + expect(comment.comment).to.eql(postCommentUserReq.comment); expect(patchedCase.updated_by).to.eql(defaultUser); }); it('should post an alert', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentAlertReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); + const comment = patchedCase.comments![0] as AttributesTypeAlerts; - expect(patchedCase.comments[0].type).to.eql(postCommentAlertReq.type); - expect(patchedCase.comments[0].alertId).to.eql(postCommentAlertReq.alertId); - expect(patchedCase.comments[0].index).to.eql(postCommentAlertReq.index); + expect(comment.type).to.eql(postCommentAlertReq.type); + expect(comment.alertId).to.eql(postCommentAlertReq.alertId); + expect(comment.index).to.eql(postCommentAlertReq.index); expect(patchedCase.updated_by).to.eql(defaultUser); }); it('unhappy path - 400s when type is missing', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ + const postedCase = await createCase(supertest, postCaseReq); + await createComment( + supertest, + postedCase.id, + { + // @ts-expect-error bad: 'comment', - }) - .expect(400); + }, + 400 + ); }); it('unhappy path - 400s when missing attributes for type user', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ type: CommentType.user }) - .expect(400); + const postedCase = await createCase(supertest, postCaseReq); + await createComment( + supertest, + postedCase.id, + // @ts-expect-error + { + type: CommentType.user, + }, + 400 + ); }); it('unhappy path - 400s when adding excess attributes for type user', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); for (const attribute of ['alertId', 'index']) { - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ type: CommentType.user, [attribute]: attribute, comment: 'a comment' }) - .expect(400); + await createComment( + supertest, + postedCase.id, + { + type: CommentType.user, + [attribute]: attribute, + comment: 'a comment', + }, + 400 + ); } }); it('unhappy path - 400s when missing attributes for type alert', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); const allRequestAttributes = { type: CommentType.alert, @@ -156,26 +140,19 @@ export default ({ getService }: FtrProviderContext): void => { for (const attribute of ['alertId', 'index']) { const requestAttributes = omit(attribute, allRequestAttributes); - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(requestAttributes) - .expect(400); + // @ts-expect-error + await createComment(supertest, postedCase.id, requestAttributes, 400); } }); it('unhappy path - 400s when adding excess attributes for type alert', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); for (const attribute of ['comment']) { - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ + await createComment( + supertest, + postedCase.id, + { type: CommentType.alert, [attribute]: attribute, alertId: 'test-id', @@ -184,27 +161,26 @@ export default ({ getService }: FtrProviderContext): void => { id: 'id', name: 'name', }, - }) - .expect(400); + }, + 400 + ); } }); it('unhappy path - 400s when case is missing', async () => { - await supertest - .post(`${CASES_URL}/not-exists/comments`) - .set('kbn-xsrf', 'true') - .send({ + await createComment( + supertest, + 'not-exists', + { + // @ts-expect-error bad: 'comment', - }) - .expect(400); + }, + 400 + ); }); it('unhappy path - 400s when adding an alert to a closed case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); await supertest .patch(CASES_URL) @@ -220,34 +196,17 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentAlertReq) - .expect(400); + await createComment(supertest, postedCase.id, postCommentAlertReq, 400); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests it.skip('400s when adding an alert to a collection case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCollectionReq) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentAlertReq) - .expect(400); + const postedCase = await createCase(supertest, postCaseReq); + await createComment(supertest, postedCase.id, postCommentAlertReq, 400); }); it('400s when adding a generated alert to an individual case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); await supertest .post(`${CASES_URL}/${postedCase.id}/comments`) @@ -270,12 +229,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should change the status of the alert if sync alert is on', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); await supertest .patch(CASES_URL) @@ -299,19 +253,15 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - rule: { - id: 'id', - name: 'name', - }, - type: CommentType.alert, - }) - .expect(200); + await createComment(supertest, 'not-exists', { + alertId: alert._id, + index: alert._index, + rule: { + id: 'id', + name: 'name', + }, + type: CommentType.alert, + }); const { body: updatedAlert } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) @@ -353,19 +303,15 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - rule: { - id: 'id', - name: 'name', - }, - type: CommentType.alert, - }) - .expect(200); + await createComment(supertest, 'not-exists', { + alertId: alert._id, + index: alert._index, + rule: { + id: 'id', + name: 'name', + }, + type: CommentType.alert, + }); const { body: updatedAlert } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) From 9cccf90d5634ae871622eac1d9528e1c485cd958 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 7 Apr 2021 14:19:54 +0300 Subject: [PATCH 03/25] Convert add comment test --- .../cases/common/api/cases/user_actions.ts | 1 + .../server/client/attachments/add.test.ts | 593 ------------------ .../case_api_integration/common/lib/utils.ts | 58 +- .../tests/common/comments/post_comment.ts | 118 +++- .../tests/common/configure/get_configure.ts | 4 +- .../tests/common/configure/patch_configure.ts | 4 +- .../tests/common/configure/post_configure.ts | 6 +- 7 files changed, 161 insertions(+), 623 deletions(-) delete mode 100644 x-pack/plugins/cases/server/client/attachments/add.test.ts diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index 7188ee44efa93..55dfac391f3be 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -58,6 +58,7 @@ export const CaseUserActionsResponseRt = rt.array(CaseUserActionResponseRT); export type CaseUserActionAttributes = rt.TypeOf; export type CaseUserActionsResponse = rt.TypeOf; +export type CaseUserActionResponse = rt.TypeOf; export type UserAction = rt.TypeOf; export type UserActionField = rt.TypeOf; diff --git a/x-pack/plugins/cases/server/client/attachments/add.test.ts b/x-pack/plugins/cases/server/client/attachments/add.test.ts deleted file mode 100644 index 23b7bc37dc814..0000000000000 --- a/x-pack/plugins/cases/server/client/attachments/add.test.ts +++ /dev/null @@ -1,593 +0,0 @@ -/* - * 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 { omit } from 'lodash/fp'; -import { CommentType } from '../../../common/api'; -import { isCaseError } from '../../common/error'; -import { - createMockSavedObjectsRepository, - mockCaseComments, - mockCases, -} from '../../routes/api/__fixtures__'; -import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; - -type AlertComment = CommentType.alert | CommentType.generatedAlert; - -describe('addComment', () => { - beforeEach(async () => { - jest.restoreAllMocks(); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2020-10-23T21:54:48.952Z'), - })); - }); - - describe('happy path', () => { - test('it adds a comment correctly', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - expect(res.id).toEqual('mock-id-1'); - expect(res.totalComment).toEqual(res.comments!.length); - expect(res.comments![res.comments!.length - 1]).toMatchInlineSnapshot(` - Object { - "associationType": "case", - "comment": "Wow, good luck catching that bad meanie!", - "created_at": "2020-10-23T21:54:48.952Z", - "created_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "id": "mock-comment", - "pushed_at": null, - "pushed_by": null, - "type": "user", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); - - test('it adds a comment of type alert correctly', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - type: CommentType.alert, - alertId: 'test-id', - index: 'test-index', - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }); - - expect(res.id).toEqual('mock-id-1'); - expect(res.totalComment).toEqual(res.comments!.length); - expect(res.comments![res.comments!.length - 1]).toMatchInlineSnapshot(` - Object { - "alertId": "test-id", - "associationType": "case", - "created_at": "2020-10-23T21:54:48.952Z", - "created_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "id": "mock-comment", - "index": "test-index", - "pushed_at": null, - "pushed_by": null, - "rule": Object { - "id": "test-rule1", - "name": "test-rule", - }, - "type": "alert", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); - - test('it updates the case correctly after adding a comment', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - expect(res.updated_at).toEqual('2020-10-23T21:54:48.952Z'); - expect(res.updated_by).toEqual({ - email: 'd00d@awesome.com', - full_name: 'Awesome D00d', - username: 'awesome', - }); - }); - - test('it creates a user action', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - expect( - casesClient.services.userActionService.postUserActions.mock.calls[0][0].actions - ).toEqual([ - { - attributes: { - action: 'create', - action_at: '2020-10-23T21:54:48.952Z', - action_by: { - email: 'd00d@awesome.com', - full_name: 'Awesome D00d', - username: 'awesome', - }, - action_field: ['comment'], - new_value: '{"comment":"Wow, good luck catching that bad meanie!","type":"user"}', - old_value: null, - }, - references: [ - { - id: 'mock-id-1', - name: 'associated-cases', - type: 'cases', - }, - { - id: 'mock-comment', - name: 'associated-cases-comments', - type: 'cases-comments', - }, - ], - }, - ]); - }); - - test('it allow user to create comments without authentications', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ - savedObjectsClient, - badAuth: true, - }); - const res = await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - expect(res.id).toEqual('mock-id-1'); - expect(res.comments![res.comments!.length - 1]).toMatchInlineSnapshot(` - Object { - "associationType": "case", - "comment": "Wow, good luck catching that bad meanie!", - "created_at": "2020-10-23T21:54:48.952Z", - "created_by": Object { - "email": null, - "full_name": null, - "username": null, - }, - "id": "mock-comment", - "pushed_at": null, - "pushed_by": null, - "type": "user", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); - - test('it update the status of the alert if the case is synced with alerts', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ - savedObjectsClient, - badAuth: true, - }); - - casesClient.client.updateAlertsStatus = jest.fn(); - - await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - type: CommentType.alert, - alertId: 'test-alert', - index: 'test-index', - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }); - - expect(casesClient.client.updateAlertsStatus).toHaveBeenCalledWith({ - alerts: [{ id: 'test-alert', index: 'test-index', status: 'open' }], - }); - }); - - test('it should NOT update the status of the alert if the case is NOT synced with alerts', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: [ - { - ...mockCases[0], - attributes: { ...mockCases[0].attributes, settings: { syncAlerts: false } }, - }, - ], - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ - savedObjectsClient, - badAuth: true, - }); - - casesClient.client.updateAlertsStatus = jest.fn(); - - await casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - type: CommentType.alert, - alertId: 'test-alert', - index: 'test-index', - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }); - - expect(casesClient.client.updateAlertsStatus).not.toHaveBeenCalled(); - }); - }); - - describe('unhappy path', () => { - test('it throws when missing type', async () => { - expect.assertions(3); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client - .addComment({ - caseId: 'mock-id-1', - // @ts-expect-error - comment: {}, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }); - }); - - test('it throws when missing attributes: type user', async () => { - expect.assertions(3); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const allRequestAttributes = { - type: CommentType.user, - comment: 'a comment', - }; - - ['comment'].forEach((attribute) => { - const requestAttributes = omit(attribute, allRequestAttributes); - return casesClient.client - .addComment({ - caseId: 'mock-id-1', - // @ts-expect-error - comment: { - ...requestAttributes, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }); - }); - }); - - test('it throws when excess attributes are provided: type user', async () => { - expect.assertions(6); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - - ['alertId', 'index'].forEach((attribute) => { - return casesClient.client - .addComment({ - caseId: 'mock-id-1', - comment: { - [attribute]: attribute, - comment: 'a comment', - type: CommentType.user, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }); - }); - }); - - test('it throws when missing attributes: type alert', async () => { - expect.assertions(6); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const allRequestAttributes = { - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - }; - - ['alertId', 'index'].forEach((attribute) => { - const requestAttributes = omit(attribute, allRequestAttributes); - return casesClient.client - .addComment({ - caseId: 'mock-id-1', - // @ts-expect-error - comment: { - ...requestAttributes, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }); - }); - }); - - test('it throws when excess attributes are provided: type alert', async () => { - expect.assertions(3); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - - ['comment'].forEach((attribute) => { - return casesClient.client - .addComment({ - caseId: 'mock-id-1', - comment: { - [attribute]: attribute, - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }); - }); - }); - - test('it throws when the case does not exists', async () => { - expect.assertions(4); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client - .addComment({ - caseId: 'not-exists', - comment: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(404); - }); - }); - - test('it throws when postNewCase throws', async () => { - expect.assertions(4); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client - .addComment({ - caseId: 'mock-id-1', - comment: { - comment: 'Throw an error', - type: CommentType.user, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(400); - }); - }); - - test('it throws when the case is closed and the comment is of type alert', async () => { - expect.assertions(4); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client - .addComment({ - caseId: 'mock-id-4', - comment: { - type: CommentType.alert, - alertId: 'test-alert', - index: 'test-index', - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(400); - }); - }); - - describe('alert format', () => { - it.each([ - ['1', ['index1', 'index2'], CommentType.alert], - [['1', '2'], 'index', CommentType.alert], - ['1', ['index1', 'index2'], CommentType.generatedAlert], - [['1', '2'], 'index', CommentType.generatedAlert], - ])( - 'throws an error with an alert comment with contents id: %p indices: %p type: %s', - async (alertId, index, type) => { - expect.assertions(1); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ - savedObjectsClient, - }); - await expect( - casesClient.client.addComment({ - caseId: 'mock-id-4', - comment: { - // casting because type must be either alert or generatedAlert but type is CommentType - type: type as AlertComment, - alertId, - index, - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }) - ).rejects.toThrow(); - } - ); - - it.each([ - ['1', ['index1'], CommentType.alert], - [['1', '2'], ['index', 'other-index'], CommentType.alert], - ])( - 'does not throw an error with an alert comment with contents id: %p indices: %p type: %s', - async (alertId, index, type) => { - expect.assertions(1); - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ - savedObjectsClient, - }); - await expect( - casesClient.client.addComment({ - caseId: 'mock-id-1', - comment: { - // casting because type must be either alert or generatedAlert but type is CommentType - type: type as AlertComment, - alertId, - index, - rule: { - id: 'test-rule1', - name: 'test-rule', - }, - }, - }) - ).resolves.not.toBeUndefined(); - } - ); - }); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 61e344a5cf880..331dab9c6824c 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -4,8 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import expect from '@kbn/expect'; +import { omit } from 'lodash'; +import expect from '@kbn/expect'; import type { ApiResponse, estypes } from '@elastic/elasticsearch'; import type { KibanaClient } from '@elastic/elasticsearch/api/kibana'; @@ -25,6 +26,7 @@ import { CasesResponse, CasesFindResponse, CommentRequest, + CaseUserActionResponse, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -334,12 +336,40 @@ export const getResilientConnector = () => ({ }, }); -export const removeServerGeneratedPropertiesFromConfigure = ( - config: Partial -): Partial => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { created_at, updated_at, version, ...rest } = config; - return rest; +interface SavedObjectAttributes { + id?: string | null; + created_at?: string | null; + updated_at?: string | null; + version?: string | null; + [key: string]: unknown; +} + +export const removeServerGeneratedPropertiesFromObject = ( + object: T, + keys: string | readonly string[] +): Omit => { + // @ts-ignore + return omit(object, keys); +}; + +export const removeServerGeneratedPropertiesFromSavedObject = ( + attributes: T +): Omit => { + const keysToRemove = ['created_at', 'updated_at', 'version', 'id'] as const; + return removeServerGeneratedPropertiesFromObject( + attributes, + keysToRemove + ); +}; + +export const removeServerGeneratedPropertiesFromUserAction = ( + attributes: CaseUserActionResponse +): Omit => { + const keysToRemove = ['action_id', 'action_at'] as const; + return removeServerGeneratedPropertiesFromObject< + CaseUserActionResponse, + typeof keysToRemove[number] + >(attributes, keysToRemove); }; export const deleteAllCaseItems = async (es: KibanaClient) => { @@ -510,3 +540,17 @@ export const createComment = async ( return theCase; }; + +export const getAllUserAction = async ( + supertest: st.SuperTest, + caseId: string, + expectedHttpCode: number = 200 +) => { + const { body: userActions } = await supertest + .get(`${CASES_URL}/${caseId}/user_actions`) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return userActions; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index 68d6c8b2ad039..19ec3b3dc7f89 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -12,6 +12,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../../plugins/security_solution/common/constants'; import { + CaseResponse, CommentsResponse, CommentType, AttributesTypeUser, @@ -35,6 +36,9 @@ import { deleteComments, createCase, createComment, + getAllUserAction, + removeServerGeneratedPropertiesFromUserAction, + removeServerGeneratedPropertiesFromSavedObject, } from '../../../../common/lib/utils'; import { createSignalsIndex, @@ -64,24 +68,67 @@ export default ({ getService }: FtrProviderContext): void => { it('should post a comment', async () => { const postedCase = await createCase(supertest, postCaseReq); const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - const comment = patchedCase.comments![0] as AttributesTypeUser; + const comment = removeServerGeneratedPropertiesFromSavedObject( + patchedCase.comments![0] as AttributesTypeUser + ); + + expect(comment).to.eql({ + type: postCommentUserReq.type, + comment: postCommentUserReq.comment, + associationType: 'case', + created_by: defaultUser, + pushed_at: null, + pushed_by: null, + updated_by: null, + }); - expect(comment.type).to.eql(postCommentUserReq.type); - expect(comment.comment).to.eql(postCommentUserReq.comment); + // updates the case correctly after adding a comment + expect(patchedCase.totalComment).to.eql(patchedCase.comments!.length); expect(patchedCase.updated_by).to.eql(defaultUser); }); it('should post an alert', async () => { const postedCase = await createCase(supertest, postCaseReq); const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); - const comment = patchedCase.comments![0] as AttributesTypeAlerts; + const comment = removeServerGeneratedPropertiesFromSavedObject( + patchedCase.comments![0] as AttributesTypeAlerts + ); - expect(comment.type).to.eql(postCommentAlertReq.type); - expect(comment.alertId).to.eql(postCommentAlertReq.alertId); - expect(comment.index).to.eql(postCommentAlertReq.index); + expect(comment).to.eql({ + type: postCommentAlertReq.type, + alertId: postCommentAlertReq.alertId, + index: postCommentAlertReq.index, + rule: postCommentAlertReq.rule, + associationType: 'case', + created_by: defaultUser, + pushed_at: null, + pushed_by: null, + updated_by: null, + }); + + // updates the case correctly after adding a comment + expect(patchedCase.totalComment).to.eql(patchedCase.comments!.length); expect(patchedCase.updated_by).to.eql(defaultUser); }); + it('creates a user action', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const userActions = await getAllUserAction(supertest, postedCase.id); + const commentUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); + + expect(commentUserAction).to.eql({ + action_field: ['comment'], + action: 'create', + action_by: defaultUser, + new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}"}`, + old_value: null, + case_id: `${postedCase.id}`, + comment_id: `${patchedCase.comments![0].id}`, + sub_case_id: '', + }); + }); + it('unhappy path - 400s when type is missing', async () => { const postedCase = await createCase(supertest, postCaseReq); await createComment( @@ -201,7 +248,7 @@ export default ({ getService }: FtrProviderContext): void => { // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests it.skip('400s when adding an alert to a collection case', async () => { - const postedCase = await createCase(supertest, postCaseReq); + const postedCase = await createCase(supertest, postCollectionReq); await createComment(supertest, postedCase.id, postCommentAlertReq, 400); }); @@ -253,7 +300,7 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - await createComment(supertest, 'not-exists', { + await createComment(supertest, postedCase.id, { alertId: alert._id, index: alert._index, rule: { @@ -274,12 +321,10 @@ export default ({ getService }: FtrProviderContext): void => { it('should NOT change the status of the alert if sync alert is off', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, settings: { syncAlerts: false } }) - .expect(200); + const postedCase = await createCase(supertest, { + ...postCaseReq, + settings: { syncAlerts: false }, + }); await supertest .patch(CASES_URL) @@ -303,7 +348,7 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - await createComment(supertest, 'not-exists', { + await createComment(supertest, postedCase.id, { alertId: alert._id, index: alert._index, rule: { @@ -332,6 +377,47 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.message).to.contain('subCaseId'); }); + describe('alert format', () => { + type AlertComment = CommentType.alert | CommentType.generatedAlert; + + for (const [alertId, index, type] of [ + ['1', ['index1', 'index2'], CommentType.alert], + [['1', '2'], 'index', CommentType.alert], + ['1', ['index1', 'index2'], CommentType.generatedAlert], + [['1', '2'], 'index', CommentType.generatedAlert], + ]) { + it(`throws an error with an alert comment with contents id: ${alertId} indices: ${index} type: ${type}`, async () => { + const postedCase = await createCase(supertest, postCaseReq); + await createComment( + supertest, + postedCase.id, + { ...postCommentAlertReq, alertId, index, type: type as AlertComment }, + 400 + ); + }); + } + + for (const [alertId, index, type] of [ + ['1', ['index1'], CommentType.alert], + [['1', '2'], ['index', 'other-index'], CommentType.alert], + ]) { + it(`does not throw an error with an alert comment with contents id: ${alertId} indices: ${index} type: ${type}`, async () => { + const postedCase = await createCase(supertest, postCaseReq); + await createComment( + supertest, + postedCase.id, + { + ...postCommentAlertReq, + alertId, + index, + type: type as AlertComment, + }, + 200 + ); + }); + } + }); + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests describe.skip('sub case comments', () => { let actionID: string; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts index 391cb3a4e5a2a..5047e5c6b56b1 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASE_CONFIGURE_URL } from '../../../../../../plugins/cases/common/constants'; import { getConfiguration, - removeServerGeneratedPropertiesFromConfigure, + removeServerGeneratedPropertiesFromSavedObject, getConfigurationOutput, deleteConfiguration, } from '../../../../common/lib/utils'; @@ -49,7 +49,7 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(200); - const data = removeServerGeneratedPropertiesFromConfigure(body); + const data = removeServerGeneratedPropertiesFromSavedObject(body); expect(data).to.eql(getConfigurationOutput()); }); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts index 1e2ef74479ffd..3feee479455c8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASE_CONFIGURE_URL } from '../../../../../../plugins/cases/common/constants'; import { getConfiguration, - removeServerGeneratedPropertiesFromConfigure, + removeServerGeneratedPropertiesFromSavedObject, getConfigurationOutput, deleteConfiguration, } from '../../../../common/lib/utils'; @@ -39,7 +39,7 @@ export default ({ getService }: FtrProviderContext): void => { .send({ closure_type: 'close-by-pushing', version: res.body.version }) .expect(200); - const data = removeServerGeneratedPropertiesFromConfigure(body); + const data = removeServerGeneratedPropertiesFromSavedObject(body); expect(data).to.eql({ ...getConfigurationOutput(true), closure_type: 'close-by-pushing' }); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts index 9d0fad202a517..3672cd3a755f7 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASE_CONFIGURE_URL } from '../../../../../../plugins/cases/common/constants'; import { getConfiguration, - removeServerGeneratedPropertiesFromConfigure, + removeServerGeneratedPropertiesFromSavedObject, getConfigurationOutput, deleteConfiguration, } from '../../../../common/lib/utils'; @@ -33,7 +33,7 @@ export default ({ getService }: FtrProviderContext): void => { .send(getConfiguration()) .expect(200); - const data = removeServerGeneratedPropertiesFromConfigure(body); + const data = removeServerGeneratedPropertiesFromSavedObject(body); expect(data).to.eql(getConfigurationOutput()); }); @@ -56,7 +56,7 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(200); - const data = removeServerGeneratedPropertiesFromConfigure(body); + const data = removeServerGeneratedPropertiesFromSavedObject(body); expect(data).to.eql(getConfigurationOutput()); }); From 4675df16b051d44b42f444f6585a5f65519b49c0 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 7 Apr 2021 16:06:31 +0300 Subject: [PATCH 04/25] Improve removal of properties --- .../case_api_integration/common/lib/mock.ts | 54 ----------------- .../case_api_integration/common/lib/utils.ts | 58 +++++++++++++++---- .../tests/common/connectors/case.ts | 5 +- .../tests/common/sub_cases/get_sub_case.ts | 10 +--- 4 files changed, 53 insertions(+), 74 deletions(-) diff --git a/x-pack/test/case_api_integration/common/lib/mock.ts b/x-pack/test/case_api_integration/common/lib/mock.ts index f1f088e5c5042..adcb5b6f4f346 100644 --- a/x-pack/test/case_api_integration/common/lib/mock.ts +++ b/x-pack/test/case_api_integration/common/lib/mock.ts @@ -165,60 +165,6 @@ export const subCaseResp = ({ updated_by: defaultUser, }); -interface FormattedCollectionResponse { - caseInfo: Partial; - subCases?: Array>; - comments?: Array>; -} - -export const formatCollectionResponse = (caseInfo: CaseResponse): FormattedCollectionResponse => { - const subCase = removeServerGeneratedPropertiesFromSubCase(caseInfo.subCases?.[0]); - return { - caseInfo: removeServerGeneratedPropertiesFromCaseCollection(caseInfo), - subCases: subCase ? [subCase] : undefined, - comments: removeServerGeneratedPropertiesFromComments( - caseInfo.subCases?.[0].comments ?? caseInfo.comments - ), - }; -}; - -export const removeServerGeneratedPropertiesFromSubCase = ( - subCase: Partial | undefined -): Partial | undefined => { - if (!subCase) { - return; - } - // eslint-disable-next-line @typescript-eslint/naming-convention - const { closed_at, created_at, updated_at, version, comments, ...rest } = subCase; - return rest; -}; - -export const removeServerGeneratedPropertiesFromCaseCollection = ( - config: Partial -): Partial => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { closed_at, created_at, updated_at, version, subCases, ...rest } = config; - return rest; -}; - -export const removeServerGeneratedPropertiesFromCase = ( - config: Partial -): Partial => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { closed_at, created_at, updated_at, version, ...rest } = config; - return rest; -}; - -export const removeServerGeneratedPropertiesFromComments = ( - comments: CommentResponse[] | undefined -): Array> | undefined => { - return comments?.map((comment) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { created_at, updated_at, version, ...rest } = comment; - return rest; - }); -}; - const findCommon = { page: 1, per_page: 20, diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 331dab9c6824c..973145aff518d 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -27,6 +27,8 @@ import { CasesFindResponse, CommentRequest, CaseUserActionResponse, + SubCaseResponse, + CommentResponse, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -336,7 +338,7 @@ export const getResilientConnector = () => ({ }, }); -interface SavedObjectAttributes { +interface CommonSavedObjectAttributes { id?: string | null; created_at?: string | null; updated_at?: string | null; @@ -344,22 +346,32 @@ interface SavedObjectAttributes { [key: string]: unknown; } +const savedObjectCommonAttributes = ['created_at', 'updated_at', 'version', 'id'] as const; + export const removeServerGeneratedPropertiesFromObject = ( object: T, - keys: string | readonly string[] + keys: readonly string[] ): Omit => { // @ts-ignore return omit(object, keys); }; -export const removeServerGeneratedPropertiesFromSavedObject = ( - attributes: T -): Omit => { - const keysToRemove = ['created_at', 'updated_at', 'version', 'id'] as const; - return removeServerGeneratedPropertiesFromObject( - attributes, - keysToRemove - ); +export const removeServerGeneratedPropertiesFromSavedObject = < + T extends CommonSavedObjectAttributes +>( + attributes: T, + keys?: readonly string[] +): Omit< + T, + typeof keys extends string[] + ? typeof savedObjectCommonAttributes[number] & typeof keys[number] + : typeof savedObjectCommonAttributes[number] +> => { + const typedKeys = keys != null ? keys : []; + return removeServerGeneratedPropertiesFromObject< + T, + typeof savedObjectCommonAttributes[number] & typeof typedKeys[number] + >(attributes, [...savedObjectCommonAttributes, ...typedKeys]); }; export const removeServerGeneratedPropertiesFromUserAction = ( @@ -372,6 +384,32 @@ export const removeServerGeneratedPropertiesFromUserAction = ( >(attributes, keysToRemove); }; +export const removeServerGeneratedPropertiesFromSubCase = ( + subCase: SubCaseResponse | undefined +) => { + if (!subCase) { + return; + } + + const keysToRemove = ['closed_at', 'comments'] as const; + return removeServerGeneratedPropertiesFromSavedObject(subCase, keysToRemove); +}; + +export const removeServerGeneratedPropertiesFromCase = ( + theCase: CaseResponse +): Partial => { + const keysToRemove = ['closed_at'] as const; + return removeServerGeneratedPropertiesFromSavedObject(theCase, keysToRemove); +}; + +export const removeServerGeneratedPropertiesFromComments = ( + comments: CommentResponse[] | undefined +): Array> | undefined => { + return comments?.map((comment) => { + return removeServerGeneratedPropertiesFromSavedObject(comment, []); + }); +}; + export const deleteAllCaseItems = async (es: KibanaClient) => { await Promise.all([ deleteCases(es), diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts index 8cfd21a5af2c0..767233f756829 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts @@ -11,12 +11,11 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { CommentType } from '../../../../../../plugins/cases/common/api'; +import { postCaseReq, postCaseResp } from '../../../../common/lib/mock'; import { - postCaseReq, - postCaseResp, removeServerGeneratedPropertiesFromCase, removeServerGeneratedPropertiesFromComments, -} from '../../../../common/lib/mock'; +} from '../../../../common/lib/utils'; import { createRule, createSignalsIndex, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts index 28e33df529cc6..35ed4ba5c3c71 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/sub_cases/get_sub_case.ts @@ -8,19 +8,15 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { - commentsResp, - postCommentAlertReq, - removeServerGeneratedPropertiesFromComments, - removeServerGeneratedPropertiesFromSubCase, - subCaseResp, -} from '../../../../common/lib/mock'; +import { commentsResp, postCommentAlertReq, subCaseResp } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, defaultCreateSubComment, deleteAllCaseItems, deleteCaseAction, + removeServerGeneratedPropertiesFromComments, + removeServerGeneratedPropertiesFromSubCase, } from '../../../../common/lib/utils'; import { getCaseCommentsUrl, From 80e1d8753050adb461804738a72542e875ce953f Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 7 Apr 2021 17:59:51 +0300 Subject: [PATCH 05/25] Convert add case test --- .../cases/server/client/cases/create.test.ts | 482 ------------------ .../case_api_integration/common/lib/mock.ts | 4 +- .../tests/common/cases/post_case.ts | 201 ++++++-- 3 files changed, 149 insertions(+), 538 deletions(-) delete mode 100644 x-pack/plugins/cases/server/client/cases/create.test.ts diff --git a/x-pack/plugins/cases/server/client/cases/create.test.ts b/x-pack/plugins/cases/server/client/cases/create.test.ts deleted file mode 100644 index 1542b025ab96c..0000000000000 --- a/x-pack/plugins/cases/server/client/cases/create.test.ts +++ /dev/null @@ -1,482 +0,0 @@ -/* - * 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 { - ConnectorTypes, - CaseStatuses, - CaseType, - CasesClientPostRequest, -} from '../../../common/api'; -import { isCaseError } from '../../common/error'; - -import { - createMockSavedObjectsRepository, - mockCaseConfigure, - mockCases, -} from '../../routes/api/__fixtures__'; -import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; - -describe('create', () => { - beforeEach(async () => { - jest.restoreAllMocks(); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), - // when we create a case we generate an ID that is used for the saved object. Internally the ID generation code - // calls Date.getTime so we need it to return something even though the inject saved object client is going to - // override it with a different ID anyway - // Otherwise we'll get an error when the function is called - getTime: jest.fn().mockReturnValue(1), - })); - }); - - describe('happy path', () => { - test('it creates the case correctly', async () => { - const postCase: CasesClientPostRequest = { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - tags: ['defacement'], - type: CaseType.individual, - connector: { - id: '123', - name: 'Jira', - type: ConnectorTypes.jira, - fields: { issueType: 'Task', priority: 'High', parent: null }, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseConfigureSavedObject: mockCaseConfigure, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.create(postCase); - - expect(res).toMatchInlineSnapshot(` - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": Object { - "issueType": "Task", - "parent": null, - "priority": "High", - }, - "id": "123", - "name": "Jira", - "type": ".jira", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-it", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - - expect( - casesClient.services.userActionService.postUserActions.mock.calls[0][0].actions - // using a snapshot here so we don't have to update the text field manually each time it changes - ).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "action": "create", - "action_at": "2019-11-25T21:54:48.952Z", - "action_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "action_field": Array [ - "description", - "status", - "tags", - "title", - "connector", - "settings", - ], - "new_value": "{\\"type\\":\\"individual\\",\\"description\\":\\"This is a brand new case of a bad meanie defacing data\\",\\"title\\":\\"Super Bad Security Issue\\",\\"tags\\":[\\"defacement\\"],\\"connector\\":{\\"id\\":\\"123\\",\\"name\\":\\"Jira\\",\\"type\\":\\".jira\\",\\"fields\\":{\\"issueType\\":\\"Task\\",\\"priority\\":\\"High\\",\\"parent\\":null}},\\"settings\\":{\\"syncAlerts\\":true},\\"owner\\":\\"securitySolution\\"}", - "old_value": null, - }, - "references": Array [ - Object { - "id": "mock-it", - "name": "associated-cases", - "type": "cases", - }, - ], - }, - ] - `); - }); - - test('it creates the case without connector in the configuration', async () => { - const postCase: CasesClientPostRequest = { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - tags: ['defacement'], - type: CaseType.individual, - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.create(postCase); - - expect(res).toMatchInlineSnapshot(` - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-it", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); - - test('Allow user to create case without authentication', async () => { - const postCase: CasesClientPostRequest = { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - tags: ['defacement'], - type: CaseType.individual, - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ - savedObjectsClient, - badAuth: true, - }); - const res = await casesClient.client.create(postCase); - - expect(res).toMatchInlineSnapshot(` - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": null, - "full_name": null, - "username": null, - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-it", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); - }); - - describe('unhappy path', () => { - test('it throws when missing title', async () => { - expect.assertions(3); - const postCase = { - description: 'This is a brand new case of a bad meanie defacing data', - tags: ['defacement'], - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .create({ theCase: postCase }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws when missing description', async () => { - expect.assertions(3); - const postCase = { - title: 'a title', - tags: ['defacement'], - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .create({ theCase: postCase }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws when missing tags', async () => { - expect.assertions(3); - const postCase = { - title: 'a title', - description: 'This is a brand new case of a bad meanie defacing data', - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .create({ theCase: postCase }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws when missing connector ', async () => { - expect.assertions(3); - const postCase = { - title: 'a title', - description: 'This is a brand new case of a bad meanie defacing data', - tags: ['defacement'], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .create({ theCase: postCase }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws when connector missing the right fields', async () => { - expect.assertions(3); - const postCase = { - title: 'a title', - description: 'This is a brand new case of a bad meanie defacing data', - tags: ['defacement'], - connector: { - id: '123', - name: 'Jira', - type: ConnectorTypes.jira, - fields: {}, - }, - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .create({ theCase: postCase }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws if you passing status for a new case', async () => { - expect.assertions(3); - const postCase = { - title: 'a title', - description: 'This is a brand new case of a bad meanie defacing data', - tags: ['defacement'], - type: CaseType.individual, - status: CaseStatuses.closed, - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client.create(postCase).catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }); - }); - - it(`Returns an error if postNewCase throws`, async () => { - const postCase: CasesClientPostRequest = { - description: 'Throw an error', - title: 'Super Bad Security Issue', - tags: ['error'], - type: CaseType.individual, - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }; - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - - return casesClient.client.create(postCase).catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(400); - }); - }); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/mock.ts b/x-pack/test/case_api_integration/common/lib/mock.ts index adcb5b6f4f346..c3a6cb8714115 100644 --- a/x-pack/test/case_api_integration/common/lib/mock.ts +++ b/x-pack/test/case_api_integration/common/lib/mock.ts @@ -92,11 +92,11 @@ export const postCommentGenAlertReq: ContextTypeGeneratedAlertType = { }; export const postCaseResp = ( - id: string, + id?: string | null, req: CasePostRequest = postCaseReq ): Partial => ({ ...req, - id, + ...(id != null ? { id } : {}), comments: [], totalAlerts: 0, totalComment: 0, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 2249587620d5f..865a10d9f5c42 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -11,13 +11,22 @@ import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { ConnectorTypes, ConnectorJiraTypeFields, + CaseStatuses, } from '../../../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCaseResp, - removeServerGeneratedPropertiesFromCase, + defaultUser, + postCaseReq, } from '../../../../common/lib/mock'; -import { createCaseAsUser, deleteCases } from '../../../../common/lib/utils'; +import { + createCaseAsUser, + deleteCases, + createCase, + removeServerGeneratedPropertiesFromCase, + removeServerGeneratedPropertiesFromUserAction, + getAllUserAction, +} from '../../../../common/lib/utils'; import { secOnly, secOnlyRead, @@ -39,65 +48,149 @@ export default ({ getService }: FtrProviderContext): void => { await deleteCases(es); }); - it('should post a case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(getPostCaseRequest()) - .expect(200); + describe('happy path', () => { + it('should post a case', async () => { + const postedCase = await createCase( + supertest, + getPostCaseRequest({ + connector: { + id: '123', + name: 'Jira', + type: ConnectorTypes.jira, + fields: { issueType: 'Task', priority: 'High', parent: null }, + }, + }) + ); + const data = removeServerGeneratedPropertiesFromCase(postedCase); - const data = removeServerGeneratedPropertiesFromCase(postedCase); - expect(data).to.eql(postCaseResp(postedCase.id)); - }); + expect(data).to.eql( + postCaseResp( + null, + getPostCaseRequest({ + connector: { + id: '123', + name: 'Jira', + type: ConnectorTypes.jira, + fields: { issueType: 'Task', priority: 'High', parent: null }, + }, + }) + ) + ); + }); - it('unhappy path - 400s when bad query supplied', async () => { - await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - // @ts-expect-error - .send({ ...getPostCaseRequest({ badKey: true }) }) - .expect(400); - }); + it('should create a user action when creating a case', async () => { + const postedCase = await createCase(supertest, getPostCaseRequest()); + const userActions = await getAllUserAction(supertest, postedCase.id); + const creationUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[0]); - it('unhappy path - 400s when connector is not supplied', async () => { - const { connector, ...caseWithoutConnector } = getPostCaseRequest(); + expect(creationUserAction).to.eql({ + action_field: ['description', 'status', 'tags', 'title', 'connector', 'settings'], + action: 'create', + action_by: defaultUser, + new_value: `{"type":"${postedCase.type}","description":"${ + postedCase.description + }","title":"${postedCase.title}","tags":${JSON.stringify( + postedCase.tags + )},"connector":${JSON.stringify(postedCase.connector)},"settings":${JSON.stringify( + postedCase.settings + )},"owner":"${postedCase.owner}"}`, + old_value: null, + case_id: `${postedCase.id}`, + comment_id: null, + sub_case_id: '', + }); + }); - await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(caseWithoutConnector) - .expect(400); - }); + it('creates the case without connector in the configuration', async () => { + const postedCase = await createCase(supertest, getPostCaseRequest()); + const data = removeServerGeneratedPropertiesFromCase(postedCase); - it('unhappy path - 400s when connector has wrong type', async () => { - await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...getPostCaseRequest({ - // @ts-expect-error - connector: { id: 'wrong', name: 'wrong', type: '.not-exists', fields: null }, - }), - }) - .expect(400); + expect(data).to.eql(postCaseResp()); + }); }); - it('unhappy path - 400s when connector has wrong fields', async () => { - await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...getPostCaseRequest({ - // @ts-expect-error - connector: { - id: 'wrong', - name: 'wrong', - type: ConnectorTypes.jira, - fields: { unsupported: 'value' }, - } as ConnectorJiraTypeFields, - }), - }) - .expect(400); + describe('unhappy path', () => { + it('400s when bad query supplied', async () => { + await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + // @ts-expect-error + .send({ ...getPostCaseRequest({ badKey: true }) }) + .expect(400); + }); + + it('400s when connector is not supplied', async () => { + const { connector, ...caseWithoutConnector } = getPostCaseRequest(); + + await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(caseWithoutConnector) + .expect(400); + }); + + it('400s when connector has wrong type', async () => { + await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getPostCaseRequest({ + // @ts-expect-error + connector: { id: 'wrong', name: 'wrong', type: '.not-exists', fields: null }, + }), + }) + .expect(400); + }); + + it('400s when connector has wrong fields', async () => { + await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getPostCaseRequest({ + // @ts-expect-error + connector: { + id: 'wrong', + name: 'wrong', + type: ConnectorTypes.jira, + fields: { unsupported: 'value' }, + } as ConnectorJiraTypeFields, + }), + }) + .expect(400); + }); + + it('400s when missing title', async () => { + const { title, ...caseWithoutTitle } = getPostCaseRequest(); + + await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(caseWithoutTitle).expect(400); + }); + + it('400s when missing description', async () => { + const { description, ...caseWithoutDescription } = getPostCaseRequest(); + + await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(caseWithoutDescription) + .expect(400); + }); + + it('400s when missing tags', async () => { + const { tags, ...caseWithoutTags } = getPostCaseRequest(); + + await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(caseWithoutTags).expect(400); + }); + + it('400s if you passing status for a new case', async () => { + const req = getPostCaseRequest(); + + await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ ...req, status: CaseStatuses.open }) + .expect(400); + }); }); describe('rbac', () => { From 265adbf17eace19c2ae73127614e842badcde71d Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 8 Apr 2021 14:49:41 +0300 Subject: [PATCH 06/25] Improve utils --- .../case_api_integration/common/lib/utils.ts | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 973145aff518d..193313caa59b0 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -346,38 +346,30 @@ interface CommonSavedObjectAttributes { [key: string]: unknown; } -const savedObjectCommonAttributes = ['created_at', 'updated_at', 'version', 'id'] as const; +const savedObjectCommonAttributes = ['created_at', 'updated_at', 'version', 'id']; -export const removeServerGeneratedPropertiesFromObject = ( +const removeServerGeneratedPropertiesFromObject = ( object: T, - keys: readonly string[] + keys: K[] ): Omit => { - // @ts-ignore return omit(object, keys); }; - export const removeServerGeneratedPropertiesFromSavedObject = < T extends CommonSavedObjectAttributes >( attributes: T, - keys?: readonly string[] -): Omit< - T, - typeof keys extends string[] - ? typeof savedObjectCommonAttributes[number] & typeof keys[number] - : typeof savedObjectCommonAttributes[number] -> => { - const typedKeys = keys != null ? keys : []; - return removeServerGeneratedPropertiesFromObject< - T, - typeof savedObjectCommonAttributes[number] & typeof typedKeys[number] - >(attributes, [...savedObjectCommonAttributes, ...typedKeys]); + keys: Array = [] +): Omit => { + return removeServerGeneratedPropertiesFromObject(attributes, [ + ...savedObjectCommonAttributes, + ...keys, + ]); }; export const removeServerGeneratedPropertiesFromUserAction = ( attributes: CaseUserActionResponse -): Omit => { - const keysToRemove = ['action_id', 'action_at'] as const; +) => { + const keysToRemove: Array = ['action_id', 'action_at']; return removeServerGeneratedPropertiesFromObject< CaseUserActionResponse, typeof keysToRemove[number] @@ -391,15 +383,16 @@ export const removeServerGeneratedPropertiesFromSubCase = ( return; } - const keysToRemove = ['closed_at', 'comments'] as const; - return removeServerGeneratedPropertiesFromSavedObject(subCase, keysToRemove); + return removeServerGeneratedPropertiesFromSavedObject(subCase, [ + 'closed_at', + 'comments', + ]); }; export const removeServerGeneratedPropertiesFromCase = ( theCase: CaseResponse ): Partial => { - const keysToRemove = ['closed_at'] as const; - return removeServerGeneratedPropertiesFromSavedObject(theCase, keysToRemove); + return removeServerGeneratedPropertiesFromSavedObject(theCase, ['closed_at']); }; export const removeServerGeneratedPropertiesFromComments = ( From 19d19f19f2026020c9a1ebab7c2a402b90c79b08 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 8 Apr 2021 17:41:59 +0300 Subject: [PATCH 07/25] Convert update case test --- .../cases/server/client/cases/update.test.ts | 772 ------------ .../case_api_integration/common/lib/utils.ts | 15 + .../tests/common/cases/patch_cases.ts | 1059 ++++++++--------- .../tests/common/comments/post_comment.ts | 345 +++--- 4 files changed, 703 insertions(+), 1488 deletions(-) delete mode 100644 x-pack/plugins/cases/server/client/cases/update.test.ts diff --git a/x-pack/plugins/cases/server/client/cases/update.test.ts b/x-pack/plugins/cases/server/client/cases/update.test.ts deleted file mode 100644 index 1269545bf485c..0000000000000 --- a/x-pack/plugins/cases/server/client/cases/update.test.ts +++ /dev/null @@ -1,772 +0,0 @@ -/* - * 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 { ConnectorTypes, CasesPatchRequest, CaseStatuses } from '../../../common/api'; -import { isCaseError } from '../../common/error'; -import { - createMockSavedObjectsRepository, - mockCaseNoConnectorId, - mockCases, - mockCaseComments, -} from '../../routes/api/__fixtures__'; -import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; - -describe('update', () => { - beforeEach(async () => { - jest.restoreAllMocks(); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), - })); - }); - - describe('happy path', () => { - test('it closes the case correctly', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.update(patchCases); - - expect(res).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": "2019-11-25T21:54:48.952Z", - "closed_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-id-1", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "closed", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - - expect( - casesClient.services.userActionService.postUserActions.mock.calls[0][0].actions - ).toEqual([ - { - attributes: { - action: 'update', - action_at: '2019-11-25T21:54:48.952Z', - action_by: { - email: 'd00d@awesome.com', - full_name: 'Awesome D00d', - username: 'awesome', - }, - action_field: ['status'], - new_value: CaseStatuses.closed, - old_value: CaseStatuses.open, - }, - references: [ - { - id: 'mock-id-1', - name: 'associated-cases', - type: 'cases', - }, - ], - }, - ]); - }); - - test('it opens the case correctly', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.open, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: [ - { - ...mockCases[0], - attributes: { ...mockCases[0].attributes, status: CaseStatuses.closed }, - }, - ...mockCases.slice(1), - ], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.update(patchCases); - - expect(res).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-id-1", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - test('it change the status of case to in-progress correctly', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-4', - status: CaseStatuses['in-progress'], - version: 'WzUsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.update(patchCases); - - expect(res).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": Object { - "issueType": "Task", - "parent": null, - "priority": "High", - }, - "id": "123", - "name": "My connector", - "type": ".jira", - }, - "created_at": "2019-11-25T22:32:17.947Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "Oh no, a bad meanie going LOLBins all over the place!", - "external_service": null, - "id": "mock-id-4", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "in-progress", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "LOLBins", - ], - "title": "Another bad one", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - test('it updates a case without a connector.id', async () => { - const patchCases = { - cases: [ - { - id: 'mock-no-connector_id', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.update(patchCases); - - expect(res).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": "2019-11-25T21:54:48.952Z", - "closed_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-no-connector_id", - "settings": Object { - "syncAlerts": true, - }, - "status": "closed", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - test('it updates the connector correctly', async () => { - const patchCases = ({ - cases: [ - { - id: 'mock-id-3', - connector: { - id: '456', - name: 'My connector 2', - type: ConnectorTypes.jira, - fields: { issueType: 'Bug', priority: 'Low', parent: null }, - }, - version: 'WzUsMV0=', - }, - ], - } as unknown) as CasesPatchRequest; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.update(patchCases); - - expect(res).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": Object { - "issueType": "Bug", - "parent": null, - "priority": "Low", - }, - "id": "456", - "name": "My connector 2", - "type": ".jira", - }, - "created_at": "2019-11-25T22:32:17.947Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "Oh no, a bad meanie going LOLBins all over the place!", - "external_service": null, - "id": "mock-id-3", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "LOLBins", - ], - "title": "Another bad one", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - test('it updates alert status when the status is updated and syncAlerts=true', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: [ - { - ...mockCaseComments[3], - references: [ - { - type: 'cases', - name: 'associated-cases', - id: 'mock-id-1', - }, - ], - }, - ], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - casesClient.client.updateAlertsStatus = jest.fn(); - - await casesClient.client.update(patchCases); - - expect(casesClient.client.updateAlertsStatus).toHaveBeenCalledWith({ - alerts: [ - { - id: 'test-id', - index: 'test-index', - status: 'closed', - }, - ], - }); - }); - - test('it does NOT updates alert status when the status is updated and syncAlerts=false', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: [ - { - ...mockCases[0], - attributes: { ...mockCases[0].attributes, settings: { syncAlerts: false } }, - }, - ], - caseCommentSavedObject: [{ ...mockCaseComments[3] }], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - - await casesClient.client.update(patchCases); - - expect(casesClient.esClient.bulk).not.toHaveBeenCalled(); - }); - - test('it updates alert status when syncAlerts is turned on', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - settings: { syncAlerts: true }, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: [ - { - ...mockCases[0], - attributes: { ...mockCases[0].attributes, settings: { syncAlerts: false } }, - }, - ], - caseCommentSavedObject: [{ ...mockCaseComments[3] }], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - casesClient.client.updateAlertsStatus = jest.fn(); - - await casesClient.client.update(patchCases); - - expect(casesClient.client.updateAlertsStatus).toHaveBeenCalledWith({ - alerts: [{ id: 'test-id', index: 'test-index', status: 'open' }], - }); - }); - - test('it does NOT updates alert status when syncAlerts is turned off', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - settings: { syncAlerts: false }, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: [{ ...mockCaseComments[3] }], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - - await casesClient.client.update(patchCases); - - expect(casesClient.esClient.bulk).not.toHaveBeenCalled(); - }); - - test('it updates alert status for multiple cases', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - settings: { syncAlerts: true }, - version: 'WzAsMV0=', - }, - { - id: 'mock-id-2', - status: CaseStatuses.closed, - version: 'WzQsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: [ - { - ...mockCases[0], - attributes: { ...mockCases[0].attributes, settings: { syncAlerts: false } }, - }, - { - ...mockCases[1], - }, - ], - caseCommentSavedObject: [ - { - ...mockCaseComments[3], - references: [ - { - type: 'cases', - name: 'associated-cases', - id: 'mock-id-1', - }, - ], - }, - { - ...mockCaseComments[4], - references: [ - { - type: 'cases', - name: 'associated-cases', - id: 'mock-id-2', - }, - ], - }, - ], - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - casesClient.client.updateAlertsStatus = jest.fn(); - - await casesClient.client.update(patchCases); - - expect(casesClient.client.updateAlertsStatus).toHaveBeenCalledWith({ - alerts: [ - { id: 'test-id', index: 'test-index', status: 'open' }, - { id: 'test-id-2', index: 'test-index-2', status: 'closed' }, - ], - }); - }); - - test('it does NOT call updateAlertsStatus when there is no comments of type alerts', async () => { - const patchCases = { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - - await casesClient.client.update(patchCases); - - expect(casesClient.esClient.bulk).not.toHaveBeenCalled(); - }); - }); - - describe('unhappy path', () => { - test('it throws when missing id', async () => { - expect.assertions(3); - const patchCases = { - cases: [ - { - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - version: 'WzUsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .update({ cases: patchCases }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws when missing version', async () => { - expect.assertions(3); - const patchCases = { - cases: [ - { - id: 'mock-id-3', - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return ( - casesClient.client - // @ts-expect-error - .update({ cases: patchCases }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(400); - }) - ); - }); - - test('it throws when fields are identical', async () => { - expect.assertions(5); - const patchCases = { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.open, - version: 'WzAsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client.update(patchCases).catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(406); - expect(boomErr.message).toContain('All update fields are identical to current version.'); - }); - }); - - test('it throws when case does not exist', async () => { - expect.assertions(5); - const patchCases = { - cases: [ - { - id: 'not-exists', - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - version: 'WzUsMV0=', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client.update(patchCases).catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(404); - expect(boomErr.message).toContain( - 'These cases not-exists do not exist. Please check you have the correct ids.' - ); - }); - }); - - test('it throws when cases conflicts', async () => { - expect.assertions(5); - const patchCases = { - cases: [ - { - id: 'mock-id-1', - version: 'WzAsMV1=', - title: 'Super Bad Security Issue', - }, - ], - }; - - const savedObjectsClient = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - return casesClient.client.update(patchCases).catch((e) => { - expect(e).not.toBeNull(); - expect(isCaseError(e)).toBeTruthy(); - const boomErr = e.boomify(); - expect(boomErr.isBoom).toBe(true); - expect(boomErr.output.statusCode).toBe(409); - expect(boomErr.message).toContain( - 'These cases mock-id-1 has been updated. Please refresh before saving additional updates.' - ); - }); - }); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 193313caa59b0..a6c031ad4f836 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -29,6 +29,7 @@ import { CaseUserActionResponse, SubCaseResponse, CommentResponse, + CasesPatchRequest, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -585,3 +586,17 @@ export const getAllUserAction = async ( return userActions; }; + +export const updateCase = async ( + supertest: st.SuperTest, + params: CasesPatchRequest, + expectedHttpCode: number = 200 +): Promise => { + const { body: cases } = await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send(params) + .expect(expectedHttpCode); + + return cases; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index c9abaf4730d36..14aac034670f5 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -15,6 +15,7 @@ import { CaseStatuses, CaseType, CommentType, + ConnectorTypes, } from '../../../../../../plugins/cases/common/api'; import { defaultUser, @@ -23,9 +24,18 @@ import { postCollectionReq, postCommentAlertReq, postCommentUserReq, - removeServerGeneratedPropertiesFromCase, } from '../../../../common/lib/mock'; -import { deleteAllCaseItems, getSignalsWithES, setStatus } from '../../../../common/lib/utils'; +import { + deleteAllCaseItems, + getSignalsWithES, + setStatus, + createCase, + createComment, + updateCase, + getAllUserAction, + removeServerGeneratedPropertiesFromCase, + removeServerGeneratedPropertiesFromUserAction, +} from '../../../../common/lib/utils'; import { createSignalsIndex, deleteSignalsIndex, @@ -49,160 +59,131 @@ export default ({ getService }: FtrProviderContext): void => { await deleteAllCaseItems(es); }); - it('should patch a case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCases } = await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ + describe('happy path', () => { + it('should patch a case', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCases = await updateCase(supertest, { cases: [ { id: postedCase.id, version: postedCase.version, - status: 'closed', + title: 'new title', }, ], - }) - .expect(200); - - const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); - expect(data).to.eql({ - ...postCaseResp(postedCase.id), - closed_by: defaultUser, - status: 'closed', - updated_by: defaultUser, + }); + + const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + expect(data).to.eql({ + ...postCaseResp(), + title: 'new title', + updated_by: defaultUser, + }); }); - }); - it('should patch a case with new connector', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCases } = await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ + it('should closes the case correctly', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCases = await updateCase(supertest, { cases: [ { id: postedCase.id, version: postedCase.version, - connector: { - id: 'jira', - name: 'Jira', - type: '.jira', - fields: { issueType: 'Task', priority: null, parent: null }, - }, + status: CaseStatuses.closed, }, ], - }) - .expect(200); - - const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); - expect(data).to.eql({ - ...postCaseResp(postedCase.id), - connector: { - id: 'jira', - name: 'Jira', - type: '.jira', - fields: { issueType: 'Task', priority: null, parent: null }, - }, - updated_by: defaultUser, + }); + + const userActions = await getAllUserAction(supertest, postedCase.id); + const statusUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); + const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + + expect(data).to.eql({ + ...postCaseResp(), + status: CaseStatuses.closed, + closed_by: defaultUser, + updated_by: defaultUser, + }); + + expect(statusUserAction).to.eql({ + action_field: ['status'], + action: 'update', + action_by: defaultUser, + new_value: CaseStatuses.closed, + old_value: CaseStatuses.open, + case_id: `${postedCase.id}`, + comment_id: null, + sub_case_id: '', + }); }); - }); - it('unhappy path - 404s when case is not there', async () => { - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ + it('should change the status of case to in-progress correctly', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCases = await updateCase(supertest, { cases: [ { - id: 'not-real', - version: 'version', - status: 'closed', + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses['in-progress'], }, ], - }) - .expect(404); - }); + }); + + const userActions = await getAllUserAction(supertest, postedCase.id); + const statusUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); + const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + + expect(data).to.eql({ + ...postCaseResp(), + status: CaseStatuses['in-progress'], + updated_by: defaultUser, + }); - // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests - it.skip('should 400 and not allow converting a collection back to an individual case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCollectionReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ + expect(statusUserAction).to.eql({ + action_field: ['status'], + action: 'update', + action_by: defaultUser, + new_value: CaseStatuses['in-progress'], + old_value: CaseStatuses.open, + case_id: `${postedCase.id}`, + comment_id: null, + sub_case_id: '', + }); + }); + + it('should patch a case with new connector', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCases = await updateCase(supertest, { cases: [ { id: postedCase.id, version: postedCase.version, - type: CaseType.individual, + connector: { + id: 'jira', + name: 'Jira', + type: ConnectorTypes.jira, + fields: { issueType: 'Task', priority: null, parent: null }, + }, }, ], - }) - .expect(400); - }); + }); - // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests - it.skip('should allow converting an individual case to a collection when it does not have alerts', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: patchedCase.id, - version: patchedCase.version, - type: CaseType.collection, - }, - ], - }) - .expect(200); - }); + const data = removeServerGeneratedPropertiesFromCase(patchedCases[0]); + expect(data).to.eql({ + ...postCaseResp(), + connector: { + id: 'jira', + name: 'Jira', + type: '.jira', + fields: { issueType: 'Task', priority: null, parent: null }, + }, + updated_by: defaultUser, + }); + }); - it('should 400 when attempting to update an individual case to a collection when it has alerts attached to it', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentAlertReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests + it.skip('should allow converting an individual case to a collection when it does not have alerts', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + await updateCase(supertest, { cases: [ { id: patchedCase.id, @@ -210,190 +191,261 @@ export default ({ getService }: FtrProviderContext): void => { type: CaseType.collection, }, ], - }) - .expect(400); + }); + }); }); - it('should 400 when attempting to update the case type when the case connector feature is disabled', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - type: CaseType.collection, - }, - ], - }) - .expect(400); - }); + describe('unhappy path', () => { + it('404s when case is not there', async () => { + await updateCase( + supertest, + { + cases: [ + { + id: 'not-real', + version: 'version', + status: CaseStatuses.closed, + }, + ], + }, + 404 + ); + }); - // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests - it.skip("should 400 when attempting to update a collection case's status", async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCollectionReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: 'closed', - }, - ], - }) - .expect(400); - }); + it('400s when id is missing', async () => { + await updateCase( + supertest, + { + cases: [ + // @ts-expect-error + { + version: 'version', + status: CaseStatuses.closed, + }, + ], + }, + 400 + ); + }); - it('unhappy path - 406s when excess data sent', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - badKey: 'closed', - }, - ], - }) - .expect(406); - }); + it('406s when fields are identical', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.open, + }, + ], + }, + 406 + ); + }); - it('unhappy path - 400s when bad data sent', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: true, - }, - ], - }) - .expect(400); - }); + it('400s when version is missing', async () => { + await updateCase( + supertest, + { + cases: [ + // @ts-expect-error + { + id: 'not-real', + status: CaseStatuses.closed, + }, + ], + }, + 400 + ); + }); - it('unhappy path - 400s when unsupported status sent', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: 'not-supported', - }, - ], - }) - .expect(400); - }); + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests + it.skip('should 400 and not allow converting a collection back to an individual case', async () => { + const postedCase = await createCase(supertest, postCollectionReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + type: CaseType.individual, + }, + ], + }, + 400 + ); + }); - it('unhappy path - 400s when bad connector type sent', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - connector: { id: 'none', name: 'none', type: '.not-exists', fields: null }, - }, - ], - }) - .expect(400); - }); + it('406s when excess data sent', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + // @ts-expect-error + badKey: 'closed', + }, + ], + }, + 406 + ); + }); - it('unhappy path - 400s when bad connector sent', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - connector: { - id: 'none', - name: 'none', - type: '.jira', - fields: { unsupported: 'value' }, + it('400s when bad data sent', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + // @ts-expect-error + status: true, }, - }, - ], - }) - .expect(400); - }); + ], + }, + 400 + ); + }); - it('unhappy path - 409s when conflict', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(`${CASES_URL}`) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: 'version', - status: 'closed', - }, - ], - }) - .expect(409); + it('400s when unsupported status sent', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + // @ts-expect-error + status: 'not-supported', + }, + ], + }, + 400 + ); + }); + + it('400s when bad connector type sent', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + // @ts-expect-error + connector: { id: 'none', name: 'none', type: '.not-exists', fields: null }, + }, + ], + }, + 400 + ); + }); + + it('400s when bad connector sent', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + connector: { + id: 'jira', + name: 'Jira', + // @ts-expect-error + type: ConnectorTypes.jira, + // @ts-expect-error + fields: { unsupported: 'value' }, + }, + }, + ], + }, + 400 + ); + }); + + it('409s when conflict', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: 'version', + // @ts-expect-error + status: 'closed', + }, + ], + }, + 409 + ); + }); + + it('should 400 when attempting to update an individual case to a collection when it has alerts attached to it', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); + await updateCase( + supertest, + { + cases: [ + { + id: patchedCase.id, + version: patchedCase.version, + type: CaseType.collection, + }, + ], + }, + 400 + ); + }); + + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed delete these tests + it('should 400 when attempting to update the case type when the case connector feature is disabled', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + type: CaseType.collection, + }, + ], + }, + 400 + ); + }); + + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests + it.skip("should 400 when attempting to update a collection case's status", async () => { + const postedCase = await createCase(supertest, postCollectionReq); + await updateCase( + supertest, + { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }, + 400 + ); + }); }); describe('alerts', () => { @@ -412,47 +464,34 @@ export default ({ getService }: FtrProviderContext): void => { const signalID = '5f2b9ec41f8febb1c06b5d1045aeabb9874733b7617e88a370510f2fb3a41a5d'; const signalID2 = '4d0f4b1533e46b66b43bdd0330d23f39f2cf42a7253153270e38d30cce9ff0c6'; - const { body: individualCase1 } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - settings: { - syncAlerts: false, - }, - }); + // does NOT updates alert status when adding comments and syncAlerts=false + const individualCase1 = await createCase(supertest, { + ...postCaseReq, + settings: { + syncAlerts: false, + }, + }); - const { body: updatedInd1WithComment } = await supertest - .post(`${CASES_URL}/${individualCase1.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: signalID, - index: defaultSignalsIndex, - rule: { id: 'test-rule-id', name: 'test-index-id' }, - type: CommentType.alert, - }) - .expect(200); + const updatedInd1WithComment = await createComment(supertest, individualCase1.id, { + alertId: signalID, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }); - const { body: individualCase2 } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - settings: { - syncAlerts: false, - }, - }); + const individualCase2 = await createCase(supertest, { + ...postCaseReq, + settings: { + syncAlerts: false, + }, + }); - const { body: updatedInd2WithComment } = await supertest - .post(`${CASES_URL}/${individualCase2.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: signalID2, - index: defaultSignalsIndex, - rule: { id: 'test-rule-id', name: 'test-index-id' }, - type: CommentType.alert, - }) - .expect(200); + const updatedInd2WithComment = await createComment(supertest, individualCase2.id, { + alertId: signalID2, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }); await es.indices.refresh({ index: defaultSignalsIndex }); @@ -470,6 +509,7 @@ export default ({ getService }: FtrProviderContext): void => { CaseStatuses.open ); + // does NOT updates alert status when the status is updated and syncAlerts=false const updatedIndWithStatus: CasesResponse = (await setStatus({ supertest, cases: [ @@ -503,18 +543,15 @@ export default ({ getService }: FtrProviderContext): void => { CaseStatuses.open ); + // it updates alert status when syncAlerts is turned on // turn on the sync settings - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: updatedIndWithStatus.map((caseInfo) => ({ - id: caseInfo.id, - version: caseInfo.version, - settings: { syncAlerts: true }, - })), - }) - .expect(200); + await updateCase(supertest, { + cases: updatedIndWithStatus.map((caseInfo) => ({ + id: caseInfo.id, + version: caseInfo.version, + settings: { syncAlerts: true }, + })), + }); await es.indices.refresh({ index: defaultSignalsIndex }); @@ -561,37 +598,26 @@ export default ({ getService }: FtrProviderContext): void => { const signalIDInSecondIndex = 'duplicate-signal-id'; const signalsIndex2 = '.siem-signals-default-000002'; - const { body: individualCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - settings: { - syncAlerts: false, - }, - }); + const individualCase = await createCase(supertest, { + ...postCaseReq, + settings: { + syncAlerts: false, + }, + }); - const { body: updatedIndWithComment } = await supertest - .post(`${CASES_URL}/${individualCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: signalIDInFirstIndex, - index: defaultSignalsIndex, - rule: { id: 'test-rule-id', name: 'test-index-id' }, - type: CommentType.alert, - }) - .expect(200); + const updatedIndWithComment = await createComment(supertest, individualCase.id, { + alertId: signalIDInFirstIndex, + index: defaultSignalsIndex, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }); - const { body: updatedIndWithComment2 } = await supertest - .post(`${CASES_URL}/${updatedIndWithComment.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: signalIDInSecondIndex, - index: signalsIndex2, - rule: { id: 'test-rule-id', name: 'test-index-id' }, - type: CommentType.alert, - }) - .expect(200); + const updatedIndWithComment2 = await createComment(supertest, updatedIndWithComment.id, { + alertId: signalIDInSecondIndex, + index: signalsIndex2, + rule: { id: 'test-rule-id', name: 'test-index-id' }, + type: CommentType.alert, + }); await es.indices.refresh({ index: defaultSignalsIndex }); @@ -629,19 +655,15 @@ export default ({ getService }: FtrProviderContext): void => { ).to.be(CaseStatuses.open); // turn on the sync settings - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: updatedIndWithStatus[0].id, - version: updatedIndWithStatus[0].version, - settings: { syncAlerts: true }, - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: updatedIndWithStatus[0].id, + version: updatedIndWithStatus[0].version, + settings: { syncAlerts: true }, + }, + ], + }); await es.indices.refresh({ index: defaultSignalsIndex }); signals = await getSignals(); @@ -675,12 +697,7 @@ export default ({ getService }: FtrProviderContext): void => { it('updates alert status when the status is updated and syncAlerts=true', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); @@ -690,35 +707,26 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - rule: { - id: 'id', - name: 'name', - }, - type: CommentType.alert, - }) - .expect(200); + const caseUpdated = await createComment(supertest, postedCase.id, { + alertId: alert._id, + index: alert._index, + rule: { + id: 'id', + name: 'name', + }, + type: CommentType.alert, + }); await es.indices.refresh({ index: alert._index }); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - status: 'in-progress', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + status: CaseStatuses['in-progress'], + }, + ], + }); // force a refresh on the index that the signal is stored in so that we can search for it and get the correct // status @@ -736,11 +744,10 @@ export default ({ getService }: FtrProviderContext): void => { it('does NOT updates alert status when the status is updated and syncAlerts=false', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, settings: { syncAlerts: false } }) - .expect(200); + const postedCase = await createCase(supertest, { + ...postCaseReq, + settings: { syncAlerts: false }, + }); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); @@ -750,33 +757,25 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - type: CommentType.alert, - rule: { - id: 'id', - name: 'name', - }, - }) - .expect(200); + const caseUpdated = await createComment(supertest, postedCase.id, { + alertId: alert._id, + index: alert._index, + type: CommentType.alert, + rule: { + id: 'id', + name: 'name', + }, + }); - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - status: 'in-progress', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + status: CaseStatuses['in-progress'], + }, + ], + }); const { body: updatedAlert } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) @@ -790,11 +789,10 @@ export default ({ getService }: FtrProviderContext): void => { it('it updates alert status when syncAlerts is turned on', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, settings: { syncAlerts: false } }) - .expect(200); + const postedCase = await createCase(supertest, { + ...postCaseReq, + settings: { syncAlerts: false }, + }); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); @@ -804,49 +802,37 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - rule: { - id: 'id', - name: 'name', - }, - type: CommentType.alert, - }) - .expect(200); + const caseUpdated = await createComment(supertest, postedCase.id, { + alertId: alert._id, + index: alert._index, + rule: { + id: 'id', + name: 'name', + }, + type: CommentType.alert, + }); // Update the status of the case with sync alerts off - const { body: caseStatusUpdated } = await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - status: 'in-progress', - }, - ], - }) - .expect(200); + const caseStatusUpdated = await updateCase(supertest, { + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + status: CaseStatuses['in-progress'], + }, + ], + }); // Turn sync alerts on - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseStatusUpdated[0].id, - version: caseStatusUpdated[0].version, - settings: { syncAlerts: true }, - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: caseStatusUpdated[0].id, + version: caseStatusUpdated[0].version, + settings: { syncAlerts: true }, + }, + ], + }); // refresh the index because syncAlerts was set to true so the alert's status should have been updated await es.indices.refresh({ index: alert._index }); @@ -863,12 +849,7 @@ export default ({ getService }: FtrProviderContext): void => { it('it does NOT updates alert status when syncAlerts is turned off', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - + const postedCase = await createCase(supertest, postCaseReq); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); @@ -877,49 +858,37 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; expect(alert._source.signal.status).eql('open'); - const { body: caseUpdated } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - alertId: alert._id, - index: alert._index, - type: CommentType.alert, - rule: { - id: 'id', - name: 'name', - }, - }) - .expect(200); + const caseUpdated = await createComment(supertest, postedCase.id, { + alertId: alert._id, + index: alert._index, + type: CommentType.alert, + rule: { + id: 'id', + name: 'name', + }, + }); // Turn sync alerts off - const { body: caseSettingsUpdated } = await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseUpdated.id, - version: caseUpdated.version, - settings: { syncAlerts: false }, - }, - ], - }) - .expect(200); + const caseSettingsUpdated = await updateCase(supertest, { + cases: [ + { + id: caseUpdated.id, + version: caseUpdated.version, + settings: { syncAlerts: false }, + }, + ], + }); // Update the status of the case with sync alerts off - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: caseSettingsUpdated[0].id, - version: caseSettingsUpdated[0].version, - status: 'in-progress', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: caseSettingsUpdated[0].id, + version: caseSettingsUpdated[0].version, + status: CaseStatuses['in-progress'], + }, + ], + }); const { body: updatedAlert } = await supertest .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index 19ec3b3dc7f89..094cecd6ffa5b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../../plugins/security_solution/common/constants'; import { - CaseResponse, CommentsResponse, CommentType, AttributesTypeUser, @@ -65,201 +64,214 @@ export default ({ getService }: FtrProviderContext): void => { await deleteCasesUserActions(es); }); - it('should post a comment', async () => { - const postedCase = await createCase(supertest, postCaseReq); - const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - const comment = removeServerGeneratedPropertiesFromSavedObject( - patchedCase.comments![0] as AttributesTypeUser - ); - - expect(comment).to.eql({ - type: postCommentUserReq.type, - comment: postCommentUserReq.comment, - associationType: 'case', - created_by: defaultUser, - pushed_at: null, - pushed_by: null, - updated_by: null, - }); + describe('happy path', () => { + it('should post a comment', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const comment = removeServerGeneratedPropertiesFromSavedObject( + patchedCase.comments![0] as AttributesTypeUser + ); - // updates the case correctly after adding a comment - expect(patchedCase.totalComment).to.eql(patchedCase.comments!.length); - expect(patchedCase.updated_by).to.eql(defaultUser); - }); + expect(comment).to.eql({ + type: postCommentUserReq.type, + comment: postCommentUserReq.comment, + associationType: 'case', + created_by: defaultUser, + pushed_at: null, + pushed_by: null, + updated_by: null, + }); - it('should post an alert', async () => { - const postedCase = await createCase(supertest, postCaseReq); - const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); - const comment = removeServerGeneratedPropertiesFromSavedObject( - patchedCase.comments![0] as AttributesTypeAlerts - ); - - expect(comment).to.eql({ - type: postCommentAlertReq.type, - alertId: postCommentAlertReq.alertId, - index: postCommentAlertReq.index, - rule: postCommentAlertReq.rule, - associationType: 'case', - created_by: defaultUser, - pushed_at: null, - pushed_by: null, - updated_by: null, + // updates the case correctly after adding a comment + expect(patchedCase.totalComment).to.eql(patchedCase.comments!.length); + expect(patchedCase.updated_by).to.eql(defaultUser); }); - // updates the case correctly after adding a comment - expect(patchedCase.totalComment).to.eql(patchedCase.comments!.length); - expect(patchedCase.updated_by).to.eql(defaultUser); - }); + it('should post an alert', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); + const comment = removeServerGeneratedPropertiesFromSavedObject( + patchedCase.comments![0] as AttributesTypeAlerts + ); - it('creates a user action', async () => { - const postedCase = await createCase(supertest, postCaseReq); - const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - const userActions = await getAllUserAction(supertest, postedCase.id); - const commentUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); - - expect(commentUserAction).to.eql({ - action_field: ['comment'], - action: 'create', - action_by: defaultUser, - new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}"}`, - old_value: null, - case_id: `${postedCase.id}`, - comment_id: `${patchedCase.comments![0].id}`, - sub_case_id: '', - }); - }); + expect(comment).to.eql({ + type: postCommentAlertReq.type, + alertId: postCommentAlertReq.alertId, + index: postCommentAlertReq.index, + rule: postCommentAlertReq.rule, + associationType: 'case', + created_by: defaultUser, + pushed_at: null, + pushed_by: null, + updated_by: null, + }); - it('unhappy path - 400s when type is missing', async () => { - const postedCase = await createCase(supertest, postCaseReq); - await createComment( - supertest, - postedCase.id, - { - // @ts-expect-error - bad: 'comment', - }, - 400 - ); - }); + // updates the case correctly after adding a comment + expect(patchedCase.totalComment).to.eql(patchedCase.comments!.length); + expect(patchedCase.updated_by).to.eql(defaultUser); + }); - it('unhappy path - 400s when missing attributes for type user', async () => { - const postedCase = await createCase(supertest, postCaseReq); - await createComment( - supertest, - postedCase.id, - // @ts-expect-error - { - type: CommentType.user, - }, - 400 - ); + it('creates a user action', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const userActions = await getAllUserAction(supertest, postedCase.id); + const commentUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); + + expect(commentUserAction).to.eql({ + action_field: ['comment'], + action: 'create', + action_by: defaultUser, + new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}"}`, + old_value: null, + case_id: `${postedCase.id}`, + comment_id: `${patchedCase.comments![0].id}`, + sub_case_id: '', + }); + }); }); - it('unhappy path - 400s when adding excess attributes for type user', async () => { - const postedCase = await createCase(supertest, postCaseReq); - - for (const attribute of ['alertId', 'index']) { + describe('unhappy path', () => { + it('400s when type is missing', async () => { + const postedCase = await createCase(supertest, postCaseReq); await createComment( supertest, postedCase.id, { - type: CommentType.user, - [attribute]: attribute, - comment: 'a comment', + // @ts-expect-error + bad: 'comment', }, 400 ); - } - }); - - it('unhappy path - 400s when missing attributes for type alert', async () => { - const postedCase = await createCase(supertest, postCaseReq); - - const allRequestAttributes = { - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - rule: { - id: 'id', - name: 'name', - }, - }; - - for (const attribute of ['alertId', 'index']) { - const requestAttributes = omit(attribute, allRequestAttributes); - // @ts-expect-error - await createComment(supertest, postedCase.id, requestAttributes, 400); - } - }); - - it('unhappy path - 400s when adding excess attributes for type alert', async () => { - const postedCase = await createCase(supertest, postCaseReq); + }); - for (const attribute of ['comment']) { + it('400s when missing attributes for type user', async () => { + const postedCase = await createCase(supertest, postCaseReq); await createComment( supertest, postedCase.id, + // @ts-expect-error { - type: CommentType.alert, - [attribute]: attribute, - alertId: 'test-id', - index: 'test-index', - rule: { - id: 'id', - name: 'name', - }, + type: CommentType.user, }, 400 ); - } - }); + }); + + it('400s when adding excess attributes for type user', async () => { + const postedCase = await createCase(supertest, postCaseReq); - it('unhappy path - 400s when case is missing', async () => { - await createComment( - supertest, - 'not-exists', - { + for (const attribute of ['alertId', 'index']) { + await createComment( + supertest, + postedCase.id, + { + type: CommentType.user, + [attribute]: attribute, + comment: 'a comment', + }, + 400 + ); + } + }); + + it('400s when missing attributes for type alert', async () => { + const postedCase = await createCase(supertest, postCaseReq); + + const allRequestAttributes = { + type: CommentType.alert, + index: 'test-index', + alertId: 'test-id', + rule: { + id: 'id', + name: 'name', + }, + }; + + for (const attribute of ['alertId', 'index']) { + const requestAttributes = omit(attribute, allRequestAttributes); // @ts-expect-error - bad: 'comment', - }, - 400 - ); - }); + await createComment(supertest, postedCase.id, requestAttributes, 400); + } + }); - it('unhappy path - 400s when adding an alert to a closed case', async () => { - const postedCase = await createCase(supertest, postCaseReq); + it('400s when adding excess attributes for type alert', async () => { + const postedCase = await createCase(supertest, postCaseReq); - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ + for (const attribute of ['comment']) { + await createComment( + supertest, + postedCase.id, { - id: postedCase.id, - version: postedCase.version, - status: 'closed', + type: CommentType.alert, + [attribute]: attribute, + alertId: 'test-id', + index: 'test-index', + rule: { + id: 'id', + name: 'name', + }, }, - ], - }) - .expect(200); + 400 + ); + } + }); - await createComment(supertest, postedCase.id, postCommentAlertReq, 400); - }); + it('400s when case is missing', async () => { + await createComment( + supertest, + 'not-exists', + { + // @ts-expect-error + bad: 'comment', + }, + 400 + ); + }); - // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests - it.skip('400s when adding an alert to a collection case', async () => { - const postedCase = await createCase(supertest, postCollectionReq); - await createComment(supertest, postedCase.id, postCommentAlertReq, 400); - }); + it('400s when adding an alert to a closed case', async () => { + const postedCase = await createCase(supertest, postCaseReq); + + await supertest + .patch(CASES_URL) + .set('kbn-xsrf', 'true') + .send({ + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: 'closed', + }, + ], + }) + .expect(200); - it('400s when adding a generated alert to an individual case', async () => { - const postedCase = await createCase(supertest, postCaseReq); + await createComment(supertest, postedCase.id, postCommentAlertReq, 400); + }); - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentGenAlertReq) - .expect(400); + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests + it.skip('400s when adding an alert to a collection case', async () => { + const postedCase = await createCase(supertest, postCollectionReq); + await createComment(supertest, postedCase.id, postCommentAlertReq, 400); + }); + + it('400s when adding a generated alert to an individual case', async () => { + const postedCase = await createCase(supertest, postCaseReq); + + await supertest + .post(`${CASES_URL}/${postedCase.id}/comments`) + .set('kbn-xsrf', 'true') + .send(postCommentGenAlertReq) + .expect(400); + }); + + it('should return a 400 when passing the subCaseId', async () => { + const { body } = await supertest + .post(`${CASES_URL}/case-id/comments?subCaseId=value`) + .set('kbn-xsrf', 'true') + .send(postCommentUserReq) + .expect(400); + expect(body.message).to.contain('subCaseId'); + }); }); describe('alerts', () => { @@ -368,15 +380,6 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should return a 400 when passing the subCaseId', async () => { - const { body } = await supertest - .post(`${CASES_URL}/case-id/comments?subCaseId=value`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(400); - expect(body.message).to.contain('subCaseId'); - }); - describe('alert format', () => { type AlertComment = CommentType.alert | CommentType.generatedAlert; From bf3177b62c7da25e77ef7f0a0184709efd5b4a9e Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 8 Apr 2021 18:40:18 +0300 Subject: [PATCH 08/25] Delete internal case client tests --- .../client/alerts/update_status.test.ts | 27 -------- .../client/configure/get_fields.test.ts | 61 ------------------- .../client/configure/get_mappings.test.ts | 54 ---------------- 3 files changed, 142 deletions(-) delete mode 100644 x-pack/plugins/cases/server/client/alerts/update_status.test.ts delete mode 100644 x-pack/plugins/cases/server/client/configure/get_fields.test.ts delete mode 100644 x-pack/plugins/cases/server/client/configure/get_mappings.test.ts diff --git a/x-pack/plugins/cases/server/client/alerts/update_status.test.ts b/x-pack/plugins/cases/server/client/alerts/update_status.test.ts deleted file mode 100644 index 44d6fc244270a..0000000000000 --- a/x-pack/plugins/cases/server/client/alerts/update_status.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 { CaseStatuses } from '../../../common/api'; -import { createMockSavedObjectsRepository } from '../../routes/api/__fixtures__'; -import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; - -describe('updateAlertsStatus', () => { - it('updates the status of the alert correctly', async () => { - const savedObjectsClient = createMockSavedObjectsRepository(); - - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - await casesClient.client.updateStatus({ - alerts: [{ id: 'alert-id-1', index: '.siem-signals', status: CaseStatuses.closed }], - }); - - expect(casesClient.services.alertsService.updateAlertsStatus).toHaveBeenCalledWith({ - logger: expect.anything(), - scopedClusterClient: expect.anything(), - alerts: [{ id: 'alert-id-1', index: '.siem-signals', status: CaseStatuses.closed }], - }); - }); -}); diff --git a/x-pack/plugins/cases/server/client/configure/get_fields.test.ts b/x-pack/plugins/cases/server/client/configure/get_fields.test.ts deleted file mode 100644 index 2e2973516d0fd..0000000000000 --- a/x-pack/plugins/cases/server/client/configure/get_fields.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 { ConnectorTypes } from '../../../common/api'; - -import { createMockSavedObjectsRepository, mockCaseMappings } from '../../routes/api/__fixtures__'; -import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; -import { actionsClientMock } from '../../../../actions/server/actions_client.mock'; -import { actionsErrResponse, mappings, mockGetFieldsResponse } from './mock'; -describe('get_fields', () => { - const execute = jest.fn().mockReturnValue(mockGetFieldsResponse); - const actionsMock = { ...actionsClientMock.create(), execute }; - beforeEach(async () => { - jest.clearAllMocks(); - }); - - describe('happy path', () => { - test('it gets fields', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseMappingsSavedObject: mockCaseMappings, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.getFields({ - actionsClient: actionsMock, - connectorType: ConnectorTypes.jira, - connectorId: '123', - }); - expect(res).toEqual({ - fields: [ - { id: 'summary', name: 'Summary', required: true, type: 'text' }, - { id: 'description', name: 'Description', required: false, type: 'text' }, - ], - defaultMappings: mappings[ConnectorTypes.jira], - }); - }); - }); - - describe('unhappy path', () => { - test('it throws error', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseMappingsSavedObject: mockCaseMappings, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - await casesClient.client - .getFields({ - actionsClient: { ...actionsMock, execute: jest.fn().mockReturnValue(actionsErrResponse) }, - connectorType: ConnectorTypes.jira, - connectorId: '123', - }) - .catch((e) => { - expect(e).not.toBeNull(); - expect(e.isBoom).toBe(true); - expect(e.output.statusCode).toBe(424); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts b/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts deleted file mode 100644 index 0ec2fc8b4621d..0000000000000 --- a/x-pack/plugins/cases/server/client/configure/get_mappings.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 { ConnectorTypes } from '../../../common/api'; - -import { createMockSavedObjectsRepository, mockCaseMappings } from '../../routes/api/__fixtures__'; -import { createCasesClientWithMockSavedObjectsClient } from '../mocks'; -import { actionsClientMock } from '../../../../actions/server/actions_client.mock'; -import { mappings, mockGetFieldsResponse } from './mock'; - -describe('get_mappings', () => { - const execute = jest.fn().mockReturnValue(mockGetFieldsResponse); - const actionsMock = { ...actionsClientMock.create(), execute }; - beforeEach(async () => { - jest.restoreAllMocks(); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), - })); - }); - - describe('happy path', () => { - test('it gets existing mappings', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseMappingsSavedObject: mockCaseMappings, - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.getMappings({ - actionsClient: actionsMock, - connectorType: ConnectorTypes.jira, - connectorId: '123', - }); - - expect(res).toEqual(mappings[ConnectorTypes.jira]); - }); - test('it creates new mappings', async () => { - const savedObjectsClient = createMockSavedObjectsRepository({ - caseMappingsSavedObject: [], - }); - const casesClient = await createCasesClientWithMockSavedObjectsClient({ savedObjectsClient }); - const res = await casesClient.client.getMappings({ - actionsClient: actionsMock, - connectorType: ConnectorTypes.jira, - connectorId: '123', - }); - - expect(res).toEqual(mappings[ConnectorTypes.jira]); - }); - }); -}); From bd34566267710e86cdd0759e419512fb19c32476 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 8 Apr 2021 20:12:25 +0300 Subject: [PATCH 09/25] Convert delete comment test --- .../api/cases/comments/delete_comment.test.ts | 66 ----------- .../case_api_integration/common/lib/utils.ts | 15 +++ .../tests/common/cases/patch_cases.ts | 1 - .../tests/common/cases/post_case.ts | 7 +- .../tests/common/comments/delete_comment.ts | 108 +++++++----------- 5 files changed, 58 insertions(+), 139 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts deleted file mode 100644 index dcbcd7b9e246d..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, - mockCaseComments, -} from '../../__fixtures__'; -import { initDeleteCommentApi } from './delete_comment'; -import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; - -describe('DELETE comment', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initDeleteCommentApi, 'delete'); - }); - it(`deletes the comment. responds with 204`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENT_DETAILS_URL, - method: 'delete', - params: { - case_id: 'mock-id-1', - comment_id: 'mock-comment-1', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(204); - }); - it(`returns an error when thrown from deleteComment service`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENT_DETAILS_URL, - method: 'delete', - params: { - case_id: 'mock-id-1', - comment_id: 'bad-guy', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index a6c031ad4f836..9e8f8afeaf2c8 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -600,3 +600,18 @@ export const updateCase = async ( return cases; }; + +export const deleteComment = async ( + supertest: st.SuperTest, + caseId: string, + commentId: string, + expectedHttpCode: number = 204 +): Promise => { + const { body: comment } = await supertest + .delete(`${CASES_URL}/${caseId}/comments/${commentId}`) + .set('kbn-xsrf', 'true') + .expect(expectedHttpCode) + .send(); + + return comment; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 14aac034670f5..7cf7f632a3391 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -8,7 +8,6 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../../plugins/security_solution/common/constants'; import { CasesResponse, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 865a10d9f5c42..4f5aa77005e5e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -13,12 +13,7 @@ import { ConnectorJiraTypeFields, CaseStatuses, } from '../../../../../../plugins/cases/common/api'; -import { - getPostCaseRequest, - postCaseResp, - defaultUser, - postCaseReq, -} from '../../../../common/lib/mock'; +import { getPostCaseRequest, postCaseResp, defaultUser } from '../../../../common/lib/mock'; import { createCaseAsUser, deleteCases, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts index 1b3681b204f5c..7db6cfa5322eb 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts @@ -18,6 +18,9 @@ import { deleteCases, deleteCasesUserActions, deleteComments, + createCase, + createComment, + deleteComment, } from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export @@ -32,77 +35,50 @@ export default ({ getService }: FtrProviderContext): void => { await deleteCasesUserActions(es); }); - it('should delete a comment', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body: comment } = await supertest - .delete(`${CASES_URL}/${postedCase.id}/comments/${patchedCase.comments[0].id}`) - .set('kbn-xsrf', 'true') - .expect(204) - .send(); - expect(comment).to.eql({}); - }); + describe('happy path', () => { + it('should delete a comment', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const comment = await deleteComment(supertest, postedCase.id, patchedCase.comments![0].id); - it('unhappy path - 404s when comment belongs to different case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body } = await supertest - .delete(`${CASES_URL}/fake-id/comments/${patchedCase.comments[0].id}`) - .set('kbn-xsrf', 'true') - .send() - .expect(404); - - expect(body.message).to.eql( - `This comment ${patchedCase.comments[0].id} does not exist in fake-id).` - ); + expect(comment).to.eql({}); + }); }); - it('unhappy path - 404s when comment is not there', async () => { - await supertest - .delete(`${CASES_URL}/fake-id/comments/fake-id`) - .set('kbn-xsrf', 'true') - .send() - .expect(404); - }); + describe('unhappy path', () => { + it('404s when comment belongs to different case', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const message = await deleteComment(supertest, 'fake-id', patchedCase.comments![0].id, 404); - it('should return a 400 when attempting to delete all comments when passing the `subCaseId` parameter', async () => { - const { body } = await supertest - .delete(`${CASES_URL}/case-id/comments?subCaseId=value`) - .set('kbn-xsrf', 'true') - .send() - .expect(400); - // make sure the failure is because of the subCaseId - expect(body.message).to.contain('subCaseId'); - }); + expect(message).to.eql( + `This comment ${patchedCase.comments![0].id} does not exist in fake-id).` + ); + }); + + it('404s when comment is not there', async () => { + await deleteComment(supertest, 'fake-id', 'fake-id', 404); + }); - it('should return a 400 when attempting to delete a single comment when passing the `subCaseId` parameter', async () => { - const { body } = await supertest - .delete(`${CASES_URL}/case-id/comments/comment-id?subCaseId=value`) - .set('kbn-xsrf', 'true') - .send() - .expect(400); - // make sure the failure is because of the subCaseId - expect(body.message).to.contain('subCaseId'); + it('should return a 400 when attempting to delete all comments when passing the `subCaseId` parameter', async () => { + const { body } = await supertest + .delete(`${CASES_URL}/case-id/comments?subCaseId=value`) + .set('kbn-xsrf', 'true') + .send() + .expect(400); + // make sure the failure is because of the subCaseId + expect(body.message).to.contain('subCaseId'); + }); + + it('should return a 400 when attempting to delete a single comment when passing the `subCaseId` parameter', async () => { + const { body } = await supertest + .delete(`${CASES_URL}/case-id/comments/comment-id?subCaseId=value`) + .set('kbn-xsrf', 'true') + .send() + .expect(400); + // make sure the failure is because of the subCaseId + expect(body.message).to.contain('subCaseId'); + }); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests From 3b8ca25ab1aca2fd6a3d4304e88f2836792f0f71 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 8 Apr 2021 20:23:23 +0300 Subject: [PATCH 10/25] Convert get comment test --- .../api/cases/comments/get_comment.test.ts | 71 ------------------- .../case_api_integration/common/lib/utils.ts | 30 ++++++++ .../tests/common/comments/get_all_comments.ts | 30 ++------ .../tests/common/comments/get_comment.ts | 37 +++------- 4 files changed, 47 insertions(+), 121 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts deleted file mode 100644 index 8ee43eaba8a82..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/get_comment.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCaseComments, - mockCases, -} from '../../__fixtures__'; -import { flattenCommentSavedObject } from '../../utils'; -import { initGetCommentApi } from './get_comment'; -import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; - -describe('GET comment', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initGetCommentApi, 'get'); - }); - it(`returns the comment`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENT_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-id-1', - comment_id: 'mock-comment-1', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - const myPayload = mockCaseComments.find((s) => s.id === 'mock-comment-1'); - expect(myPayload).not.toBeUndefined(); - if (myPayload != null) { - expect(response.payload).toEqual(flattenCommentSavedObject(myPayload)); - } - }); - it(`returns an error when getComment throws`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENT_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-id-1', - comment_id: 'not-real', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 9e8f8afeaf2c8..7c3067081a1d9 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -30,6 +30,7 @@ import { SubCaseResponse, CommentResponse, CasesPatchRequest, + AllCommentsResponse, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -615,3 +616,32 @@ export const deleteComment = async ( return comment; }; + +export const getAllComments = async ( + supertest: st.SuperTest, + caseId: string, + expectedHttpCode: number = 204 +): Promise => { + const { body: comments } = await supertest + .get(`${CASES_URL}/${caseId}/comments`) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return comments; +}; + +export const getComment = async ( + supertest: st.SuperTest, + caseId: string, + commentId: string, + expectedHttpCode: number = 204 +): Promise => { + const { body: comment } = await supertest + .get(`${CASES_URL}/${caseId}/comments/${commentId}`) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return comment; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts index f02022e70e330..06eb9d0fb4174 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_all_comments.ts @@ -15,6 +15,9 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, + createCase, + createComment, + getAllComments, } from '../../../../common/lib/utils'; import { CommentType } from '../../../../../../plugins/cases/common/api'; @@ -29,29 +32,10 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should get multiple comments for a single case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body: comments } = await supertest - .get(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + await createComment(supertest, postedCase.id, postCommentUserReq); + await createComment(supertest, postedCase.id, postCommentUserReq); + const comments = await getAllComments(supertest, postedCase.id); expect(comments.length).to.eql(2); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts index 97face4c8734c..e843b31d18dfd 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/get_comment.ts @@ -15,8 +15,11 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, + createCase, + createComment, + getComment, } from '../../../../common/lib/utils'; -import { CommentResponse, CommentType } from '../../../../../../plugins/cases/common/api'; +import { CommentType } from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -29,33 +32,15 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should get a comment', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + const comment = await getComment(supertest, postedCase.id, patchedCase.comments![0].id); - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body: comment } = await supertest - .get(`${CASES_URL}/${postedCase.id}/comments/${patchedCase.comments[0].id}`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(comment).to.eql(patchedCase.comments[0]); + expect(comment).to.eql(patchedCase.comments![0]); }); it('unhappy path - 404s when comment is not there', async () => { - await supertest - .get(`${CASES_URL}/fake-id/comments/fake-comment`) - .set('kbn-xsrf', 'true') - .send() - .expect(404); + await getComment(supertest, 'fake-id', 'fake-id', 404); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests @@ -69,9 +54,7 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should get a sub case comment', async () => { const { newSubCaseInfo: caseInfo } = await createSubCase({ supertest, actionID }); - const { body: comment }: { body: CommentResponse } = await supertest - .get(`${CASES_URL}/${caseInfo.id}/comments/${caseInfo.comments![0].id}`) - .expect(200); + const comment = await getComment(supertest, caseInfo.id, caseInfo.comments![0].id); expect(comment.type).to.be(CommentType.generatedAlert); }); }); From 68f525138d1b04ef8978da8cfba7d385f02dcb84 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 12 Apr 2021 19:15:36 +0300 Subject: [PATCH 11/25] Migrate update comment --- .../api/cases/comments/patch_comment.test.ts | 378 ------------------ .../api/cases/comments/post_comment.test.ts | 326 --------------- .../case_api_integration/common/lib/utils.ts | 16 + .../tests/common/comments/patch_comment.ts | 375 +++++++++-------- 4 files changed, 202 insertions(+), 893 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts deleted file mode 100644 index 9cc0575f9bb94..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/patch_comment.test.ts +++ /dev/null @@ -1,378 +0,0 @@ -/* - * 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 { omit } from 'lodash/fp'; -import { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCaseComments, - mockCases, -} from '../../__fixtures__'; -import { initPatchCommentApi } from './patch_comment'; -import { CASE_COMMENTS_URL } from '../../../../../common/constants'; -import { CommentType } from '../../../../../common/api'; - -describe('PATCH comment', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initPatchCommentApi, 'patch'); - }); - - it(`Patch a comment`, async () => { - const commentID = 'mock-comment-1'; - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-1', - }, - body: { - type: CommentType.user, - comment: 'Update my comment', - id: commentID, - version: 'WzEsMV0=', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - const updatedComment = response.payload.comments.find( - (comment: { id: string }) => comment.id === commentID - ); - expect(updatedComment.comment).toEqual('Update my comment'); - }); - - it(`Patch an alert`, async () => { - const commentID = 'mock-comment-4'; - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-4', - }, - body: { - type: CommentType.alert, - alertId: 'new-id', - index: 'test-index', - rule: { - id: 'rule-id', - name: 'rule', - }, - id: commentID, - version: 'WzYsMV0=', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - const updatedComment = response.payload.comments.find( - (comment: { id: string }) => comment.id === commentID - ); - expect(updatedComment.alertId).toEqual('new-id'); - }); - - it(`it throws when missing attributes: type user`, async () => { - const allRequestAttributes = { - type: CommentType.user, - comment: 'a comment', - }; - - for (const attribute of ['comment']) { - const requestAttributes = omit(attribute, allRequestAttributes); - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: requestAttributes, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it throws when excess attributes are provided: type user`, async () => { - for (const attribute of ['alertId', 'index']) { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - [attribute]: attribute, - comment: 'a comment', - type: CommentType.user, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it throws when missing attributes: type alert`, async () => { - const allRequestAttributes = { - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - }; - - for (const attribute of ['alertId', 'index']) { - const requestAttributes = omit(attribute, allRequestAttributes); - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: requestAttributes, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it throws when excess attributes are provided: type alert`, async () => { - for (const attribute of ['comment']) { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - [attribute]: attribute, - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it fails to change the type of the comment`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-1', - }, - body: { - type: CommentType.alert, - alertId: 'test-id', - index: 'test-index', - rule: { - id: 'rule-id', - name: 'rule', - }, - id: 'mock-comment-1', - version: 'WzEsMV0=', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - expect(response.payload.message).toEqual('You cannot change the type of the comment.'); - }); - - it(`Fails with 409 if version does not match`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-1', - }, - body: { - type: CommentType.user, - id: 'mock-comment-1', - comment: 'Update my comment', - version: 'badv=', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(409); - }); - - it(`Returns an error if updateComment throws`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-1', - }, - body: { - type: CommentType.user, - comment: 'Update my comment', - id: 'mock-comment-does-not-exist', - version: 'WzEsMV0=', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - expect(response.payload.isBoom).toEqual(true); - }); - - describe('alert format', () => { - it.each([ - ['1', ['index1', 'index2'], CommentType.alert, 'mock-comment-4'], - [['1', '2'], 'index', CommentType.alert, 'mock-comment-4'], - ['1', ['index1', 'index2'], CommentType.generatedAlert, 'mock-comment-6'], - [['1', '2'], 'index', CommentType.generatedAlert, 'mock-comment-6'], - ])( - 'returns an error with an alert comment with contents id: %p indices: %p type: %s comment id: %s', - async (alertId, index, type, commentID) => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-4', - }, - body: { - type, - alertId, - index, - rule: { - id: 'rule-id', - name: 'rule', - }, - id: commentID, - version: 'WzYsMV0=', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - } - ); - - it.each([ - ['1', ['index1'], CommentType.alert], - [['1', '2'], ['index', 'other-index'], CommentType.alert], - ])( - 'does not return an error with an alert comment with contents id: %p indices: %p type: %s', - async (alertId, index, type) => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'patch', - params: { - case_id: 'mock-id-4', - }, - body: { - type, - alertId, - index, - rule: { - id: 'rule-id', - name: 'rule', - }, - id: 'mock-comment-4', - // this version is different than the one in mockCaseComments because it gets updated in place - version: 'WzE3LDFd', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - } - ); - }); -}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts deleted file mode 100644 index 807ec0d089a52..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/post_comment.test.ts +++ /dev/null @@ -1,326 +0,0 @@ -/* - * 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 { omit } from 'lodash/fp'; -import { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, - mockCaseComments, -} from '../../__fixtures__'; -import { initPostCommentApi } from './post_comment'; -import { CASE_COMMENTS_URL } from '../../../../../common/constants'; -import { CommentType } from '../../../../../common/api'; - -describe('POST comment', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initPostCommentApi, 'post'); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), - })); - }); - - it(`Posts a new comment`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.comments[response.payload.comments.length - 1].id).toEqual( - 'mock-comment' - ); - }); - - it(`Posts a new comment of type alert`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - type: CommentType.alert, - alertId: 'test-id', - index: 'test-index', - rule: { - id: 'rule-id', - name: 'rule-name', - }, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.comments[response.payload.comments.length - 1].id).toEqual( - 'mock-comment' - ); - }); - - it(`it throws when missing type`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - }); - - it(`it throws when missing attributes: type user`, async () => { - const allRequestAttributes = { - type: CommentType.user, - comment: 'a comment', - }; - - for (const attribute of ['comment']) { - const requestAttributes = omit(attribute, allRequestAttributes); - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: requestAttributes, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it throws when excess attributes are provided: type user`, async () => { - for (const attribute of ['alertId', 'index']) { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - [attribute]: attribute, - comment: 'a comment', - type: CommentType.user, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it throws when missing attributes: type alert`, async () => { - const allRequestAttributes = { - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - }; - - for (const attribute of ['alertId', 'index']) { - const requestAttributes = omit(attribute, allRequestAttributes); - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: requestAttributes, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`it throws when excess attributes are provided: type alert`, async () => { - for (const attribute of ['comment']) { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - [attribute]: attribute, - type: CommentType.alert, - index: 'test-index', - alertId: 'test-id', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - } - }); - - it(`Returns an error if the case does not exist`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'this-is-not-real', - }, - body: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - expect(response.payload.isBoom).toEqual(true); - }); - - it(`Returns an error if postNewCase throws`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - comment: 'Throw an error', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - }); - - it(`Allow user to create comments without authentications`, async () => { - routeHandler = await createRoute(initPostCommentApi, 'post', true); - - const request = httpServerMock.createKibanaRequest({ - path: CASE_COMMENTS_URL, - method: 'post', - params: { - case_id: 'mock-id-1', - }, - body: { - comment: 'Wow, good luck catching that bad meanie!', - type: CommentType.user, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }), - true - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.comments[response.payload.comments.length - 1]).toMatchInlineSnapshot(` - Object { - "associationType": "case", - "comment": "Wow, good luck catching that bad meanie!", - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": null, - "full_name": null, - "username": null, - }, - "id": "mock-comment", - "pushed_at": null, - "pushed_by": null, - "type": "user", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 7c3067081a1d9..bb104d1bd14ed 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -31,6 +31,7 @@ import { CommentResponse, CasesPatchRequest, AllCommentsResponse, + CommentPatchRequest, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -645,3 +646,18 @@ export const getComment = async ( return comment; }; + +export const updateComment = async ( + supertest: st.SuperTest, + caseId: string, + req: CommentPatchRequest, + expectedHttpCode: number = 204 +): Promise => { + const { body: res } = await supertest + .patch(`${CASES_URL}/${caseId}/comments`) + .set('kbn-xsrf', 'true') + .send(req) + .expect(expectedHttpCode); + + return res; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts index 69c3cb78ecc9e..21d9837cc316d 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts @@ -10,7 +10,12 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; -import { CaseResponse, CommentType } from '../../../../../../plugins/cases/common/api'; +import { + AttributesTypeAlerts, + AttributesTypeUser, + CaseResponse, + CommentType, +} from '../../../../../../plugins/cases/common/api'; import { defaultUser, postCaseReq, @@ -25,6 +30,9 @@ import { deleteCases, deleteCasesUserActions, deleteComments, + createCase, + createComment, + updateComment, } from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export @@ -138,121 +146,88 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should patch a comment', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); const newComment = 'Well I decided to update my comment. So what? Deal with it.'; - const { body } = await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, - comment: newComment, - type: CommentType.user, - }) - .expect(200); + const updatedCase = await updateComment(supertest, postedCase.id, { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, + comment: newComment, + type: CommentType.user, + }); - expect(body.comments[0].comment).to.eql(newComment); - expect(body.comments[0].type).to.eql('user'); - expect(body.updated_by).to.eql(defaultUser); + const userComment = updatedCase.comments![0] as AttributesTypeUser; + expect(userComment.comment).to.eql(newComment); + expect(userComment.type).to.eql(CommentType.user); + expect(updatedCase.updated_by).to.eql(defaultUser); }); it('should patch an alert', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentAlertReq) - .expect(200); - - const { body } = await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, - type: CommentType.alert, - alertId: 'new-id', - index: postCommentAlertReq.index, - rule: { - id: 'id', - name: 'name', - }, - }) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); + const updatedCase = await updateComment(supertest, postedCase.id, { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, + type: CommentType.alert, + alertId: 'new-id', + index: postCommentAlertReq.index, + rule: { + id: 'id', + name: 'name', + }, + }); - expect(body.comments[0].alertId).to.eql('new-id'); - expect(body.comments[0].index).to.eql(postCommentAlertReq.index); - expect(body.comments[0].type).to.eql('alert'); - expect(body.updated_by).to.eql(defaultUser); + const alertComment = updatedCase.comments![0] as AttributesTypeAlerts; + expect(alertComment.alertId).to.eql('new-id'); + expect(alertComment.index).to.eql(postCommentAlertReq.index); + expect(alertComment.type).to.eql(CommentType.alert); + expect(alertComment.rule).to.eql({ + id: 'id', + name: 'name', + }); + expect(alertComment.updated_by).to.eql(defaultUser); }); it('unhappy path - 404s when comment is not there', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ + const postedCase = await createCase(supertest, postCaseReq); + await updateComment( + supertest, + postedCase.id, + { id: 'id', version: 'version', type: CommentType.user, comment: 'comment', - }) - .expect(404); + }, + 404 + ); }); it('unhappy path - 404s when case is not there', async () => { - await supertest - .patch(`${CASES_URL}/fake-id/comments`) - .set('kbn-xsrf', 'true') - .send({ + await updateComment( + supertest, + 'fake-id', + { id: 'id', version: 'version', type: CommentType.user, comment: 'comment', - }) - .expect(404); + }, + 404 + ); }); it('unhappy path - 400s when trying to change comment type', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + + await updateComment( + supertest, + postedCase.id, + { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, type: CommentType.alert, alertId: 'test-id', index: 'test-index', @@ -260,73 +235,50 @@ export default ({ getService }: FtrProviderContext): void => { id: 'id', name: 'name', }, - }) - .expect(400); + }, + 400 + ); }); it('unhappy path - 400s when missing attributes for type user', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, - }) - .expect(400); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + + await updateComment( + supertest, + postedCase.id, + // @ts-expect-error + { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, + }, + 400 + ); }); it('unhappy path - 400s when adding excess attributes for type user', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); for (const attribute of ['alertId', 'index']) { - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, + await updateComment( + supertest, + postedCase.id, + { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, comment: 'a comment', type: CommentType.user, [attribute]: attribute, - }) - .expect(400); + }, + 400 + ); } }); it('unhappy path - 400s when missing attributes for type alert', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); const allRequestAttributes = { type: CommentType.alert, @@ -340,38 +292,31 @@ export default ({ getService }: FtrProviderContext): void => { for (const attribute of ['alertId', 'index']) { const requestAttributes = omit(attribute, allRequestAttributes); - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, + await updateComment( + supertest, + postedCase.id, + // @ts-expect-error + { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, ...requestAttributes, - }) - .expect(400); + }, + 400 + ); } }); it('unhappy path - 400s when adding excess attributes for type alert', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); for (const attribute of ['comment']) { - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, - version: patchedCase.comments[0].version, + await updateComment( + supertest, + postedCase.id, + { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, type: CommentType.alert, index: 'test-index', alertId: 'test-id', @@ -380,35 +325,87 @@ export default ({ getService }: FtrProviderContext): void => { name: 'name', }, [attribute]: attribute, - }) - .expect(400); + }, + 400 + ); } }); it('unhappy path - 409s when conflict', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); const newComment = 'Well I decided to update my comment. So what? Deal with it.'; - await supertest - .patch(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send({ - id: patchedCase.comments[0].id, + await updateComment( + supertest, + postedCase.id, + { + id: patchedCase.comments![0].id, version: 'version-mismatch', type: CommentType.user, comment: newComment, - }) - .expect(409); + }, + 409 + ); + }); + + describe('alert format', () => { + type AlertComment = CommentType.alert | CommentType.generatedAlert; + + for (const [alertId, index, type] of [ + ['1', ['index1', 'index2'], CommentType.alert], + [['1', '2'], 'index', CommentType.alert], + ['1', ['index1', 'index2'], CommentType.generatedAlert], + [['1', '2'], 'index', CommentType.generatedAlert], + ]) { + it(`throws an error with an alert comment with contents id: ${alertId} indices: ${index} type: ${type}`, async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment( + supertest, + postedCase.id, + { ...postCommentAlertReq, alertId, index, type: type as AlertComment }, + 400 + ); + + await updateComment( + supertest, + postedCase.id, + { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, + type: type as AlertComment, + alertId, + index, + rule: postCommentAlertReq.rule, + }, + 400 + ); + }); + } + + for (const [alertId, index, type] of [ + ['1', ['index1'], CommentType.alert], + [['1', '2'], ['index', 'other-index'], CommentType.alert], + ]) { + it(`does not throw an error with an alert comment with contents id: ${alertId} indices: ${index} type: ${type}`, async () => { + const postedCase = await createCase(supertest, postCaseReq); + const patchedCase = await createComment( + supertest, + postedCase.id, + { ...postCommentAlertReq, alertId, index, type: type as AlertComment }, + 400 + ); + + await updateComment(supertest, postedCase.id, { + id: patchedCase.comments![0].id, + version: patchedCase.comments![0].version, + type: type as AlertComment, + alertId, + index, + rule: postCommentAlertReq.rule, + }); + }); + } }); }); }; From a50ab4b732afe3776759c9f461900b2ce6013753 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 12 Apr 2021 21:42:36 +0300 Subject: [PATCH 12/25] Migrate configuration --- .../api/cases/configure/get_configure.test.ts | 167 ------ .../cases/configure/get_connectors.test.ts | 142 ------ .../cases/configure/patch_configure.test.ts | 262 ---------- .../cases/configure/post_configure.test.ts | 475 ------------------ .../case_api_integration/common/config.ts | 51 +- .../case_api_integration/common/lib/utils.ts | 130 ++++- .../tests/basic/cases/push_case.ts | 8 +- .../tests/common/configure/get_configure.ts | 98 +++- .../tests/common/configure/get_connectors.ts | 75 ++- .../tests/common/configure/patch_configure.ts | 163 +++--- .../tests/common/configure/post_configure.ts | 155 ++++-- .../tests/common/connectors/case.ts | 90 ++-- .../tests/trial/cases/push_case.ts | 54 +- .../user_actions/get_all_user_actions.ts | 12 +- .../tests/trial/configure/get_connectors.ts | 8 +- 15 files changed, 612 insertions(+), 1278 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts deleted file mode 100644 index 5f6e25f6c8a6d..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/get_configure.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCaseConfigure, - mockCaseMappings, -} from '../../__fixtures__'; - -import { initGetCaseConfigure } from './get_configure'; -import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; -import { mappings } from '../../../../client/configure/mock'; -import { ConnectorTypes } from '../../../../../common/api/connectors'; -import { CasesClient } from '../../../../client'; - -describe('GET configuration', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initGetCaseConfigure, 'get'); - }); - - it('returns the configuration', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - expect(res.payload).toEqual({ - ...mockCaseConfigure[0].attributes, - error: null, - mappings: mappings[ConnectorTypes.jira], - version: mockCaseConfigure[0].version, - }); - }); - - it('handles undefined version correctly', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: [{ ...mockCaseConfigure[0], version: undefined }], - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - expect(res.payload).toEqual({ - connector: { - id: '789', - name: 'My connector 3', - type: '.jira', - fields: null, - }, - closure_type: 'close-by-user', - created_at: '2020-04-09T09:43:51.778Z', - created_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - error: null, - mappings: mappings[ConnectorTypes.jira], - updated_at: '2020-04-09T09:43:51.778Z', - updated_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - version: '', - }); - }); - - it('returns an empty object when there is no configuration', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: [], - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - - expect(res.payload).toEqual({}); - }); - - it('returns an error if find throws an error', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: [{ ...mockCaseConfigure[0], id: 'throw-error-find' }], - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(404); - expect(res.payload.isBoom).toEqual(true); - }); - - it('returns an error when mappings request throws', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: [], - }) - ); - const mockThrowContext = { - ...context, - cases: { - ...context.cases, - getCasesClient: async () => { - return ({ - ...(await context?.cases?.getCasesClient()), - getMappings: async () => { - throw new Error(); - }, - // This avoids ts errors with overriding getMappings - } as unknown) as CasesClient; - }, - }, - }; - - const res = await routeHandler(mockThrowContext, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - expect(res.payload).toEqual({ - ...mockCaseConfigure[0].attributes, - error: 'Error connecting to My connector 3 instance', - mappings: [], - version: mockCaseConfigure[0].version, - }); - }); -}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts deleted file mode 100644 index 3fa0fe2f83f79..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/get_connectors.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCaseConfigure, - mockCaseMappings, -} from '../../__fixtures__'; - -import { initCaseConfigureGetActionConnector } from './get_connectors'; -import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common/constants'; -import { getActions } from '../../__mocks__/request_responses'; - -describe('GET connectors', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initCaseConfigureGetActionConnector, 'get'); - }); - - it('returns case owned connectors', async () => { - const req = httpServerMock.createKibanaRequest({ - path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - - const expected = getActions(); - // The first connector returned by getActions is of type .webhook and we expect to be filtered - expected.shift(); - expect(res.payload).toEqual(expected); - }); - - it('filters out connectors that are not enabled in license', async () => { - const req = httpServerMock.createKibanaRequest({ - path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const actionsClient = context.actions.getActionsClient(); - (actionsClient.listTypes as jest.Mock).mockImplementation(() => - Promise.resolve([ - { - id: '.servicenow', - name: 'ServiceNow', - minimumLicenseRequired: 'platinum', - enabled: false, - enabledInConfig: true, - // User does not have a platinum license - enabledInLicense: false, - }, - { - id: '.jira', - name: 'Jira', - minimumLicenseRequired: 'gold', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.resilient', - name: 'IBM Resilient', - minimumLicenseRequired: 'platinum', - enabled: false, - enabledInConfig: true, - // User does not have a platinum license - enabledInLicense: false, - }, - ]) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - expect(res.payload).toEqual([ - { - id: '456', - actionTypeId: '.jira', - name: 'Connector without isCaseOwned', - config: { - apiUrl: 'https://elastic.jira.com', - }, - isPreconfigured: false, - referencedByCount: 0, - }, - { - id: 'for-mock-case-id-3', - actionTypeId: '.jira', - name: 'For mock case id 3', - config: { - apiUrl: 'https://elastic.jira.com', - }, - isPreconfigured: false, - referencedByCount: 0, - }, - ]); - }); - - it('it throws an error when actions client is null', async () => { - const req = httpServerMock.createKibanaRequest({ - path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - // @ts-expect-error - context.actions = undefined; - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(404); - expect(res.payload.isBoom).toEqual(true); - }); -}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts deleted file mode 100644 index f94d2e462a336..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/patch_configure.test.ts +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCaseMappings, -} from '../../__fixtures__'; - -import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects'; -import { initPatchCaseConfigure } from './patch_configure'; -import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; -import { ConnectorTypes } from '../../../../../common/api/connectors'; -import { CasesClient } from '../../../../client'; - -describe('PATCH configuration', () => { - let routeHandler: RequestHandler; - - beforeAll(async () => { - routeHandler = await createRoute(initPatchCaseConfigure, 'patch'); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2020-04-09T09:43:51.778Z'), - })); - }); - - it('patch configuration', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - closure_type: 'close-by-pushing', - version: mockCaseConfigure[0].version, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - ...mockCaseConfigure[0].attributes, - connector: { fields: null, id: '789', name: 'My connector 3', type: '.jira' }, - closure_type: 'close-by-pushing', - updated_at: '2020-04-09T09:43:51.778Z', - updated_by: { email: 'd00d@awesome.com', full_name: 'Awesome D00d', username: 'awesome' }, - version: 'WzE3LDFd', - }) - ); - }); - - it('patch configuration without authentication', async () => { - routeHandler = await createRoute(initPatchCaseConfigure, 'patch', true); - - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - closure_type: 'close-by-pushing', - version: mockCaseConfigure[0].version, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - ...mockCaseConfigure[0].attributes, - connector: { fields: null, id: '789', name: 'My connector 3', type: '.jira' }, - closure_type: 'close-by-pushing', - updated_at: '2020-04-09T09:43:51.778Z', - updated_by: { email: null, full_name: null, username: null }, - version: 'WzE3LDFd', - }) - ); - }); - - it('patch configuration - connector', async () => { - routeHandler = await createRoute(initPatchCaseConfigure, 'patch'); - - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - connector: { - id: 'connector-new', - name: 'New connector', - type: '.jira', - fields: null, - }, - version: mockCaseConfigure[0].version, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - ...mockCaseConfigure[0].attributes, - connector: { id: 'connector-new', name: 'New connector', type: '.jira', fields: null }, - closure_type: 'close-by-user', - updated_at: '2020-04-09T09:43:51.778Z', - updated_by: { email: 'd00d@awesome.com', full_name: 'Awesome D00d', username: 'awesome' }, - version: 'WzE3LDFd', - }) - ); - }); - - it('patch configuration with error message for getMappings throw', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - closure_type: 'close-by-pushing', - connector: { - id: 'connector-new', - name: 'New connector', - type: '.jira', - fields: null, - }, - version: mockCaseConfigure[0].version, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: [], - }) - ); - const mockThrowContext = { - ...context, - cases: { - ...context.cases, - getCasesClient: async () => { - return ({ - ...(await context?.cases?.getCasesClient()), - getMappings: () => { - throw new Error(); - }, - // This avoids ts errors with overriding getMappings - } as unknown) as CasesClient; - }, - }, - }; - - const res = await routeHandler(mockThrowContext, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - mappings: [], - error: 'Error connecting to New connector instance', - }) - ); - }); - it('throw error when configuration have not being created', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - closure_type: 'close-by-pushing', - version: mockCaseConfigure[0].version, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: [], - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(409); - expect(res.payload.isBoom).toEqual(true); - }); - - it('throw error when the versions are different', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - closure_type: 'close-by-pushing', - version: 'different-version', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(409); - expect(res.payload.isBoom).toEqual(true); - }); - - it('handles undefined version correctly', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'patch', - body: { - connector: { - id: 'no-version', - name: 'no version', - type: ConnectorTypes.none, - fields: null, - }, - version: mockCaseConfigure[0].version, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.payload).toEqual( - expect.objectContaining({ - version: '', - }) - ); - }); -}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts b/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts deleted file mode 100644 index e690d9f870c34..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/configure/post_configure.test.ts +++ /dev/null @@ -1,475 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCaseConfigure, - mockCaseMappings, -} from '../../__fixtures__'; - -import { initPostCaseConfigure } from './post_configure'; -import { newConfiguration } from '../../__mocks__/request_responses'; -import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; -import { ConnectorTypes } from '../../../../../common/api/connectors'; -import { CasesClient } from '../../../../client'; - -describe('POST configuration', () => { - let routeHandler: RequestHandler; - - beforeAll(async () => { - routeHandler = await createRoute(initPostCaseConfigure, 'post'); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2020-04-09T09:43:51.778Z'), - })); - }); - - it('create configuration', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - connector: { - id: '456', - name: 'My connector 2', - type: '.jira', - fields: null, - }, - closure_type: 'close-by-pushing', - created_at: '2020-04-09T09:43:51.778Z', - created_by: { email: 'd00d@awesome.com', full_name: 'Awesome D00d', username: 'awesome' }, - updated_at: null, - updated_by: null, - }) - ); - }); - it('create configuration with error message for getMappings throw', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: [], - }) - ); - const mockThrowContext = { - ...context, - cases: { - ...context.cases, - getCasesClient: async () => { - return ({ - ...(await context?.cases?.getCasesClient()), - getMappings: () => { - throw new Error(); - }, - // This avoids ts errors with overriding getMappings - } as unknown) as CasesClient; - }, - }, - }; - - const res = await routeHandler(mockThrowContext, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - mappings: [], - error: 'Error connecting to My connector 2 instance', - }) - ); - }); - - it('create configuration without authentication', async () => { - routeHandler = await createRoute(initPostCaseConfigure, 'post', true); - - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - connector: { - id: '456', - name: 'My connector 2', - type: '.jira', - fields: null, - }, - closure_type: 'close-by-pushing', - created_at: '2020-04-09T09:43:51.778Z', - created_by: { email: null, full_name: null, username: null }, - updated_at: null, - updated_by: null, - }) - ); - }); - - it('throws when missing connector.id', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - connector: { - name: 'My connector 2', - type: '.jira', - fields: null, - }, - closure_type: 'close-by-pushing', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('throws when missing connector.name', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - connector: { - id: '456', - type: '.jira', - fields: null, - }, - closure_type: 'close-by-pushing', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('throws when missing connector.type', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - connector: { - id: '456', - name: 'My connector 2', - fields: null, - }, - closure_type: 'close-by-pushing', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('throws when missing connector.fields', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - connector: { - id: '456', - name: 'My connector 2', - type: ConnectorTypes.none, - }, - closure_type: 'close-by-pushing', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('throws when missing closure_type', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - connector: { - id: '456', - name: 'My connector 2', - type: '.jira', - fields: null, - }, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('it deletes the previous configuration', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const savedObjectRepository = createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }); - - const { context } = await createRouteContext(savedObjectRepository); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(savedObjectRepository.delete.mock.calls[0][1]).toBe(mockCaseConfigure[0].id); - }); - - it('it does NOT delete when not found', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const savedObjectRepository = createMockSavedObjectsRepository({ - caseConfigureSavedObject: [], - caseMappingsSavedObject: mockCaseMappings, - }); - - const { context } = await createRouteContext(savedObjectRepository); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(savedObjectRepository.delete).not.toHaveBeenCalled(); - }); - - it('it deletes all configuration', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const savedObjectRepository = createMockSavedObjectsRepository({ - caseConfigureSavedObject: [ - mockCaseConfigure[0], - { ...mockCaseConfigure[0], id: 'mock-configuration-2' }, - ], - caseMappingsSavedObject: mockCaseMappings, - }); - - const { context } = await createRouteContext(savedObjectRepository); - - const res = await routeHandler(context, req, kibanaResponseFactory); - - expect(res.status).toEqual(200); - expect(savedObjectRepository.delete.mock.calls[0][1]).toBe(mockCaseConfigure[0].id); - expect(savedObjectRepository.delete.mock.calls[1][1]).toBe('mock-configuration-2'); - }); - - it('returns an error if find throws an error', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: [{ ...mockCaseConfigure[0], id: 'throw-error-find' }], - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(404); - expect(res.payload.isBoom).toEqual(true); - }); - - it('returns an error if delete throws an error', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: newConfiguration, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: [{ ...mockCaseConfigure[0], id: 'throw-error-delete' }], - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(500); - expect(res.payload.isBoom).toEqual(true); - }); - - it('returns an error if post throws an error', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - connector: { - id: 'throw-error-create', - name: 'My connector 2', - fields: null, - }, - closure_type: 'close-by-pushing', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('handles undefined version correctly', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - ...newConfiguration, - connector: { - id: 'no-version', - name: 'no version', - type: ConnectorTypes.none, - fields: null, - }, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(200); - expect(res.payload).toEqual( - expect.objectContaining({ - version: '', - }) - ); - }); - - it('returns an error if fields are not null', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - ...newConfiguration, - connector: { id: 'not-null', name: 'not-null', type: ConnectorTypes.none, fields: {} }, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); - - it('returns an error if the type of the connector does not exists', async () => { - const req = httpServerMock.createKibanaRequest({ - path: CASE_CONFIGURE_URL, - method: 'post', - body: { - ...newConfiguration, - connector: { id: 'not-exists', name: 'not-exist', type: '.not-exists', fields: null }, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseConfigureSavedObject: mockCaseConfigure, - caseMappingsSavedObject: mockCaseMappings, - }) - ); - - const res = await routeHandler(context, req, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload.isBoom).toEqual(true); - }); -}); diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts index 0d9a1030d6808..888ae107f221d 100644 --- a/x-pack/test/case_api_integration/common/config.ts +++ b/x-pack/test/case_api_integration/common/config.ts @@ -62,6 +62,35 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) fs.statSync(path.resolve(__dirname, 'fixtures', 'plugins', file)).isDirectory() ); + const alertingAllFiles = fs.readdirSync( + path.resolve( + __dirname, + '..', + '..', + 'alerting_api_integration', + 'common', + 'fixtures', + 'plugins' + ) + ); + + const alertingPlugins = alertingAllFiles.filter((file) => + fs + .statSync( + path.resolve( + __dirname, + '..', + '..', + 'alerting_api_integration', + 'common', + 'fixtures', + 'plugins', + file + ) + ) + .isDirectory() + ); + return { testFiles: testFiles ? testFiles : [require.resolve('../tests/common')], servers, @@ -90,15 +119,19 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) '--xpack.eventLog.logEntries=true', ...disabledPlugins.map((key) => `--xpack.${key}.enabled=false`), // Actions simulators plugin. Needed for testing push to external services. - `--plugin-path=${path.resolve( - __dirname, - '..', - '..', - 'alerting_api_integration', - 'common', - 'fixtures', - 'plugins' - )}`, + ...alertingPlugins.map( + (pluginDir) => + `--plugin-path=${path.resolve( + __dirname, + '..', + '..', + 'alerting_api_integration', + 'common', + 'fixtures', + 'plugins', + pluginDir + )}` + ), ...plugins.map( (pluginDir) => `--plugin-path=${path.resolve(__dirname, 'fixtures', 'plugins', pluginDir)}` diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index bb104d1bd14ed..8f829b333bb0e 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -12,7 +12,12 @@ import type { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import * as st from 'supertest'; import supertestAsPromised from 'supertest-as-promised'; -import { CASES_URL, SUB_CASES_PATCH_DEL_URL } from '../../../../plugins/cases/common/constants'; +import { + CASES_URL, + CASE_CONFIGURE_CONNECTORS_URL, + CASE_CONFIGURE_URL, + SUB_CASES_PATCH_DEL_URL, +} from '../../../../plugins/cases/common/constants'; import { CasesConfigureRequest, CasesConfigureResponse, @@ -32,11 +37,13 @@ import { CasesPatchRequest, AllCommentsResponse, CommentPatchRequest, + CasesConfigurePatch, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; import { ContextTypeGeneratedAlertType } from '../../../../plugins/cases/server/connectors'; import { SignalHit } from '../../../../plugins/security_solution/server/lib/detection_engine/signals/types'; +import { ActionResult, FindActionResult } from '../../../../plugins/actions/server/types'; import { User } from './authentication/types'; function toArray(input: T | T[]): T[] { @@ -154,11 +161,11 @@ export const createSubCase = async (args: { */ export const createCaseAction = async (supertest: st.SuperTest) => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', - actionTypeId: '.case', + connector_type_id: '.case', config: {}, }) .expect(200); @@ -172,7 +179,7 @@ export const deleteCaseAction = async ( supertest: st.SuperTest, id: string ) => { - await supertest.delete(`/api/actions/action/${id}`).set('kbn-xsrf', 'foo'); + await supertest.delete(`/api/actions/connector/${id}`).set('kbn-xsrf', 'foo'); }; /** @@ -241,7 +248,7 @@ export const createSubCaseComment = async ({ } const caseConnector = await supertest - .post(`/api/actions/action/${actionIDToUse}/_execute`) + .post(`/api/actions/connector/${actionIDToUse}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { @@ -258,7 +265,7 @@ export const createSubCaseComment = async ({ return { newSubCaseInfo: caseConnector.body.data, modifiedSubCases: closedSubCases }; }; -export const getConfiguration = ({ +export const getConfigurationRequest = ({ id = 'none', name = 'none', type = ConnectorTypes.none, @@ -275,19 +282,23 @@ export const getConfiguration = ({ }; }; -export const getConfigurationOutput = (update = false): Partial => { +export const getConfigurationOutput = ( + update = false, + overwrite = {} +): Partial => { return { - ...getConfiguration(), + ...getConfigurationRequest(), error: null, mappings: [], created_by: { email: null, full_name: null, username: 'elastic' }, updated_by: update ? { email: null, full_name: null, username: 'elastic' } : null, + ...overwrite, }; }; export const getServiceNowConnector = () => ({ name: 'ServiceNow Connector', - actionTypeId: '.servicenow', + connector_type_id: '.servicenow', secrets: { username: 'admin', password: 'password', @@ -299,7 +310,7 @@ export const getServiceNowConnector = () => ({ export const getJiraConnector = () => ({ name: 'Jira Connector', - actionTypeId: '.jira', + connector_type_id: '.jira', secrets: { email: 'elastic@elastic.co', apiToken: 'token', @@ -330,7 +341,7 @@ export const getMappings = () => [ export const getResilientConnector = () => ({ name: 'Resilient Connector', - actionTypeId: '.resilient', + connector_type_id: '.resilient', secrets: { apiKeyId: 'id', apiKeySecret: 'secret', @@ -341,6 +352,33 @@ export const getResilientConnector = () => ({ }, }); +export const getServiceNowSIRConnector = () => ({ + name: 'ServiceNow Connector', + connector_type_id: '.servicenow-sir', + secrets: { + username: 'admin', + password: 'password', + }, + config: { + apiUrl: 'http://some.non.existent.com', + }, +}); + +export const getWebhookConnector = () => ({ + name: 'A generic Webhook action', + connector_type_id: '.webhook', + secrets: { + user: 'user', + password: 'password', + }, + config: { + headers: { + 'Content-Type': 'text/plain', + }, + url: 'http://some.non.existent.com', + }, +}); + interface CommonSavedObjectAttributes { id?: string | null; created_at?: string | null; @@ -661,3 +699,73 @@ export const updateComment = async ( return res; }; + +export const getConfiguration = async ( + supertest: st.SuperTest, + expectedHttpCode: number = 200 +): Promise => { + const { body: configuration } = await supertest + .get(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return configuration; +}; + +export const createConfiguration = async ( + supertest: st.SuperTest, + req: CasesConfigureRequest = getConfigurationRequest(), + expectedHttpCode: number = 200 +): Promise => { + const { body: configuration } = await supertest + .post(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(req) + .expect(expectedHttpCode); + + return configuration; +}; + +type CreateConnectorResponse = Omit & { connector_type_id: string }; + +export const createConnector = async ( + supertest: st.SuperTest, + req: Record, + expectedHttpCode: number = 200 +): Promise => { + const { body: connector } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'true') + .send(req) + .expect(expectedHttpCode); + + return connector; +}; + +export const getCaseConnectors = async ( + supertest: st.SuperTest, + expectedHttpCode: number = 200 +): Promise => { + const { body: connectors } = await supertest + .get(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return connectors; +}; + +export const updateConfiguration = async ( + supertest: st.SuperTest, + req: CasesConfigurePatch, + expectedHttpCode: number = 200 +): Promise => { + const { body: configuration } = await supertest + .patch(CASE_CONFIGURE_URL) + .set('kbn-xsrf', 'true') + .send(req) + .expect(expectedHttpCode); + + return configuration; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts index 067171cef30a4..5894d90da68a1 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts @@ -15,7 +15,7 @@ import { deleteCasesUserActions, deleteComments, deleteConfiguration, - getConfiguration, + getConfigurationRequest, getServiceNowConnector, } from '../../../../common/lib/utils'; import { ConnectorTypes } from '../../../../../../plugins/cases/common/api'; @@ -35,7 +35,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should get 403 when trying to create a connector', async () => { await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -48,7 +48,7 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send( - getConfiguration({ + getConfigurationRequest({ id: 'not-exist', name: 'Not exist', type: ConnectorTypes.serviceNowITSM, @@ -61,7 +61,7 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: 'not-exist', name: 'Not exist', type: ConnectorTypes.serviceNowITSM, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts index 5047e5c6b56b1..1f36ecc812c5f 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_configure.ts @@ -8,49 +8,103 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASE_CONFIGURE_URL } from '../../../../../../plugins/cases/common/constants'; +import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; +import { + ExternalServiceSimulator, + getExternalServiceSimulatorPath, +} from '../../../../../alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin'; +import { ConnectorTypes } from '../../../../../../plugins/cases/common/api'; import { - getConfiguration, removeServerGeneratedPropertiesFromSavedObject, getConfigurationOutput, deleteConfiguration, + getConfiguration, + createConfiguration, + getConfigurationRequest, + createConnector, + getServiceNowConnector, } from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const es = getService('es'); + const kibanaServer = getService('kibanaServer'); describe('get_configure', () => { + const actionsRemover = new ActionsRemover(supertest); + let servicenowSimulatorURL: string = ''; + + before(() => { + servicenowSimulatorURL = kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW) + ); + }); + afterEach(async () => { await deleteConfiguration(es); + await actionsRemover.removeAll(); }); it('should return an empty find body correctly if no configuration is loaded', async () => { - const { body } = await supertest - .get(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(body).to.eql({}); + const configuration = await getConfiguration(supertest); + expect(configuration).to.eql({}); }); it('should return a configuration', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); - - const { body } = await supertest - .get(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - const data = removeServerGeneratedPropertiesFromSavedObject(body); + await createConfiguration(supertest); + const configuration = await getConfiguration(supertest); + + const data = removeServerGeneratedPropertiesFromSavedObject(configuration); expect(data).to.eql(getConfigurationOutput()); }); + + it('should return a configuration with mapping', async () => { + const connector = await createConnector(supertest, { + ...getServiceNowConnector(), + config: { apiUrl: servicenowSimulatorURL }, + }); + + actionsRemover.add('default', connector.id, 'action', 'actions'); + + await createConfiguration( + supertest, + getConfigurationRequest({ + id: connector.id, + name: connector.name, + type: connector.connector_type_id as ConnectorTypes, + }) + ); + + const configuration = await getConfiguration(supertest); + const data = removeServerGeneratedPropertiesFromSavedObject(configuration); + expect(data).to.eql( + getConfigurationOutput(false, { + mappings: [ + { + action_type: 'overwrite', + source: 'title', + target: 'short_description', + }, + { + action_type: 'overwrite', + source: 'description', + target: 'description', + }, + { + action_type: 'append', + source: 'comments', + target: 'work_notes', + }, + ], + connector: { + id: connector.id, + name: connector.name, + type: connector.connector_type_id, + fields: null, + }, + }) + ); + }); }); }; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts index 1b6cf2ad56c59..bee8ffec233cb 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts @@ -8,8 +8,16 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../../plugins/cases/common/constants'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; +import { + getCaseConnectors, + createConnector, + getServiceNowConnector, + getJiraConnector, + getResilientConnector, + getServiceNowSIRConnector, + getWebhookConnector, +} from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -22,13 +30,66 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should return an empty find body correctly if no connectors are loaded', async () => { - const { body } = await supertest - .get(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const connectors = await getCaseConnectors(supertest); + expect(connectors).to.eql([]); + }); + + it('should return case owned connectors', async () => { + const sn = await createConnector(supertest, getServiceNowConnector()); + actionsRemover.add('default', sn.id, 'action', 'actions'); + + const jira = await createConnector(supertest, getJiraConnector()); + actionsRemover.add('default', jira.id, 'action', 'actions'); + + const resilient = await createConnector(supertest, getResilientConnector()); + actionsRemover.add('default', resilient.id, 'action', 'actions'); + + const sir = await createConnector(supertest, getServiceNowSIRConnector()); + actionsRemover.add('default', sir.id, 'action', 'actions'); + + // Should not be returned when getting the connectors + const webhook = await createConnector(supertest, getWebhookConnector()); + actionsRemover.add('default', webhook.id, 'action', 'actions'); + + const connectors = await getCaseConnectors(supertest); + expect(connectors).to.eql([ + { + id: jira.id, + actionTypeId: '.jira', + name: 'Jira Connector', + config: { apiUrl: 'http://some.non.existent.com', projectKey: 'pkey' }, + isPreconfigured: false, + referencedByCount: 0, + }, + { + id: resilient.id, + actionTypeId: '.resilient', + name: 'Resilient Connector', + config: { apiUrl: 'http://some.non.existent.com', orgId: 'pkey' }, + isPreconfigured: false, + referencedByCount: 0, + }, + { + id: sn.id, + actionTypeId: '.servicenow', + name: 'ServiceNow Connector', + config: { apiUrl: 'http://some.non.existent.com' }, + isPreconfigured: false, + referencedByCount: 0, + }, + { + id: sir.id, + actionTypeId: '.servicenow-sir', + name: 'ServiceNow Connector', + config: { apiUrl: 'http://some.non.existent.com' }, + isPreconfigured: false, + referencedByCount: 0, + }, + ]); + }); - expect(body).to.eql([]); + it.skip('filters out connectors that are not enabled in license', async () => { + // Should find a way to downgrade license to gold and upgrade back to trial }); }); }; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts index 3feee479455c8..8901447e37b3a 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/patch_configure.ts @@ -7,80 +7,132 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; +import { + ExternalServiceSimulator, + getExternalServiceSimulatorPath, +} from '../../../../../alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin'; -import { CASE_CONFIGURE_URL } from '../../../../../../plugins/cases/common/constants'; import { - getConfiguration, + getConfigurationRequest, removeServerGeneratedPropertiesFromSavedObject, getConfigurationOutput, deleteConfiguration, + createConfiguration, + updateConfiguration, + getServiceNowConnector, + createConnector, } from '../../../../common/lib/utils'; +import { ConnectorTypes } from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const es = getService('es'); + const kibanaServer = getService('kibanaServer'); describe('patch_configure', () => { + const actionsRemover = new ActionsRemover(supertest); + let servicenowSimulatorURL: string = ''; + + before(() => { + servicenowSimulatorURL = kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW) + ); + }); + afterEach(async () => { await deleteConfiguration(es); + await actionsRemover.removeAll(); }); it('should patch a configuration', async () => { - const res = await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); - - const { body } = await supertest - .patch(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send({ closure_type: 'close-by-pushing', version: res.body.version }) - .expect(200); - - const data = removeServerGeneratedPropertiesFromSavedObject(body); + const configuration = await createConfiguration(supertest); + const newConfiguration = await updateConfiguration(supertest, { + closure_type: 'close-by-pushing', + version: configuration.version, + }); + + const data = removeServerGeneratedPropertiesFromSavedObject(newConfiguration); expect(data).to.eql({ ...getConfigurationOutput(true), closure_type: 'close-by-pushing' }); }); + it('should patch a configuration: connector', async () => { + const connector = await createConnector(supertest, { + ...getServiceNowConnector(), + config: { apiUrl: servicenowSimulatorURL }, + }); + + actionsRemover.add('default', connector.id, 'action', 'actions'); + + const configuration = await createConfiguration(supertest); + const newConfiguration = await updateConfiguration(supertest, { + ...getConfigurationRequest({ + id: connector.id, + name: connector.name, + type: connector.connector_type_id as ConnectorTypes, + fields: null, + }), + version: configuration.version, + }); + + const data = removeServerGeneratedPropertiesFromSavedObject(newConfiguration); + expect(data).to.eql({ + ...getConfigurationOutput(true), + connector: { + id: connector.id, + name: connector.name, + type: connector.connector_type_id as ConnectorTypes, + fields: null, + }, + mappings: [ + { + action_type: 'overwrite', + source: 'title', + target: 'short_description', + }, + { + action_type: 'overwrite', + source: 'description', + target: 'description', + }, + { + action_type: 'append', + source: 'comments', + target: 'work_notes', + }, + ], + }); + }); + it('should not patch a configuration with unsupported connector type', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); - - await supertest - .patch(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - // @ts-ignore We need it to test unsupported types - .send(getConfiguration({ type: '.unsupported' })) - .expect(400); + await createConfiguration(supertest); + await updateConfiguration( + supertest, + // @ts-expect-error + getConfigurationRequest({ type: '.unsupported' }), + 400 + ); }); it('should not patch a configuration with unsupported connector fields', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); - - await supertest - .patch(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - // @ts-ignore We need it to test unsupported fields - .send(getConfiguration({ type: '.jira', fields: { unsupported: 'value' } })) - .expect(400); + await createConfiguration(supertest); + await updateConfiguration( + supertest, + // @ts-expect-error + getConfigurationRequest({ type: '.jira', fields: { unsupported: 'value' } }), + 400 + ); }); it('should handle patch request when there is no configuration', async () => { - const { body } = await supertest - .patch(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send({ closure_type: 'close-by-pushing', version: 'no-version' }) - .expect(409); + const error = await updateConfiguration( + supertest, + { closure_type: 'close-by-pushing', version: 'no-version' }, + 409 + ); - expect(body).to.eql({ + expect(error).to.eql({ error: 'Conflict', message: 'You can not patch this configuration since you did not created first with a post.', @@ -89,19 +141,14 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should handle patch request when versions are different', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); - - const { body } = await supertest - .patch(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send({ closure_type: 'close-by-pushing', version: 'no-version' }) - .expect(409); - - expect(body).to.eql({ + await createConfiguration(supertest); + const error = await updateConfiguration( + supertest, + { closure_type: 'close-by-pushing', version: 'no-version' }, + 409 + ); + + expect(error).to.eql({ error: 'Conflict', message: 'This configuration has been updated. Please refresh before saving additional updates.', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts index 3672cd3a755f7..c74e048edcfa0 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/post_configure.ts @@ -6,14 +6,16 @@ */ import expect from '@kbn/expect'; +import { ConnectorTypes } from '../../../../../../plugins/cases/common/api'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASE_CONFIGURE_URL } from '../../../../../../plugins/cases/common/constants'; import { - getConfiguration, + getConfigurationRequest, removeServerGeneratedPropertiesFromSavedObject, getConfigurationOutput, deleteConfiguration, + createConfiguration, + getConfiguration, } from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export @@ -27,55 +29,130 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should create a configuration', async () => { - const { body } = await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); + const configuration = await createConfiguration(supertest); - const data = removeServerGeneratedPropertiesFromSavedObject(body); + const data = removeServerGeneratedPropertiesFromSavedObject(configuration); expect(data).to.eql(getConfigurationOutput()); }); it('should keep only the latest configuration', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration({ id: 'connector-2' })) - .expect(200); - - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send(getConfiguration()) - .expect(200); - - const { body } = await supertest - .get(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - const data = removeServerGeneratedPropertiesFromSavedObject(body); + await createConfiguration(supertest, getConfigurationRequest({ id: 'connector-2' })); + await createConfiguration(supertest); + const configuration = await getConfiguration(supertest); + + const data = removeServerGeneratedPropertiesFromSavedObject(configuration); expect(data).to.eql(getConfigurationOutput()); }); + it('should not create a configuration when missing connector.id', async () => { + await createConfiguration( + supertest, + { + // @ts-expect-error + connector: { + name: 'Connector', + type: ConnectorTypes.none, + fields: null, + }, + closure_type: 'close-by-user', + }, + 400 + ); + }); + + it('should not create a configuration when missing connector.name', async () => { + await createConfiguration( + supertest, + { + // @ts-expect-error + connector: { + id: 'test-id', + type: ConnectorTypes.none, + fields: null, + }, + closure_type: 'close-by-user', + }, + 400 + ); + }); + + it('should not create a configuration when missing connector.type', async () => { + await createConfiguration( + supertest, + { + // @ts-expect-error + connector: { + id: 'test-id', + name: 'Connector', + fields: null, + }, + closure_type: 'close-by-user', + }, + 400 + ); + }); + + it('should not create a configuration when missing connector.fields', async () => { + await createConfiguration( + supertest, + { + // @ts-expect-error + connector: { + id: 'test-id', + type: ConnectorTypes.none, + name: 'Connector', + }, + closure_type: 'close-by-user', + }, + 400 + ); + }); + + it('should not create a configuration when when missing closure_type', async () => { + await createConfiguration( + supertest, + // @ts-expect-error + { + connector: { + id: 'test-id', + type: ConnectorTypes.none, + name: 'Connector', + fields: null, + }, + }, + 400 + ); + }); + + it('should not create a configuration when when fields are not null', async () => { + await createConfiguration( + supertest, + { + connector: { + id: 'test-id', + type: ConnectorTypes.none, + name: 'Connector', + // @ts-expect-error + fields: {}, + }, + closure_type: 'close-by-user', + }, + 400 + ); + }); + it('should not create a configuration with unsupported connector type', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - // @ts-ignore We need it to test unsupported types - .send(getConfiguration({ type: '.unsupported' })) - .expect(400); + // @ts-expect-error + await createConfiguration(supertest, getConfigurationRequest({ type: '.unsupported' }), 400); }); it('should not create a configuration with unsupported connector fields', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - // @ts-ignore We need it to test unsupported types - .send(getConfiguration({ type: '.jira', fields: { unsupported: 'value' } })) - .expect(400); + await createConfiguration( + supertest, + // @ts-expect-error + getConfigurationRequest({ type: '.jira', fields: { unsupported: 'value' } }), + 400 + ); }); }); }; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts index 767233f756829..9be413015c051 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/connectors/case.ts @@ -37,7 +37,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return 400 when creating a case action', async () => { await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext): void => { // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests it.skip('should return 200 when creating a case action successfully', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -70,7 +70,7 @@ export default ({ getService }: FtrProviderContext): void => { }); const { body: fetchedAction } = await supertest - .get(`/api/actions/action/${createdActionId}`) + .get(`/api/actions/connector/${createdActionId}`) .expect(200); expect(fetchedAction).to.eql({ @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext): void => { describe.skip('create', () => { it('should respond with a 400 Bad Request when creating a case without title', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -114,7 +114,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -130,7 +130,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when creating a case without description', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -159,7 +159,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -175,7 +175,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when creating a case without tags', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -204,7 +204,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -220,7 +220,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when creating a case without connector', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -240,7 +240,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -256,7 +256,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when creating jira without issueType', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -285,7 +285,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -301,7 +301,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when creating a connector with wrong fields', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -331,7 +331,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -347,7 +347,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when creating a none without fields as null', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -373,7 +373,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -389,7 +389,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a case', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -422,7 +422,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -447,7 +447,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a case with connector with field as null if not provided', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -476,7 +476,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -515,7 +515,7 @@ export default ({ getService }: FtrProviderContext): void => { describe.skip('update', () => { it('should respond with a 400 Bad Request when updating a case without id', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -534,7 +534,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -550,7 +550,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when updating a case without version', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -569,7 +569,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -585,7 +585,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should update a case', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -612,7 +612,7 @@ export default ({ getService }: FtrProviderContext): void => { }; await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -639,7 +639,7 @@ export default ({ getService }: FtrProviderContext): void => { describe.skip('addComment', () => { it('should respond with a 400 Bad Request when adding a comment to a case without caseId', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -673,7 +673,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when missing attributes of type user', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -691,7 +691,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -727,7 +727,7 @@ export default ({ getService }: FtrProviderContext): void => { const alert = signals.hits.hits[0]; const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -758,7 +758,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -789,7 +789,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when missing attributes of type alert', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -817,7 +817,7 @@ export default ({ getService }: FtrProviderContext): void => { for (const attribute of ['alertId']) { const requestAttributes = omit(attribute, comment); const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { @@ -838,7 +838,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when adding excess attributes for type user', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -858,7 +858,7 @@ export default ({ getService }: FtrProviderContext): void => { for (const attribute of ['blah', 'bogus']) { const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { @@ -881,7 +881,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when adding excess attributes for type alert', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -906,7 +906,7 @@ export default ({ getService }: FtrProviderContext): void => { for (const attribute of ['comment']) { const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { @@ -930,7 +930,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should respond with a 400 Bad Request when adding a comment to a case without type', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -949,7 +949,7 @@ export default ({ getService }: FtrProviderContext): void => { }; const caseConnector = await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -965,7 +965,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should add a comment of type user', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -991,7 +991,7 @@ export default ({ getService }: FtrProviderContext): void => { }; await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); @@ -1018,7 +1018,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should add a comment of type alert', async () => { const { body: createdAction } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name: 'A case connector', @@ -1049,7 +1049,7 @@ export default ({ getService }: FtrProviderContext): void => { }; await supertest - .post(`/api/actions/action/${createdActionId}/_execute`) + .post(`/api/actions/connector/${createdActionId}/_execute`) .set('kbn-xsrf', 'foo') .send({ params }) .expect(200); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index f759579510d49..b1d4fc87e4b43 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -21,7 +21,7 @@ import { deleteCasesUserActions, deleteComments, deleteConfiguration, - getConfiguration, + getConfigurationRequest, getServiceNowConnector, } from '../../../../common/lib/utils'; import { @@ -56,7 +56,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should push a case', async () => { const { body: connector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -69,10 +69,10 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send( - getConfiguration({ + getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, }) ) .expect(200); @@ -82,10 +82,10 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, fields: { urgency: '2', impact: '2', @@ -124,7 +124,7 @@ export default ({ getService }: FtrProviderContext): void => { it('pushes a comment appropriately', async () => { const { body: connector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -138,10 +138,10 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send( - getConfiguration({ + getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, }) ) .expect(200); @@ -151,10 +151,10 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, fields: { urgency: '2', impact: '2', @@ -183,7 +183,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should pushes a case and closes when closure_type: close-by-pushing', async () => { const { body: connector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -196,10 +196,10 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send({ - ...getConfiguration({ + ...getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, }), closure_type: 'close-by-pushing', }) @@ -210,10 +210,10 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, fields: { urgency: '2', impact: '2', @@ -237,7 +237,7 @@ export default ({ getService }: FtrProviderContext): void => { // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests it.skip('should push a collection case but not close it when closure_type: close-by-pushing', async () => { const { body: connector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -250,10 +250,10 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send({ - ...getConfiguration({ + ...getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, }), closure_type: 'close-by-pushing', }) @@ -264,10 +264,10 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCollectionReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, fields: { urgency: '2', impact: '2', @@ -302,7 +302,7 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration().connector, + connector: getConfigurationRequest().connector, }) .expect(200); @@ -315,7 +315,7 @@ export default ({ getService }: FtrProviderContext): void => { it('unhappy path = 409s when case is closed', async () => { const { body: connector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -329,10 +329,10 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send( - getConfiguration({ + getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, }) ) .expect(200); @@ -342,10 +342,10 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, fields: { urgency: '2', impact: '2', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 0b66200a3fab0..5ab119beeb77b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -15,7 +15,7 @@ import { deleteCasesUserActions, deleteComments, deleteConfiguration, - getConfiguration, + getConfigurationRequest, getServiceNowConnector, } from '../../../../../common/lib/utils'; @@ -49,7 +49,7 @@ export default ({ getService }: FtrProviderContext): void => { it(`on new push to service, user action: 'push-to-service' should be called with actionFields: ['pushed']`, async () => { const { body: connector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ ...getServiceNowConnector(), @@ -63,10 +63,10 @@ export default ({ getService }: FtrProviderContext): void => { .post(CASE_CONFIGURE_URL) .set('kbn-xsrf', 'true') .send( - getConfiguration({ + getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, }) ) .expect(200); @@ -76,10 +76,10 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .send({ ...postCaseReq, - connector: getConfiguration({ + connector: getConfigurationRequest({ id: connector.id, name: connector.name, - type: connector.actionTypeId, + type: connector.connector_type_id, fields: { urgency: '2', impact: '2', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts index 0b6c755c79b50..75d1378260b19 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts @@ -28,13 +28,13 @@ export default ({ getService }: FtrProviderContext): void => { it('should return the correct connectors', async () => { const { body: snConnector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send(getServiceNowConnector()) .expect(200); const { body: emailConnector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send({ name: 'An email action', @@ -51,13 +51,13 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const { body: jiraConnector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send(getJiraConnector()) .expect(200); const { body: resilientConnector } = await supertest - .post('/api/actions/action') + .post('/api/actions/connector') .set('kbn-xsrf', 'true') .send(getResilientConnector()) .expect(200); From d284f3680b69dfd926645098bec3386fe650ca19 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 12:22:39 +0300 Subject: [PATCH 13/25] Migrate statuses --- .../api/cases/status/get_status.test.ts | 92 ------------------- .../case_api_integration/common/lib/utils.ts | 15 +++ .../tests/common/cases/status/get_status.ts | 76 ++++++--------- 3 files changed, 45 insertions(+), 138 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts b/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts deleted file mode 100644 index ca12ed9c92831..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/status/get_status.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, -} from '../../__fixtures__'; -import { initGetCasesStatusApi } from './get_status'; -import { CASE_STATUS_URL } from '../../../../../common/constants'; -import { esKuery } from 'src/plugins/data/server'; -import { CaseType } from '../../../../../common/api'; - -describe('GET status', () => { - let routeHandler: RequestHandler; - const findArgs = { - fields: [], - page: 1, - perPage: 1, - type: 'cases', - sortField: 'created_at', - }; - - beforeAll(async () => { - routeHandler = await createRoute(initGetCasesStatusApi, 'get'); - }); - - it(`returns the status`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_STATUS_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(context.core.savedObjects.client.find).toHaveBeenNthCalledWith(1, { - ...findArgs, - filter: esKuery.fromKueryExpression( - `((cases.attributes.status: open AND cases.attributes.type: individual) OR cases.attributes.type: ${CaseType.collection})` - ), - }); - - expect(context.core.savedObjects.client.find).toHaveBeenNthCalledWith(2, { - ...findArgs, - filter: esKuery.fromKueryExpression( - `((cases.attributes.status: in-progress AND cases.attributes.type: individual) OR cases.attributes.type: ${CaseType.collection})` - ), - }); - - expect(context.core.savedObjects.client.find).toHaveBeenNthCalledWith(3, { - ...findArgs, - filter: esKuery.fromKueryExpression( - `((cases.attributes.status: closed AND cases.attributes.type: individual) OR cases.attributes.type: ${CaseType.collection})` - ), - }); - - expect(response.payload).toEqual({ - count_open_cases: 4, - count_in_progress_cases: 4, - count_closed_cases: 4, - }); - }); - - it(`returns an error when findCases throws`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_STATUS_URL, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [{ ...mockCases[0], id: 'throw-error-find' }], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 8f829b333bb0e..52ad94915e5cf 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -16,6 +16,7 @@ import { CASES_URL, CASE_CONFIGURE_CONNECTORS_URL, CASE_CONFIGURE_URL, + CASE_STATUS_URL, SUB_CASES_PATCH_DEL_URL, } from '../../../../plugins/cases/common/constants'; import { @@ -38,6 +39,7 @@ import { AllCommentsResponse, CommentPatchRequest, CasesConfigurePatch, + CasesStatusResponse, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -769,3 +771,16 @@ export const updateConfiguration = async ( return configuration; }; + +export const getAllCasesStatuses = async ( + supertest: st.SuperTest, + expectedHttpCode: number = 200 +): Promise => { + const { body: statuses } = await supertest + .get(CASE_STATUS_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return statuses; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts index 71602f993a1d4..2a5ba1c797672 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts @@ -8,9 +8,14 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../../common/ftr_provider_context'; -import { CASES_URL, CASE_STATUS_URL } from '../../../../../../../plugins/cases/common/constants'; +import { CaseStatuses } from '../../../../../../../plugins/cases/common/api'; import { postCaseReq } from '../../../../../common/lib/mock'; -import { deleteCases } from '../../../../../common/lib/utils'; +import { + deleteCases, + createCase, + updateCase, + getAllCasesStatuses, +} from '../../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -23,54 +28,33 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should return case statuses', async () => { - await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(postCaseReq); + await createCase(supertest, postCaseReq); + const inProgressCase = await createCase(supertest, postCaseReq); + const postedCase = await createCase(supertest, postCaseReq); - const { body: inProgressCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: 'closed', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }); - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: inProgressCase.id, - version: inProgressCase.version, - status: 'in-progress', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: inProgressCase.id, + version: inProgressCase.version, + status: CaseStatuses['in-progress'], + }, + ], + }); - const { body } = await supertest - .get(CASE_STATUS_URL) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const statuses = await getAllCasesStatuses(supertest); - expect(body).to.eql({ + expect(statuses).to.eql({ count_open_cases: 1, count_closed_cases: 1, count_in_progress_cases: 1, From ab69965f19307d4af194656b0b749263f67f8ad7 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 14:40:56 +0300 Subject: [PATCH 14/25] Migrate get case --- .../server/routes/api/cases/get_case.test.ts | 222 ------------------ .../case_api_integration/common/lib/utils.ts | 15 ++ .../tests/common/cases/get_case.ts | 43 ++-- 3 files changed, 41 insertions(+), 239 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts deleted file mode 100644 index b9312331b4df2..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/get_case.test.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler, SavedObject } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { ConnectorTypes, ESCaseAttributes } from '../../../../common/api'; -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, - mockCasesErrorTriggerData, - mockCaseComments, - mockCaseNoConnectorId, - mockCaseConfigure, -} from '../__fixtures__'; -import { flattenCaseSavedObject } from '../utils'; -import { initGetCaseApi } from './get_case'; -import { CASE_DETAILS_URL } from '../../../../common/constants'; - -describe('GET case', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initGetCaseApi, 'get'); - }); - it(`returns the case with empty case comments when includeComments is false`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-id-1', - }, - query: { - includeComments: false, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - const savedObject = (mockCases.find( - (s) => s.id === 'mock-id-1' - ) as unknown) as SavedObject; - expect(response.status).toEqual(200); - expect(response.payload).toEqual( - flattenCaseSavedObject({ - savedObject, - }) - ); - expect(response.payload.comments).toEqual([]); - }); - - it(`returns an error when thrown from getCase`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'abcdefg', - }, - query: { - includeComments: false, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - - expect(response.status).toEqual(404); - expect(response.payload.isBoom).toEqual(true); - }); - - it(`returns the case with case comments when includeComments is true`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-id-1', - }, - query: { - includeComments: true, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - - expect(response.status).toEqual(200); - expect(response.payload.comments).toHaveLength(6); - }); - - it(`returns an error when thrown from getAllCaseComments`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'bad-guy', - }, - query: { - includeComments: true, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCasesErrorTriggerData, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - - expect(response.status).toEqual(400); - }); - - it(`case w/o connector.id - returns the case with connector id when 3rd party unconfigured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-no-connector_id', - }, - query: { - includeComments: false, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - - expect(response.status).toEqual(200); - expect(response.payload.connector).toEqual({ - fields: null, - id: 'none', - name: 'none', - type: ConnectorTypes.none, - }); - }); - - it(`case w/o connector.id - returns the case with connector id when 3rd party configured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-no-connector_id', - }, - query: { - includeComments: false, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - caseConfigureSavedObject: mockCaseConfigure, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - - expect(response.status).toEqual(200); - expect(response.payload.connector).toEqual({ - fields: null, - id: 'none', - name: 'none', - type: '.none', - }); - }); - - it(`case w/ connector.id - returns the case with connector id when case already has connectorId`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASE_DETAILS_URL, - method: 'get', - params: { - case_id: 'mock-id-3', - }, - query: { - includeComments: false, - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseConfigureSavedObject: mockCaseConfigure, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - - expect(response.status).toEqual(200); - expect(response.payload.connector).toEqual({ - fields: { issueType: 'Task', priority: 'High', parent: null }, - id: '123', - name: 'My connector', - type: '.jira', - }); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 52ad94915e5cf..29a0738c00b88 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -784,3 +784,18 @@ export const getAllCasesStatuses = async ( return statuses; }; + +export const getCase = async ( + supertest: st.SuperTest, + caseId: string, + includeComments: boolean = false, + expectedHttpCode: number = 200 +): Promise => { + const { body: theCase } = await supertest + .get(`${CASES_URL}/${caseId}?includeComments=${includeComments}`) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return theCase; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts index e8bed3c5a3116..bdc838abb3a6e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts @@ -8,13 +8,17 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { AttributesTypeUser } from '../../../../../../plugins/cases/common/api'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; +import { postCaseReq, postCaseResp, postCommentUserReq } from '../../../../common/lib/mock'; import { - postCaseReq, - postCaseResp, + deleteCases, + createCase, + getCase, + createComment, removeServerGeneratedPropertiesFromCase, -} from '../../../../common/lib/mock'; -import { deleteCases } from '../../../../common/lib/utils'; + removeServerGeneratedPropertiesFromSavedObject, +} from '../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -26,21 +30,26 @@ export default ({ getService }: FtrProviderContext): void => { await deleteCases(es); }); - it('should return a case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body } = await supertest - .get(`${CASES_URL}/${postedCase.id}`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + it('should return a case with no comments', async () => { + const postedCase = await createCase(supertest, postCaseReq); + const theCase = await getCase(supertest, postedCase.id, true); - const data = removeServerGeneratedPropertiesFromCase(body); + const data = removeServerGeneratedPropertiesFromCase(theCase); expect(data).to.eql(postCaseResp(postedCase.id)); + expect(data.comments?.length).to.eql(0); + }); + + it('should return a case with comments', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await createComment(supertest, postedCase.id, postCommentUserReq); + const theCase = await getCase(supertest, postedCase.id); + + const comment = removeServerGeneratedPropertiesFromSavedObject( + theCase.comments![0] as AttributesTypeUser + ); + + expect(theCase.comments?.length).to.eql(1); + expect(theCase.comments![0]).to.eql(comment); }); it('should return a 400 when passing the includeSubCaseComments', async () => { From 80f9a969205f8120291361dbcd3f37d4e1b5f585 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 13 Apr 2021 09:05:04 -0400 Subject: [PATCH 15/25] Migrating delete unit tests --- .../routes/api/cases/delete_cases.test.ts | 114 ------------------ .../case_api_integration/common/lib/utils.ts | 21 ++++ .../tests/common/cases/delete_cases.ts | 63 +++------- 3 files changed, 38 insertions(+), 160 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts deleted file mode 100644 index a441a027769bf..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, - mockCasesErrorTriggerData, - mockCaseComments, -} from '../__fixtures__'; -import { initDeleteCasesApi } from './delete_cases'; -import { CASES_URL } from '../../../../common/constants'; - -describe('DELETE case', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initDeleteCasesApi, 'delete'); - }); - it(`deletes the case. responds with 204`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'delete', - query: { - ids: ['mock-id-1'], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(204); - }); - it(`returns an error when thrown from deleteCase service`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'delete', - query: { - ids: ['not-real'], - }, - }); - - const mockSO = createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }); - // Adding this because the delete API needs to get all the cases first to determine if they are removable or not - // so it makes a call to bulkGet first - mockSO.bulkGet.mockImplementation(async () => ({ saved_objects: [] })); - - const { context } = await createRouteContext(mockSO); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - }); - it(`returns an error when thrown from getAllCaseComments service`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'delete', - query: { - ids: ['bad-guy'], - }, - }); - - const mockSO = createMockSavedObjectsRepository({ - caseSavedObject: mockCasesErrorTriggerData, - caseCommentSavedObject: mockCaseComments, - }); - - // Adding this because the delete API needs to get all the cases first to determine if they are removable or not - // so it makes a call to bulkGet first - mockSO.bulkGet.mockImplementation(async () => ({ saved_objects: [] })); - - const { context } = await createRouteContext(mockSO); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - }); - it(`returns an error when thrown from deleteComment service`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'delete', - query: { - ids: ['valid-id'], - }, - }); - - const mockSO = createMockSavedObjectsRepository({ - caseSavedObject: mockCasesErrorTriggerData, - caseCommentSavedObject: mockCasesErrorTriggerData, - }); - - // Adding this because the delete API needs to get all the cases first to determine if they are removable or not - // so it makes a call to bulkGet first - mockSO.bulkGet.mockImplementation(async () => ({ saved_objects: [] })); - - const { context } = await createRouteContext(mockSO); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index bb104d1bd14ed..4b0cee2da180e 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -560,6 +560,27 @@ export const createCase = async ( return theCase; }; +/** + * Sends a delete request for the specified case IDs. + */ +export const deleteCase = async ({ + supertest, + caseIDs, + expectedHttpCode = 204, +}: { + supertest: st.SuperTest; + caseIDs: string[]; + expectedHttpCode?: number; +}) => { + const { body } = await supertest + .delete(`${CASES_URL}?ids=${JSON.stringify(caseIDs)}`) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return body; +}; + export const createComment = async ( supertest: st.SuperTest, caseId: string, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index e835e4da6c8dd..7b8436529c690 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; -import { postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; +import { getPostCaseRequest, postCommentUserReq } from '../../../../common/lib/mock'; import { createCaseAction, createSubCase, @@ -18,6 +18,10 @@ import { deleteCases, deleteCasesUserActions, deleteComments, + createCase, + deleteCase, + createComment, + getComment, } from '../../../../common/lib/utils'; import { getSubCaseDetailsUrl } from '../../../../../../plugins/cases/common/api/helpers'; import { CaseResponse } from '../../../../../../plugins/cases/common/api'; @@ -35,59 +39,26 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should delete a case', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body } = await supertest - .delete(`${CASES_URL}?ids=["${postedCase.id}"]`) - .set('kbn-xsrf', 'true') - .send() - .expect(204); + const postedCase = await createCase(supertest, getPostCaseRequest()); + const body = await deleteCase({ supertest, caseIDs: [postedCase.id] }); expect(body).to.eql({}); }); it(`should delete a case's comments when that case gets deleted`, async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - await supertest - .get(`${CASES_URL}/${postedCase.id}/comments/${patchedCase.comments[0].id}`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - await supertest - .delete(`${CASES_URL}?ids=["${postedCase.id}"]`) - .set('kbn-xsrf', 'true') - .send() - .expect(204); - - await supertest - .get(`${CASES_URL}/${postedCase.id}/comments/${patchedCase.comments[0].id}`) - .set('kbn-xsrf', 'true') - .send() - .expect(404); + const postedCase = await createCase(supertest, getPostCaseRequest()); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); + + // ensure that we can get the comment before deleting the case + await getComment(supertest, postedCase.id, patchedCase.comments![0].id); + await deleteCase({ supertest, caseIDs: [postedCase.id] }); + + // make sure the comment is now gone + await getComment(supertest, postedCase.id, patchedCase.comments![0].id, 404); }); it('unhappy path - 404s when case is not there', async () => { - await supertest - .delete(`${CASES_URL}?ids=["fake-id"]`) - .set('kbn-xsrf', 'true') - .send() - .expect(404); + await deleteCase({ supertest, caseIDs: ['fake-id'], expectedHttpCode: 404 }); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests From a105b5931626804b01f51f9b49fead6ad88c3575 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 16:41:40 +0300 Subject: [PATCH 16/25] Migrate find case --- .../routes/api/cases/find_cases.test.ts | 99 ---- .../case_api_integration/common/lib/utils.ts | 15 + .../tests/common/cases/find_cases.ts | 539 +++++++----------- 3 files changed, 211 insertions(+), 442 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts deleted file mode 100644 index ca9f731ca5010..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, -} from '../__fixtures__'; -import { initFindCasesApi } from './find_cases'; -import { CASES_URL } from '../../../../common/constants'; -import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects'; - -describe('FIND all cases', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initFindCasesApi, 'get'); - }); - - it(`gets all the cases`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases).toHaveLength(4); - // mockSavedObjectsRepository do not support filters and returns all cases every time. - expect(response.payload.count_open_cases).toEqual(4); - expect(response.payload.count_closed_cases).toEqual(4); - expect(response.payload.count_in_progress_cases).toEqual(4); - }); - - it(`has proper connector id on cases with configured connector`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases[2].connector.id).toEqual('123'); - }); - - it(`adds 'none' connector id to cases without when 3rd party unconfigured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases[0].connector.id).toEqual('none'); - }); - - it(`adds default connector id to cases without when 3rd party configured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - caseConfigureSavedObject: mockCaseConfigure, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases[0].connector.id).toEqual('none'); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 29a0738c00b88..a8e9a0934b499 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -799,3 +799,18 @@ export const getCase = async ( return theCase; }; + +export const findCases = async ( + supertest: st.SuperTest, + query: Record = {}, + expectedHttpCode: number = 200 +): Promise => { + const { body: res } = await supertest + .get(`${CASES_URL}/_find`) + .query({ sortOrder: 'asc', ...query }) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return res; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts index 195ada335e086..8ceb9688c62ff 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts @@ -25,8 +25,13 @@ import { createCaseAsUser, ensureSavedObjectIsAuthorized, findCasesAsUser, + findCases, + createCase, + updateCase, + createComment, } from '../../../../common/lib/utils'; import { + CaseResponse, CasesFindResponse, CaseStatuses, CaseType, @@ -62,41 +67,18 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should return empty response', async () => { - const { body } = await supertest - .get(`${CASES_URL}/_find`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(body).to.eql(findCasesResp); + const cases = await findCases(supertest); + expect(cases).to.eql(findCasesResp); }); it('should return cases', async () => { - const { body: a } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const a = await createCase(supertest, postCaseReq); + const b = await createCase(supertest, postCaseReq); + const c = await createCase(supertest, postCaseReq); - const { body: b } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body: c } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const cases = await findCases(supertest); - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 3, cases: [a, b, c], @@ -105,20 +87,11 @@ export default ({ getService }: FtrProviderContext): void => { }); it('filters by tags', async () => { - await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(postCaseReq); - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, tags: ['unique'] }) - .expect(200); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&tags=unique`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + await createCase(supertest, postCaseReq); + const postedCase = await createCase(supertest, { ...postCaseReq, tags: ['unique'] }); + const cases = await findCases(supertest, { tags: ['unique'] }); - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [postedCase], @@ -127,37 +100,21 @@ export default ({ getService }: FtrProviderContext): void => { }); it('filters by status', async () => { - const { body: openCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body: toCloseCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: toCloseCase.id, - version: toCloseCase.version, - status: 'closed', - }, - ], - }) - .expect(200); + const openCase = await createCase(supertest, postCaseReq); + const toCloseCase = await createCase(supertest, postCaseReq); + await updateCase(supertest, { + cases: [ + { + id: toCloseCase.id, + version: toCloseCase.version, + status: CaseStatuses.closed, + }, + ], + }); - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=open`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.closed }); - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [openCase], @@ -168,18 +125,10 @@ export default ({ getService }: FtrProviderContext): void => { }); it('filters by reporters', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&reporters=elastic`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const cases = await findCases(supertest, { reporters: 'elastic' }); - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [postedCase], @@ -188,32 +137,14 @@ export default ({ getService }: FtrProviderContext): void => { }); it('correctly counts comments', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); // post 2 comments - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + await createComment(supertest, postedCase.id, postCommentUserReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - expect(body).to.eql({ + const cases = await findCases(supertest); + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [ @@ -228,64 +159,38 @@ export default ({ getService }: FtrProviderContext): void => { }); it('correctly counts open/closed/in-progress', async () => { - await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(postCaseReq); + await createCase(supertest, postCaseReq); + const inProgressCase = await createCase(supertest, postCaseReq); + const postedCase = await createCase(supertest, postCaseReq); - const { body: inProgreeCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: 'closed', - }, - ], - }) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: inProgreeCase.id, - version: inProgreeCase.version, - status: 'in-progress', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }); - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: inProgressCase.id, + version: inProgressCase.version, + status: CaseStatuses['in-progress'], + }, + ], + }); - expect(body.count_open_cases).to.eql(1); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + const cases = await findCases(supertest); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('unhappy path - 400s when bad query supplied', async () => { - await supertest - .get(`${CASES_URL}/_find?perPage=true`) - .set('kbn-xsrf', 'true') - .send() - .expect(400); + await findCases(supertest, { perPage: true }, 400); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests @@ -334,75 +239,66 @@ export default ({ getService }: FtrProviderContext): void => { }); }); it('correctly counts stats without using a filter', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .expect(200); - - expect(body.total).to.eql(3); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + const cases = await findCases(supertest); + + expect(cases.total).to.eql(3); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('correctly counts stats with a filter for open cases', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=open`) - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.open }); - expect(body.cases.length).to.eql(1); + expect(cases.cases.length).to.eql(1); // since we're filtering on status and the collection only has an in-progress case, it should only return the // individual case that has the open status and no collections // ENABLE_CASE_CONNECTOR: this value is not correct because it includes a collection // that does not have an open case. This is a known issue and will need to be resolved // when this issue is addressed: https://github.com/elastic/kibana/issues/94115 - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('correctly counts stats with a filter for individual cases', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&type=${CaseType.individual}`) - .expect(200); - - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { type: CaseType.individual }); + + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats with a filter for collection cases with multiple sub cases', async () => { // this will force the first sub case attached to the collection to be closed // so we'll have one closed sub case and one open sub case await createSubCase({ supertest, caseID: collection.newSubCaseInfo.id, actionID }); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&type=${CaseType.collection}`) - .expect(200); - - expect(body.total).to.eql(1); - expect(body.cases[0].subCases?.length).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { type: CaseType.collection }); + + expect(cases.total).to.eql(1); + expect(cases.cases[0].subCases?.length).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats with a filter for collection and open cases with multiple sub cases', async () => { // this will force the first sub case attached to the collection to be closed // so we'll have one closed sub case and one open sub case await createSubCase({ supertest, caseID: collection.newSubCaseInfo.id, actionID }); - const { body }: { body: CasesFindResponse } = await supertest - .get( - `${CASES_URL}/_find?sortOrder=asc&type=${CaseType.collection}&status=${CaseStatuses.open}` - ) - .expect(200); + const cases = await findCases(supertest, { + type: CaseType.collection, + status: CaseStatuses.open, + }); - expect(body.total).to.eql(1); - expect(body.cases[0].subCases?.length).to.eql(1); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(1); + expect(cases.cases[0].subCases?.length).to.eql(1); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats including a collection without sub cases when not filtering on status', async () => { @@ -415,15 +311,13 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(204); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .expect(200); + const cases = await findCases(supertest, { type: CaseType.collection }); // it should include the collection without sub cases because we did not pass in a filter on status - expect(body.total).to.eql(3); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(3); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats including a collection without sub cases when filtering on tags', async () => { @@ -436,31 +330,27 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(204); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&tags=defacement`) - .expect(200); + const cases = await findCases(supertest, { tags: ['defacement'] }); // it should include the collection without sub cases because we did not pass in a filter on status - expect(body.total).to.eql(3); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(3); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('does not return collections without sub cases matching the requested status', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=closed`) - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.closed }); - expect(body.cases.length).to.eql(1); + expect(cases.cases.length).to.eql(1); // it should not include the collection that has a sub case as in-progress // ENABLE_CASE_CONNECTOR: this value is not correct because it includes collections. This short term // fix for when sub cases are not enabled. When the feature is completed the _find API // will need to be fixed as explained in this ticket: https://github.com/elastic/kibana/issues/94115 - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('does not return empty collections when filtering on status', async () => { @@ -473,19 +363,17 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(204); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=closed`) - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.closed }); - expect(body.cases.length).to.eql(1); + expect(cases.cases.length).to.eql(1); // ENABLE_CASE_CONNECTOR: this value is not correct because it includes collections. This short term // fix for when sub cases are not enabled. When the feature is completed the _find API // will need to be fixed as explained in this ticket: https://github.com/elastic/kibana/issues/94115 - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); }); }); @@ -500,22 +388,17 @@ export default ({ getService }: FtrProviderContext): void => { await deleteAllCaseItems(es); }); - const createCasesWithTitleAsNumber = async (total: number) => { - const responsePromises: supertestAsPromised.Test[] = []; + const createCasesWithTitleAsNumber = async (total: number): Promise => { + const responsePromises = []; for (let i = 0; i < total; i++) { // this doesn't guarantee that the cases will be created in order that the for-loop executes, // for example case with title '2', could be created before the case with title '1' since we're doing a promise all here // A promise all is just much faster than doing it one by one which would have guaranteed that the cases are // created in the order that the for-loop executes - responsePromises.push( - supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, title: `${i}` }) - ); + responsePromises.push(createCase(supertest, { ...postCaseReq, title: `${i}` })); } const responses = await Promise.all(responsePromises); - return responses.map((response) => response.body); + return responses; }; /** @@ -541,88 +424,68 @@ export default ({ getService }: FtrProviderContext): void => { }; it('returns the correct total when perPage is less than the total', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 1, - perPage: 5, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.cases.length).to.eql(5); - expect(body.total).to.eql(10); - expect(body.page).to.eql(1); - expect(body.per_page).to.eql(5); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + page: 1, + perPage: 5, + }); + + expect(cases.cases.length).to.eql(5); + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(1); + expect(cases.per_page).to.eql(5); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); it('returns the correct total when perPage is greater than the total', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 1, - perPage: 11, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.total).to.eql(10); - expect(body.page).to.eql(1); - expect(body.per_page).to.eql(11); - expect(body.cases.length).to.eql(10); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + page: 1, + perPage: 11, + }); + + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(1); + expect(cases.per_page).to.eql(11); + expect(cases.cases.length).to.eql(10); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); it('returns the correct total when perPage is equal to the total', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 1, - perPage: 10, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.total).to.eql(10); - expect(body.page).to.eql(1); - expect(body.per_page).to.eql(10); - expect(body.cases.length).to.eql(10); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + page: 1, + perPage: 10, + }); + + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(1); + expect(cases.per_page).to.eql(10); + expect(cases.cases.length).to.eql(10); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); it('returns the second page of results', async () => { const perPage = 5; - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 2, - perPage, - }) - .set('kbn-xsrf', 'true') - .expect(200); + const cases = await findCases(supertest, { + page: 2, + perPage, + }); - expect(body.total).to.eql(10); - expect(body.page).to.eql(2); - expect(body.per_page).to.eql(5); - expect(body.cases.length).to.eql(5); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(2); + expect(cases.per_page).to.eql(5); + expect(cases.cases.length).to.eql(5); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); const allCases = await getAllCasesSortedByCreatedAtAsc(); - body.cases.map((caseInfo, index) => { + cases.cases.map((caseInfo, index) => { // we started on the second page of 10 cases with a perPage of 5, so the first case should 0 + 5 (index + perPage) expect(caseInfo.title).to.eql(allCases[index + perPage]?.cases.title); }); @@ -635,27 +498,22 @@ export default ({ getService }: FtrProviderContext): void => { // it's less than or equal here because the page starts at 1, so page 5 is a valid page number // and should have case titles 9, and 10 for (let currentPage = 1; currentPage <= total / perPage; currentPage++) { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: currentPage, - perPage, - }) - .set('kbn-xsrf', 'true') - .expect(200); + const cases = await findCases(supertest, { + page: currentPage, + perPage, + }); - expect(body.total).to.eql(total); - expect(body.page).to.eql(currentPage); - expect(body.per_page).to.eql(perPage); - expect(body.cases.length).to.eql(perPage); - expect(body.count_open_cases).to.eql(total); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(total); + expect(cases.page).to.eql(currentPage); + expect(cases.per_page).to.eql(perPage); + expect(cases.cases.length).to.eql(perPage); + expect(cases.count_open_cases).to.eql(total); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); const allCases = await getAllCasesSortedByCreatedAtAsc(); - body.cases.map((caseInfo, index) => { + cases.cases.map((caseInfo, index) => { // for page 1, the cases tiles should be 0,1,2 for page 2: 3,4,5 etc (assuming the titles were sorted // correctly) expect(caseInfo.title).to.eql( @@ -666,24 +524,19 @@ export default ({ getService }: FtrProviderContext): void => { }); it('retrieves the last three cases', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - // this should skip the first 7 cases and only return the last 3 - page: 2, - perPage: 7, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.total).to.eql(10); - expect(body.page).to.eql(2); - expect(body.per_page).to.eql(7); - expect(body.cases.length).to.eql(3); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + // this should skip the first 7 cases and only return the last 3 + page: 2, + perPage: 7, + }); + + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(2); + expect(cases.per_page).to.eql(7); + expect(cases.cases.length).to.eql(3); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); }); From 6302029788668a55343b1cea57ab581d2d68f7bd Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 13 Apr 2021 09:49:42 -0400 Subject: [PATCH 17/25] Fixing delete tests --- .../case_api_integration/common/lib/utils.ts | 4 ++-- .../tests/common/cases/delete_cases.ts | 22 ++++++------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 4b0cee2da180e..59ef95996d391 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -563,7 +563,7 @@ export const createCase = async ( /** * Sends a delete request for the specified case IDs. */ -export const deleteCase = async ({ +export const sendDeleteCasesRequest = async ({ supertest, caseIDs, expectedHttpCode = 204, @@ -657,7 +657,7 @@ export const getComment = async ( supertest: st.SuperTest, caseId: string, commentId: string, - expectedHttpCode: number = 204 + expectedHttpCode: number = 200 ): Promise => { const { body: comment } = await supertest .get(`${CASES_URL}/${caseId}/comments/${commentId}`) diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 7b8436529c690..07f1efd608785 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -19,7 +19,7 @@ import { deleteCasesUserActions, deleteComments, createCase, - deleteCase, + sendDeleteCasesRequest, createComment, getComment, } from '../../../../common/lib/utils'; @@ -40,7 +40,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should delete a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); - const body = await deleteCase({ supertest, caseIDs: [postedCase.id] }); + const body = await sendDeleteCasesRequest({ supertest, caseIDs: [postedCase.id] }); expect(body).to.eql({}); }); @@ -48,17 +48,17 @@ export default ({ getService }: FtrProviderContext): void => { it(`should delete a case's comments when that case gets deleted`, async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - // ensure that we can get the comment before deleting the case await getComment(supertest, postedCase.id, patchedCase.comments![0].id); - await deleteCase({ supertest, caseIDs: [postedCase.id] }); + + await sendDeleteCasesRequest({ supertest, caseIDs: [postedCase.id] }); // make sure the comment is now gone await getComment(supertest, postedCase.id, patchedCase.comments![0].id, 404); }); it('unhappy path - 404s when case is not there', async () => { - await deleteCase({ supertest, caseIDs: ['fake-id'], expectedHttpCode: 404 }); + await sendDeleteCasesRequest({ supertest, caseIDs: ['fake-id'], expectedHttpCode: 404 }); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests @@ -78,11 +78,7 @@ export default ({ getService }: FtrProviderContext): void => { const { newSubCaseInfo: caseInfo } = await createSubCase({ supertest, actionID }); expect(caseInfo.subCases![0].id).to.not.eql(undefined); - const { body } = await supertest - .delete(`${CASES_URL}?ids=["${caseInfo.id}"]`) - .set('kbn-xsrf', 'true') - .send() - .expect(204); + const body = await sendDeleteCasesRequest({ supertest, caseIDs: [caseInfo.id] }); expect(body).to.eql({}); await supertest @@ -109,11 +105,7 @@ export default ({ getService }: FtrProviderContext): void => { // make sure we can get the second comment await supertest.get(subCaseCommentUrl).set('kbn-xsrf', 'true').send().expect(200); - await supertest - .delete(`${CASES_URL}?ids=["${caseInfo.id}"]`) - .set('kbn-xsrf', 'true') - .send() - .expect(204); + await sendDeleteCasesRequest({ supertest, caseIDs: [caseInfo.id] }); await supertest.get(subCaseCommentUrl).set('kbn-xsrf', 'true').send().expect(404); }); From ec75fba445b46de134e0ea102afb15a94d0e21eb Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 16:41:40 +0300 Subject: [PATCH 18/25] Migrate find case --- .../routes/api/cases/find_cases.test.ts | 99 ---- .../case_api_integration/common/lib/utils.ts | 15 + .../tests/common/cases/find_cases.ts | 547 +++++++----------- .../tests/common/cases/get_case.ts | 21 +- 4 files changed, 229 insertions(+), 453 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts deleted file mode 100644 index ca9f731ca5010..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, -} from '../__fixtures__'; -import { initFindCasesApi } from './find_cases'; -import { CASES_URL } from '../../../../common/constants'; -import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects'; - -describe('FIND all cases', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initFindCasesApi, 'get'); - }); - - it(`gets all the cases`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases).toHaveLength(4); - // mockSavedObjectsRepository do not support filters and returns all cases every time. - expect(response.payload.count_open_cases).toEqual(4); - expect(response.payload.count_closed_cases).toEqual(4); - expect(response.payload.count_in_progress_cases).toEqual(4); - }); - - it(`has proper connector id on cases with configured connector`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases[2].connector.id).toEqual('123'); - }); - - it(`adds 'none' connector id to cases without when 3rd party unconfigured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases[0].connector.id).toEqual('none'); - }); - - it(`adds default connector id to cases without when 3rd party configured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: `${CASES_URL}/_find`, - method: 'get', - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - caseConfigureSavedObject: mockCaseConfigure, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.cases[0].connector.id).toEqual('none'); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 29a0738c00b88..a8e9a0934b499 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -799,3 +799,18 @@ export const getCase = async ( return theCase; }; + +export const findCases = async ( + supertest: st.SuperTest, + query: Record = {}, + expectedHttpCode: number = 200 +): Promise => { + const { body: res } = await supertest + .get(`${CASES_URL}/_find`) + .query({ sortOrder: 'asc', ...query }) + .set('kbn-xsrf', 'true') + .send() + .expect(expectedHttpCode); + + return res; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts index 195ada335e086..ca3b0201c1454 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/find_cases.ts @@ -6,7 +6,6 @@ */ import expect from '@kbn/expect'; -import supertestAsPromised from 'supertest-as-promised'; import type { ApiResponse, estypes } from '@elastic/elasticsearch'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -25,12 +24,12 @@ import { createCaseAsUser, ensureSavedObjectIsAuthorized, findCasesAsUser, + findCases, + createCase, + updateCase, + createComment, } from '../../../../common/lib/utils'; -import { - CasesFindResponse, - CaseStatuses, - CaseType, -} from '../../../../../../plugins/cases/common/api'; +import { CaseResponse, CaseStatuses, CaseType } from '../../../../../../plugins/cases/common/api'; import { obsOnly, secOnly, @@ -62,41 +61,18 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should return empty response', async () => { - const { body } = await supertest - .get(`${CASES_URL}/_find`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(body).to.eql(findCasesResp); + const cases = await findCases(supertest); + expect(cases).to.eql(findCasesResp); }); it('should return cases', async () => { - const { body: a } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const a = await createCase(supertest, postCaseReq); + const b = await createCase(supertest, postCaseReq); + const c = await createCase(supertest, postCaseReq); - const { body: b } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const cases = await findCases(supertest); - const { body: c } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); - - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 3, cases: [a, b, c], @@ -105,20 +81,11 @@ export default ({ getService }: FtrProviderContext): void => { }); it('filters by tags', async () => { - await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(postCaseReq); - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, tags: ['unique'] }) - .expect(200); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&tags=unique`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + await createCase(supertest, postCaseReq); + const postedCase = await createCase(supertest, { ...postCaseReq, tags: ['unique'] }); + const cases = await findCases(supertest, { tags: ['unique'] }); - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [postedCase], @@ -127,40 +94,24 @@ export default ({ getService }: FtrProviderContext): void => { }); it('filters by status', async () => { - const { body: openCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body: toCloseCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: toCloseCase.id, - version: toCloseCase.version, - status: 'closed', - }, - ], - }) - .expect(200); + await createCase(supertest, postCaseReq); + const toCloseCase = await createCase(supertest, postCaseReq); + const patchedCase = await updateCase(supertest, { + cases: [ + { + id: toCloseCase.id, + version: toCloseCase.version, + status: CaseStatuses.closed, + }, + ], + }); - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=open`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.closed }); - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 1, - cases: [openCase], + cases: [patchedCase[0]], count_open_cases: 1, count_closed_cases: 1, count_in_progress_cases: 0, @@ -168,18 +119,10 @@ export default ({ getService }: FtrProviderContext): void => { }); it('filters by reporters', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&reporters=elastic`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); + const cases = await findCases(supertest, { reporters: 'elastic' }); - expect(body).to.eql({ + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [postedCase], @@ -188,32 +131,14 @@ export default ({ getService }: FtrProviderContext): void => { }); it('correctly counts comments', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); + const postedCase = await createCase(supertest, postCaseReq); // post 2 comments - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body: patchedCase } = await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + await createComment(supertest, postedCase.id, postCommentUserReq); + const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - expect(body).to.eql({ + const cases = await findCases(supertest); + expect(cases).to.eql({ ...findCasesResp, total: 1, cases: [ @@ -228,64 +153,38 @@ export default ({ getService }: FtrProviderContext): void => { }); it('correctly counts open/closed/in-progress', async () => { - await supertest.post(CASES_URL).set('kbn-xsrf', 'true').send(postCaseReq); - - const { body: inProgreeCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send(postCaseReq) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: 'closed', - }, - ], - }) - .expect(200); + await createCase(supertest, postCaseReq); + const inProgressCase = await createCase(supertest, postCaseReq); + const postedCase = await createCase(supertest, postCaseReq); - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: inProgreeCase.id, - version: inProgreeCase.version, - status: 'in-progress', - }, - ], - }) - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }); - const { body } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .set('kbn-xsrf', 'true') - .send() - .expect(200); + await updateCase(supertest, { + cases: [ + { + id: inProgressCase.id, + version: inProgressCase.version, + status: CaseStatuses['in-progress'], + }, + ], + }); - expect(body.count_open_cases).to.eql(1); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + const cases = await findCases(supertest); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('unhappy path - 400s when bad query supplied', async () => { - await supertest - .get(`${CASES_URL}/_find?perPage=true`) - .set('kbn-xsrf', 'true') - .send() - .expect(400); + await findCases(supertest, { perPage: true }, 400); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests @@ -334,75 +233,66 @@ export default ({ getService }: FtrProviderContext): void => { }); }); it('correctly counts stats without using a filter', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .expect(200); - - expect(body.total).to.eql(3); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + const cases = await findCases(supertest); + + expect(cases.total).to.eql(3); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('correctly counts stats with a filter for open cases', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=open`) - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.open }); - expect(body.cases.length).to.eql(1); + expect(cases.cases.length).to.eql(1); // since we're filtering on status and the collection only has an in-progress case, it should only return the // individual case that has the open status and no collections // ENABLE_CASE_CONNECTOR: this value is not correct because it includes a collection // that does not have an open case. This is a known issue and will need to be resolved // when this issue is addressed: https://github.com/elastic/kibana/issues/94115 - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('correctly counts stats with a filter for individual cases', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&type=${CaseType.individual}`) - .expect(200); - - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { type: CaseType.individual }); + + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats with a filter for collection cases with multiple sub cases', async () => { // this will force the first sub case attached to the collection to be closed // so we'll have one closed sub case and one open sub case await createSubCase({ supertest, caseID: collection.newSubCaseInfo.id, actionID }); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&type=${CaseType.collection}`) - .expect(200); - - expect(body.total).to.eql(1); - expect(body.cases[0].subCases?.length).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { type: CaseType.collection }); + + expect(cases.total).to.eql(1); + expect(cases.cases[0].subCases?.length).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats with a filter for collection and open cases with multiple sub cases', async () => { // this will force the first sub case attached to the collection to be closed // so we'll have one closed sub case and one open sub case await createSubCase({ supertest, caseID: collection.newSubCaseInfo.id, actionID }); - const { body }: { body: CasesFindResponse } = await supertest - .get( - `${CASES_URL}/_find?sortOrder=asc&type=${CaseType.collection}&status=${CaseStatuses.open}` - ) - .expect(200); + const cases = await findCases(supertest, { + type: CaseType.collection, + status: CaseStatuses.open, + }); - expect(body.total).to.eql(1); - expect(body.cases[0].subCases?.length).to.eql(1); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(1); + expect(cases.cases[0].subCases?.length).to.eql(1); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats including a collection without sub cases when not filtering on status', async () => { @@ -415,15 +305,13 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(204); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc`) - .expect(200); + const cases = await findCases(supertest, { type: CaseType.collection }); // it should include the collection without sub cases because we did not pass in a filter on status - expect(body.total).to.eql(3); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(3); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('correctly counts stats including a collection without sub cases when filtering on tags', async () => { @@ -436,31 +324,27 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(204); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&tags=defacement`) - .expect(200); + const cases = await findCases(supertest, { tags: ['defacement'] }); // it should include the collection without sub cases because we did not pass in a filter on status - expect(body.total).to.eql(3); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(3); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); it('does not return collections without sub cases matching the requested status', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=closed`) - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.closed }); - expect(body.cases.length).to.eql(1); + expect(cases.cases.length).to.eql(1); // it should not include the collection that has a sub case as in-progress // ENABLE_CASE_CONNECTOR: this value is not correct because it includes collections. This short term // fix for when sub cases are not enabled. When the feature is completed the _find API // will need to be fixed as explained in this ticket: https://github.com/elastic/kibana/issues/94115 - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(1); + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(1); }); it('does not return empty collections when filtering on status', async () => { @@ -473,19 +357,17 @@ export default ({ getService }: FtrProviderContext): void => { .send() .expect(204); - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find?sortOrder=asc&status=closed`) - .expect(200); + const cases = await findCases(supertest, { status: CaseStatuses.closed }); - expect(body.cases.length).to.eql(1); + expect(cases.cases.length).to.eql(1); // ENABLE_CASE_CONNECTOR: this value is not correct because it includes collections. This short term // fix for when sub cases are not enabled. When the feature is completed the _find API // will need to be fixed as explained in this ticket: https://github.com/elastic/kibana/issues/94115 - expect(body.total).to.eql(2); - expect(body.count_closed_cases).to.eql(1); - expect(body.count_open_cases).to.eql(1); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(2); + expect(cases.count_closed_cases).to.eql(1); + expect(cases.count_open_cases).to.eql(1); + expect(cases.count_in_progress_cases).to.eql(0); }); }); }); @@ -500,22 +382,17 @@ export default ({ getService }: FtrProviderContext): void => { await deleteAllCaseItems(es); }); - const createCasesWithTitleAsNumber = async (total: number) => { - const responsePromises: supertestAsPromised.Test[] = []; + const createCasesWithTitleAsNumber = async (total: number): Promise => { + const responsePromises = []; for (let i = 0; i < total; i++) { // this doesn't guarantee that the cases will be created in order that the for-loop executes, // for example case with title '2', could be created before the case with title '1' since we're doing a promise all here // A promise all is just much faster than doing it one by one which would have guaranteed that the cases are // created in the order that the for-loop executes - responsePromises.push( - supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ ...postCaseReq, title: `${i}` }) - ); + responsePromises.push(createCase(supertest, { ...postCaseReq, title: `${i}` })); } const responses = await Promise.all(responsePromises); - return responses.map((response) => response.body); + return responses; }; /** @@ -541,88 +418,68 @@ export default ({ getService }: FtrProviderContext): void => { }; it('returns the correct total when perPage is less than the total', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 1, - perPage: 5, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.cases.length).to.eql(5); - expect(body.total).to.eql(10); - expect(body.page).to.eql(1); - expect(body.per_page).to.eql(5); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + page: 1, + perPage: 5, + }); + + expect(cases.cases.length).to.eql(5); + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(1); + expect(cases.per_page).to.eql(5); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); it('returns the correct total when perPage is greater than the total', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 1, - perPage: 11, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.total).to.eql(10); - expect(body.page).to.eql(1); - expect(body.per_page).to.eql(11); - expect(body.cases.length).to.eql(10); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + page: 1, + perPage: 11, + }); + + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(1); + expect(cases.per_page).to.eql(11); + expect(cases.cases.length).to.eql(10); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); it('returns the correct total when perPage is equal to the total', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 1, - perPage: 10, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.total).to.eql(10); - expect(body.page).to.eql(1); - expect(body.per_page).to.eql(10); - expect(body.cases.length).to.eql(10); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + page: 1, + perPage: 10, + }); + + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(1); + expect(cases.per_page).to.eql(10); + expect(cases.cases.length).to.eql(10); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); it('returns the second page of results', async () => { const perPage = 5; - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: 2, - perPage, - }) - .set('kbn-xsrf', 'true') - .expect(200); + const cases = await findCases(supertest, { + page: 2, + perPage, + }); - expect(body.total).to.eql(10); - expect(body.page).to.eql(2); - expect(body.per_page).to.eql(5); - expect(body.cases.length).to.eql(5); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(2); + expect(cases.per_page).to.eql(5); + expect(cases.cases.length).to.eql(5); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); const allCases = await getAllCasesSortedByCreatedAtAsc(); - body.cases.map((caseInfo, index) => { + cases.cases.map((caseInfo, index) => { // we started on the second page of 10 cases with a perPage of 5, so the first case should 0 + 5 (index + perPage) expect(caseInfo.title).to.eql(allCases[index + perPage]?.cases.title); }); @@ -635,27 +492,22 @@ export default ({ getService }: FtrProviderContext): void => { // it's less than or equal here because the page starts at 1, so page 5 is a valid page number // and should have case titles 9, and 10 for (let currentPage = 1; currentPage <= total / perPage; currentPage++) { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - page: currentPage, - perPage, - }) - .set('kbn-xsrf', 'true') - .expect(200); + const cases = await findCases(supertest, { + page: currentPage, + perPage, + }); - expect(body.total).to.eql(total); - expect(body.page).to.eql(currentPage); - expect(body.per_page).to.eql(perPage); - expect(body.cases.length).to.eql(perPage); - expect(body.count_open_cases).to.eql(total); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + expect(cases.total).to.eql(total); + expect(cases.page).to.eql(currentPage); + expect(cases.per_page).to.eql(perPage); + expect(cases.cases.length).to.eql(perPage); + expect(cases.count_open_cases).to.eql(total); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); const allCases = await getAllCasesSortedByCreatedAtAsc(); - body.cases.map((caseInfo, index) => { + cases.cases.map((caseInfo, index) => { // for page 1, the cases tiles should be 0,1,2 for page 2: 3,4,5 etc (assuming the titles were sorted // correctly) expect(caseInfo.title).to.eql( @@ -666,24 +518,19 @@ export default ({ getService }: FtrProviderContext): void => { }); it('retrieves the last three cases', async () => { - const { body }: { body: CasesFindResponse } = await supertest - .get(`${CASES_URL}/_find`) - .query({ - sortOrder: 'asc', - // this should skip the first 7 cases and only return the last 3 - page: 2, - perPage: 7, - }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.total).to.eql(10); - expect(body.page).to.eql(2); - expect(body.per_page).to.eql(7); - expect(body.cases.length).to.eql(3); - expect(body.count_open_cases).to.eql(10); - expect(body.count_closed_cases).to.eql(0); - expect(body.count_in_progress_cases).to.eql(0); + const cases = await findCases(supertest, { + // this should skip the first 7 cases and only return the last 3 + page: 2, + perPage: 7, + }); + + expect(cases.total).to.eql(10); + expect(cases.page).to.eql(2); + expect(cases.per_page).to.eql(7); + expect(cases.cases.length).to.eql(3); + expect(cases.count_open_cases).to.eql(10); + expect(cases.count_closed_cases).to.eql(0); + expect(cases.count_in_progress_cases).to.eql(0); }); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts index bdc838abb3a6e..b01447d3889ec 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts @@ -10,7 +10,12 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { AttributesTypeUser } from '../../../../../../plugins/cases/common/api'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; -import { postCaseReq, postCaseResp, postCommentUserReq } from '../../../../common/lib/mock'; +import { + defaultUser, + postCaseReq, + postCaseResp, + postCommentUserReq, +} from '../../../../common/lib/mock'; import { deleteCases, createCase, @@ -35,21 +40,29 @@ export default ({ getService }: FtrProviderContext): void => { const theCase = await getCase(supertest, postedCase.id, true); const data = removeServerGeneratedPropertiesFromCase(theCase); - expect(data).to.eql(postCaseResp(postedCase.id)); + expect(data).to.eql(postCaseResp()); expect(data.comments?.length).to.eql(0); }); it('should return a case with comments', async () => { const postedCase = await createCase(supertest, postCaseReq); await createComment(supertest, postedCase.id, postCommentUserReq); - const theCase = await getCase(supertest, postedCase.id); + const theCase = await getCase(supertest, postedCase.id, true); const comment = removeServerGeneratedPropertiesFromSavedObject( theCase.comments![0] as AttributesTypeUser ); expect(theCase.comments?.length).to.eql(1); - expect(theCase.comments![0]).to.eql(comment); + expect(comment).to.eql({ + type: postCommentUserReq.type, + comment: postCommentUserReq.comment, + associationType: 'case', + created_by: defaultUser, + pushed_at: null, + pushed_by: null, + updated_by: null, + }); }); it('should return a 400 when passing the includeSubCaseComments', async () => { From 618973ccfee5347e92832603da62d48e3331ffd3 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 13 Apr 2021 10:34:55 -0400 Subject: [PATCH 19/25] Renaming functions --- .../case_api_integration/common/lib/utils.ts | 6 +++--- .../tests/basic/cases/push_case.ts | 4 ++-- .../tests/common/cases/delete_cases.ts | 16 ++++++++-------- .../tests/common/cases/get_case.ts | 4 ++-- .../tests/common/cases/post_case.ts | 4 ++-- .../common/cases/reporters/get_reporters.ts | 4 ++-- .../tests/common/cases/status/get_status.ts | 4 ++-- .../tests/common/cases/tags/get_tags.ts | 4 ++-- .../tests/common/comments/delete_comment.ts | 4 ++-- .../tests/common/comments/find_comments.ts | 4 ++-- .../tests/common/comments/patch_comment.ts | 4 ++-- .../tests/common/comments/post_comment.ts | 4 ++-- .../common/user_actions/get_all_user_actions.ts | 4 ++-- .../tests/trial/cases/push_case.ts | 4 ++-- .../cases/user_actions/get_all_user_actions.ts | 4 ++-- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 9eab5ca01411b..6314a3a20c7cc 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -448,7 +448,7 @@ export const removeServerGeneratedPropertiesFromComments = ( export const deleteAllCaseItems = async (es: KibanaClient) => { await Promise.all([ - deleteCases(es), + deleteCasesByESQuery(es), deleteSubCases(es), deleteCasesUserActions(es), deleteComments(es), @@ -468,7 +468,7 @@ export const deleteCasesUserActions = async (es: KibanaClient): Promise => }); }; -export const deleteCases = async (es: KibanaClient): Promise => { +export const deleteCasesByESQuery = async (es: KibanaClient): Promise => { await es.deleteByQuery({ index: '.kibana', // @ts-expect-error @elastic/elasticsearch DeleteByQueryRequest doesn't accept q parameter @@ -603,7 +603,7 @@ export const createCase = async ( /** * Sends a delete request for the specified case IDs. */ -export const sendDeleteCasesRequest = async ({ +export const deleteCases = async ({ supertest, caseIDs, expectedHttpCode = 204, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts index 5894d90da68a1..12ad93252f5d7 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts @@ -11,7 +11,7 @@ import { CASE_CONFIGURE_URL, CASES_URL } from '../../../../../../plugins/cases/c import { postCaseReq } from '../../../../common/lib/mock'; import { - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, deleteConfiguration, @@ -27,7 +27,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('push_case', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteConfiguration(es); await deleteCasesUserActions(es); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 07f1efd608785..2c50ac8a453f9 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -15,11 +15,11 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, createCase, - sendDeleteCasesRequest, + deleteCases, createComment, getComment, } from '../../../../common/lib/utils'; @@ -33,14 +33,14 @@ export default ({ getService }: FtrProviderContext): void => { describe('delete_cases', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteCasesUserActions(es); }); it('should delete a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); - const body = await sendDeleteCasesRequest({ supertest, caseIDs: [postedCase.id] }); + const body = await deleteCases({ supertest, caseIDs: [postedCase.id] }); expect(body).to.eql({}); }); @@ -51,14 +51,14 @@ export default ({ getService }: FtrProviderContext): void => { // ensure that we can get the comment before deleting the case await getComment(supertest, postedCase.id, patchedCase.comments![0].id); - await sendDeleteCasesRequest({ supertest, caseIDs: [postedCase.id] }); + await deleteCases({ supertest, caseIDs: [postedCase.id] }); // make sure the comment is now gone await getComment(supertest, postedCase.id, patchedCase.comments![0].id, 404); }); it('unhappy path - 404s when case is not there', async () => { - await sendDeleteCasesRequest({ supertest, caseIDs: ['fake-id'], expectedHttpCode: 404 }); + await deleteCases({ supertest, caseIDs: ['fake-id'], expectedHttpCode: 404 }); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests @@ -78,7 +78,7 @@ export default ({ getService }: FtrProviderContext): void => { const { newSubCaseInfo: caseInfo } = await createSubCase({ supertest, actionID }); expect(caseInfo.subCases![0].id).to.not.eql(undefined); - const body = await sendDeleteCasesRequest({ supertest, caseIDs: [caseInfo.id] }); + const body = await deleteCases({ supertest, caseIDs: [caseInfo.id] }); expect(body).to.eql({}); await supertest @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext): void => { // make sure we can get the second comment await supertest.get(subCaseCommentUrl).set('kbn-xsrf', 'true').send().expect(200); - await sendDeleteCasesRequest({ supertest, caseIDs: [caseInfo.id] }); + await deleteCases({ supertest, caseIDs: [caseInfo.id] }); await supertest.get(subCaseCommentUrl).set('kbn-xsrf', 'true').send().expect(404); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts index bdc838abb3a6e..9ac4b06bbb2ad 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/get_case.ts @@ -12,7 +12,7 @@ import { AttributesTypeUser } from '../../../../../../plugins/cases/common/api'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { postCaseReq, postCaseResp, postCommentUserReq } from '../../../../common/lib/mock'; import { - deleteCases, + deleteCasesByESQuery, createCase, getCase, createComment, @@ -27,7 +27,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('get_case', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); }); it('should return a case with no comments', async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 4f5aa77005e5e..22e9b6812900e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -16,7 +16,7 @@ import { import { getPostCaseRequest, postCaseResp, defaultUser } from '../../../../common/lib/mock'; import { createCaseAsUser, - deleteCases, + deleteCasesByESQuery, createCase, removeServerGeneratedPropertiesFromCase, removeServerGeneratedPropertiesFromUserAction, @@ -40,7 +40,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('post_case', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); }); describe('happy path', () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/reporters/get_reporters.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/reporters/get_reporters.ts index c6e84766e4638..c811c0982840e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/reporters/get_reporters.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/reporters/get_reporters.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../../../../common/ftr_provider_contex import { CASES_URL, CASE_REPORTERS_URL } from '../../../../../../../plugins/cases/common/constants'; import { defaultUser, postCaseReq } from '../../../../../common/lib/mock'; -import { deleteCases } from '../../../../../common/lib/utils'; +import { deleteCasesByESQuery } from '../../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -19,7 +19,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('get_reporters', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); }); it('should return reporters', async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts index 2a5ba1c797672..b71c7105be8f2 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/status/get_status.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../../../common/ftr_provider_contex import { CaseStatuses } from '../../../../../../../plugins/cases/common/api'; import { postCaseReq } from '../../../../../common/lib/mock'; import { - deleteCases, + deleteCasesByESQuery, createCase, updateCase, getAllCasesStatuses, @@ -24,7 +24,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('get_status', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); }); it('should return case statuses', async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/tags/get_tags.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/tags/get_tags.ts index 3ca8e9b6aa3ce..a47cf12158a34 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/tags/get_tags.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/tags/get_tags.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../../../../common/ftr_provider_contex import { CASES_URL, CASE_TAGS_URL } from '../../../../../../../plugins/cases/common/constants'; import { postCaseReq } from '../../../../../common/lib/mock'; -import { deleteCases } from '../../../../../common/lib/utils'; +import { deleteCasesByESQuery } from '../../../../../common/lib/utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -19,7 +19,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('get_tags', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); }); it('should return case tags', async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts index 7db6cfa5322eb..8a0f53869821c 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts @@ -15,7 +15,7 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, createCase, @@ -30,7 +30,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('delete_comment', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteCasesUserActions(es); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts index b18ea6cad2bce..95f15d1e330ff 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/find_comments.ts @@ -16,7 +16,7 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, } from '../../../../common/lib/utils'; @@ -28,7 +28,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('find_comments', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteCasesUserActions(es); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts index 21d9837cc316d..ef0dff8c50dd8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts @@ -27,7 +27,7 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, createCase, @@ -42,7 +42,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('patch_comment', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteCasesUserActions(es); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index 094cecd6ffa5b..b63e21eea201a 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -30,7 +30,7 @@ import { createSubCase, deleteAllCaseItems, deleteCaseAction, - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, createCase, @@ -59,7 +59,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('post_comment', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteCasesUserActions(es); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 664ba29f4aef3..0d11edc5587d1 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -12,7 +12,7 @@ import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; import { CommentType } from '../../../../../../plugins/cases/common/api'; import { userActionPostResp, postCaseReq, postCommentUserReq } from '../../../../common/lib/mock'; import { - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, deleteConfiguration, @@ -25,7 +25,7 @@ export default ({ getService }: FtrProviderContext): void => { describe('get_all_user_actions', () => { afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteConfiguration(es); await deleteCasesUserActions(es); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index b1d4fc87e4b43..b4896e7f365c0 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -17,7 +17,7 @@ import { postCollectionReq, } from '../../../../common/lib/mock'; import { - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, deleteConfiguration, @@ -47,7 +47,7 @@ export default ({ getService }: FtrProviderContext): void => { }); afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteConfiguration(es); await deleteCasesUserActions(es); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 5ab119beeb77b..3729b20f82b30 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../../../common/ftr_provider_contex import { CASE_CONFIGURE_URL, CASES_URL } from '../../../../../../../plugins/cases/common/constants'; import { defaultUser, postCaseReq } from '../../../../../common/lib/mock'; import { - deleteCases, + deleteCasesByESQuery, deleteCasesUserActions, deleteComments, deleteConfiguration, @@ -40,7 +40,7 @@ export default ({ getService }: FtrProviderContext): void => { ); }); afterEach(async () => { - await deleteCases(es); + await deleteCasesByESQuery(es); await deleteComments(es); await deleteConfiguration(es); await deleteCasesUserActions(es); From 3938d3663a078af2c7f655ccace9976c0189c1f8 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 18:23:02 +0300 Subject: [PATCH 20/25] Migrate push case --- .../routes/api/cases/patch_cases.test.ts | 415 ---------------- .../server/routes/api/cases/post_case.test.ts | 237 --------- .../server/routes/api/cases/push_case.test.ts | 466 ------------------ .../case_api_integration/common/lib/utils.ts | 19 +- .../tests/basic/cases/push_case.ts | 73 ++- .../tests/common/cases/patch_cases.ts | 2 +- .../tests/common/cases/post_case.ts | 29 ++ .../tests/trial/cases/push_case.ts | 400 +++++---------- 8 files changed, 191 insertions(+), 1450 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts diff --git a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts deleted file mode 100644 index 073c447460875..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.test.ts +++ /dev/null @@ -1,415 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, - mockCaseComments, -} from '../__fixtures__'; -import { initPatchCasesApi } from './patch_cases'; -import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects'; -import { CaseStatuses } from '../../../../common/api'; - -describe('PATCH cases', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initPatchCasesApi, 'patch'); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), - })); - }); - - it(`Close a case`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": "2019-11-25T21:54:48.952Z", - "closed_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-id-1", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "closed", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - it(`Open a case`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-4', - status: CaseStatuses.open, - version: 'WzUsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseConfigureSavedObject: mockCaseConfigure, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": Object { - "issueType": "Task", - "parent": null, - "priority": "High", - }, - "id": "123", - "name": "My connector", - "type": ".jira", - }, - "created_at": "2019-11-25T22:32:17.947Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "Oh no, a bad meanie going LOLBins all over the place!", - "external_service": null, - "id": "mock-id-4", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "LOLBins", - ], - "title": "Another bad one", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - it(`Change case to in-progress`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-1', - status: CaseStatuses['in-progress'], - version: 'WzAsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload).toMatchInlineSnapshot(` - Array [ - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-id-1", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "in-progress", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": "2019-11-25T21:54:48.952Z", - "updated_by": Object { - "email": "d00d@awesome.com", - "full_name": "Awesome D00d", - "username": "awesome", - }, - "version": "WzE3LDFd", - }, - ] - `); - }); - - it(`Patches a case without a connector.id`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-no-connector_id', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: [mockCaseNoConnectorId], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload[0].connector.id).toEqual('none'); - }); - - it(`Patches a case with a connector.id`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-3', - status: CaseStatuses.closed, - version: 'WzUsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload[0].connector.id).toEqual('123'); - }); - - it(`Change connector`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-3', - connector: { - id: '456', - name: 'My connector 2', - type: '.jira', - fields: { issueType: 'Bug', priority: 'Low', parent: null }, - }, - version: 'WzUsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload[0].connector).toEqual({ - id: '456', - name: 'My connector 2', - type: '.jira', - fields: { issueType: 'Bug', priority: 'Low', parent: null }, - }); - }); - - it(`Fails with 409 if version does not match`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-1', - case: { status: CaseStatuses.closed }, - version: 'badv=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(409); - }); - - it(`Fails with 406 if updated field is unchanged`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-1', - case: { status: CaseStatuses.open }, - version: 'WzAsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseCommentSavedObject: mockCaseComments, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(406); - }); - - it(`Returns an error if updateCase throws`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', - method: 'patch', - body: { - cases: [ - { - id: 'mock-id-does-not-exist', - status: CaseStatuses.closed, - version: 'WzAsMV0=', - }, - ], - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(404); - expect(response.payload.isBoom).toEqual(true); - }); -}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts deleted file mode 100644 index 3991340612c74..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/post_case.test.ts +++ /dev/null @@ -1,237 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, -} from '../__fixtures__'; -import { initPostCaseApi } from './post_case'; -import { CASES_URL } from '../../../../common/constants'; -import { mockCaseConfigure } from '../__fixtures__/mock_saved_objects'; -import { ConnectorTypes, CaseStatuses } from '../../../../common/api'; - -describe('POST cases', () => { - let routeHandler: RequestHandler; - beforeAll(async () => { - routeHandler = await createRoute(initPostCaseApi, 'post'); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue('2019-11-25T21:54:48.952Z'), - })); - }); - - it(`Posts a new case, no connector configured`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'post', - body: { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - tags: ['defacement'], - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.id).toEqual('mock-it'); - expect(response.payload.status).toEqual('open'); - expect(response.payload.created_by.username).toEqual('awesome'); - expect(response.payload.connector).toEqual({ - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }); - }); - - it(`Posts a new case, connector provided`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'post', - body: { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - tags: ['defacement'], - connector: { - id: '123', - name: 'Jira', - type: '.jira', - fields: { issueType: 'Task', priority: 'High', parent: null }, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseConfigureSavedObject: mockCaseConfigure, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.connector).toEqual({ - id: '123', - name: 'Jira', - type: '.jira', - fields: { issueType: 'Task', priority: 'High', parent: null }, - }); - }); - - it(`Error if you passing status for a new case`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'post', - body: { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - status: CaseStatuses.open, - tags: ['defacement'], - connector: null, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - }); - - it(`Returns an error if postNewCase throws`, async () => { - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'post', - body: { - description: 'Throw an error', - title: 'Super Bad Security Issue', - tags: ['error'], - connector: null, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(400); - expect(response.payload.isBoom).toEqual(true); - }); - - it(`Allow user to create case without authentication`, async () => { - routeHandler = await createRoute(initPostCaseApi, 'post', true); - - const request = httpServerMock.createKibanaRequest({ - path: CASES_URL, - method: 'post', - body: { - description: 'This is a brand new case of a bad meanie defacing data', - title: 'Super Bad Security Issue', - tags: ['defacement'], - connector: { - id: 'none', - name: 'none', - type: ConnectorTypes.none, - fields: null, - }, - settings: { - syncAlerts: true, - }, - owner: 'securitySolution', - }, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseConfigureSavedObject: mockCaseConfigure, - }), - true - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload).toMatchInlineSnapshot(` - Object { - "closed_at": null, - "closed_by": null, - "comments": Array [], - "connector": Object { - "fields": null, - "id": "none", - "name": "none", - "type": ".none", - }, - "created_at": "2019-11-25T21:54:48.952Z", - "created_by": Object { - "email": null, - "full_name": null, - "username": null, - }, - "description": "This is a brand new case of a bad meanie defacing data", - "external_service": null, - "id": "mock-it", - "owner": "securitySolution", - "settings": Object { - "syncAlerts": true, - }, - "status": "open", - "subCaseIds": undefined, - "subCases": undefined, - "tags": Array [ - "defacement", - ], - "title": "Super Bad Security Issue", - "totalAlerts": 0, - "totalComment": 0, - "type": "individual", - "updated_at": null, - "updated_by": null, - "version": "WzksMV0=", - } - `); - }); -}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts b/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts deleted file mode 100644 index adac2c9f7ee38..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/cases/push_case.test.ts +++ /dev/null @@ -1,466 +0,0 @@ -/* - * 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 { kibanaResponseFactory, RequestHandler } from 'src/core/server'; -import { httpServerMock } from 'src/core/server/mocks'; - -import { - createMockSavedObjectsRepository, - createRoute, - createRouteContext, - mockCases, - mockCaseConfigure, - mockCaseMappings, - mockUserActions, - mockCaseComments, -} from '../__fixtures__'; -import { initPushCaseApi } from './push_case'; -import { CasesRequestHandlerContext } from '../../../types'; -import { getCasePushUrl } from '../../../../common/api/helpers'; - -describe('Push case', () => { - let routeHandler: RequestHandler; - const mockDate = '2019-11-25T21:54:48.952Z'; - const caseId = 'mock-id-3'; - const connectorId = '123'; - const path = getCasePushUrl(caseId, connectorId); - - beforeAll(async () => { - routeHandler = await createRoute(initPushCaseApi, 'post'); - const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; - spyOnDate.mockImplementation(() => ({ - toISOString: jest.fn().mockReturnValue(mockDate), - })); - }); - - it(`Pushes a case`, async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.external_service).toEqual({ - connector_id: connectorId, - connector_name: 'ServiceNow', - external_id: '10663', - external_title: 'RJ2-200', - external_url: 'https://siem-kibana.atlassian.net/browse/RJ2-200', - pushed_at: mockDate, - pushed_by: { - email: 'd00d@awesome.com', - full_name: 'Awesome D00d', - username: 'awesome', - }, - }); - }); - - it(`Pushes a case with comments`, async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - caseCommentSavedObject: [mockCaseComments[0]], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.comments[0].pushed_at).toEqual(mockDate); - expect(response.payload.comments[0].pushed_by).toEqual({ - email: 'd00d@awesome.com', - full_name: 'Awesome D00d', - username: 'awesome', - }); - }); - - it(`Filters comments with type alert correctly`, async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - caseCommentSavedObject: [mockCaseComments[0], mockCaseComments[3]], - }) - ); - - const casesClient = await context.cases.getCasesClient(); - casesClient.getAlerts = jest.fn().mockResolvedValue([]); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(casesClient.getAlerts).toHaveBeenCalledWith({ - alertsInfo: [{ id: 'test-id', index: 'test-index' }], - }); - }); - - it(`Calls execute with correct arguments`, async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: 'for-mock-case-id-3', - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const actionsClient = context.actions.getActionsClient(); - - await routeHandler(context, request, kibanaResponseFactory); - expect(actionsClient.execute).toHaveBeenCalledWith({ - actionId: 'for-mock-case-id-3', - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - issueType: 'Task', - parent: null, - priority: 'High', - labels: ['LOLBins'], - summary: 'Another bad one', - description: - 'Oh no, a bad meanie going LOLBins all over the place! (created at 2019-11-25T22:32:17.947Z by elastic)', - externalId: null, - }, - comments: [], - }, - }, - }); - }); - - it(`Pushes a case and closes when closure_type: 'close-by-pushing'`, async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseUserActionsSavedObject: mockUserActions, - caseConfigureSavedObject: [ - { - ...mockCaseConfigure[0], - attributes: { - ...mockCaseConfigure[0].attributes, - closure_type: 'close-by-pushing', - }, - }, - ], - }) - ); - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(response.payload.closed_at).toEqual(mockDate); - }); - - it(`post the correct user action`, async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context, services } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - services.userActionService.postUserActions = jest.fn(); - const postUserActions = services.userActionService.postUserActions as jest.Mock; - - const response = await routeHandler(context, request, kibanaResponseFactory); - expect(response.status).toEqual(200); - expect(postUserActions.mock.calls[0][0].actions[0].attributes).toEqual({ - action: 'push-to-service', - action_at: '2019-11-25T21:54:48.952Z', - action_by: { - email: 'd00d@awesome.com', - full_name: 'Awesome D00d', - username: 'awesome', - }, - action_field: ['pushed'], - new_value: - '{"pushed_at":"2019-11-25T21:54:48.952Z","pushed_by":{"username":"awesome","full_name":"Awesome D00d","email":"d00d@awesome.com"},"connector_id":"123","connector_name":"ServiceNow","external_id":"10663","external_title":"RJ2-200","external_url":"https://siem-kibana.atlassian.net/browse/RJ2-200"}', - old_value: null, - }); - }); - - it('Unhappy path - case id is missing', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const res = await routeHandler(context, request, kibanaResponseFactory); - expect(res.status).toEqual(400); - }); - - it('Unhappy path - connector id is missing', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const res = await routeHandler(context, request, kibanaResponseFactory); - expect(res.status).toEqual(400); - }); - - it('Unhappy path - case does not exists', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: 'not-exist', - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const res = await routeHandler(context, request, kibanaResponseFactory); - expect(res.status).toEqual(404); - }); - - it('Unhappy path - connector does not exists', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: 'not-exists', - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const res = await routeHandler(context, request, kibanaResponseFactory); - expect(res.status).toEqual(404); - }); - - it('Unhappy path - cannot push to a closed case', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: 'mock-id-4', - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const res = await routeHandler(context, request, kibanaResponseFactory); - expect(res.status).toEqual(409); - expect(res.payload.output.payload.message).toBe( - 'This case Another bad one is closed. You can not pushed if the case is closed.' - ); - }); - - it('Unhappy path - throws when external service returns an error', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const actionsClient = context.actions.getActionsClient(); - (actionsClient.execute as jest.Mock).mockResolvedValue({ - status: 'error', - }); - - const res = await routeHandler(context, request, kibanaResponseFactory); - expect(res.status).toEqual(424); - expect(res.payload.output.payload.message).toBe('Error pushing to service'); - }); - - it('Unhappy path - context case missing', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const betterContext = ({ - ...context, - cases: null, - } as unknown) as CasesRequestHandlerContext; - - const res = await routeHandler(betterContext, request, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload).toEqual('RouteHandlerContext is not registered for cases'); - }); - - it('Unhappy path - context actions missing', async () => { - const request = httpServerMock.createKibanaRequest({ - path, - method: 'post', - params: { - case_id: caseId, - connector_id: connectorId, - }, - body: {}, - }); - - const { context } = await createRouteContext( - createMockSavedObjectsRepository({ - caseSavedObject: mockCases, - caseMappingsSavedObject: mockCaseMappings, - caseConfigureSavedObject: mockCaseConfigure, - caseUserActionsSavedObject: mockUserActions, - }) - ); - - const betterContext = ({ - ...context, - actions: null, - } as unknown) as CasesRequestHandlerContext; - - const res = await routeHandler(betterContext, request, kibanaResponseFactory); - expect(res.status).toEqual(400); - expect(res.payload).toEqual('Action client not found'); - }); -}); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index a8e9a0934b499..9491815023239 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -729,7 +729,9 @@ export const createConfiguration = async ( return configuration; }; -type CreateConnectorResponse = Omit & { connector_type_id: string }; +export type CreateConnectorResponse = Omit & { + connector_type_id: string; +}; export const createConnector = async ( supertest: st.SuperTest, @@ -814,3 +816,18 @@ export const findCases = async ( return res; }; + +export const pushCase = async ( + supertest: st.SuperTest, + caseId: string, + connectorId: string, + expectedHttpCode: number = 200 +): Promise => { + const { body: res } = await supertest + .post(`${CASES_URL}/${caseId}/connector/${connectorId}/_push`) + .set('kbn-xsrf', 'true') + .send({}) + .expect(expectedHttpCode); + + return res; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts index 5894d90da68a1..2ab9f9246fbc8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/cases/push_case.ts @@ -7,8 +7,6 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import { CASE_CONFIGURE_URL, CASES_URL } from '../../../../../../plugins/cases/common/constants'; - import { postCaseReq } from '../../../../common/lib/mock'; import { deleteCases, @@ -17,6 +15,10 @@ import { deleteConfiguration, getConfigurationRequest, getServiceNowConnector, + createConnector, + createConfiguration, + createCase, + pushCase, } from '../../../../common/lib/utils'; import { ConnectorTypes } from '../../../../../../plugins/cases/common/api'; @@ -34,53 +36,36 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should get 403 when trying to create a connector', async () => { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - ...getServiceNowConnector(), - }) - .expect(403); + await createConnector(supertest, getServiceNowConnector(), 403); }); it('should get 404 when trying to push to a case without a valid connector id', async () => { - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send( - getConfigurationRequest({ - id: 'not-exist', - name: 'Not exist', - type: ConnectorTypes.serviceNowITSM, - }) - ) - .expect(200); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - connector: getConfigurationRequest({ - id: 'not-exist', - name: 'Not exist', - type: ConnectorTypes.serviceNowITSM, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }).connector, + await createConfiguration( + supertest, + getConfigurationRequest({ + id: 'not-exist', + name: 'Not exist', + type: ConnectorTypes.serviceNowITSM, }) - .expect(200); + ); + + const postedCase = await createCase(supertest, { + ...postCaseReq, + connector: { + id: 'not-exist', + name: 'Not exist', + type: ConnectorTypes.serviceNowITSM, + fields: { + urgency: '2', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + }, + }); - await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/not-exist/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(404); + await pushCase(supertest, postedCase.id, 'not-exist', 404); }); }); }; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 7cf7f632a3391..1d7baabaf93b0 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext): void => { ); }); - it('409s when conflict', async () => { + it('409s when version does not match', async () => { const postedCase = await createCase(supertest, postCaseReq); await updateCase( supertest, diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 4f5aa77005e5e..b4fb39868f6f8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -73,6 +73,35 @@ export default ({ getService }: FtrProviderContext): void => { ); }); + it('should post a case: none connector', async () => { + const postedCase = await createCase( + supertest, + getPostCaseRequest({ + connector: { + id: 'none', + name: 'none', + type: ConnectorTypes.none, + fields: null, + }, + }) + ); + const data = removeServerGeneratedPropertiesFromCase(postedCase); + + expect(data).to.eql( + postCaseResp( + null, + getPostCaseRequest({ + connector: { + id: 'none', + name: 'none', + type: ConnectorTypes.none, + fields: null, + }, + }) + ) + ); + }); + it('should create a user action when creating a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); const userActions = await getAllUserAction(supertest, postedCase.id); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index b1d4fc87e4b43..d137291171df6 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -5,17 +5,13 @@ * 2.0. */ +/* eslint-disable @typescript-eslint/naming-convention */ + import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; -import { CASE_CONFIGURE_URL, CASES_URL } from '../../../../../../plugins/cases/common/constants'; -import { - postCaseReq, - defaultUser, - postCommentUserReq, - postCollectionReq, -} from '../../../../common/lib/mock'; +import { postCaseReq, defaultUser, postCommentUserReq } from '../../../../common/lib/mock'; import { deleteCases, deleteCasesUserActions, @@ -23,12 +19,26 @@ import { deleteConfiguration, getConfigurationRequest, getServiceNowConnector, + createConnector, + createConfiguration, + createCase, + pushCase, + createComment, + CreateConnectorResponse, + updateCase, + getAllUserAction, + removeServerGeneratedPropertiesFromUserAction, } from '../../../../common/lib/utils'; import { ExternalServiceSimulator, getExternalServiceSimulatorPath, } from '../../../../../alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin'; -import { CaseStatuses } from '../../../../../../plugins/cases/common/api'; +import { + CaseConnector, + CaseResponse, + CaseStatuses, + ConnectorTypes, +} from '../../../../../../plugins/cases/common/api'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -54,57 +64,51 @@ export default ({ getService }: FtrProviderContext): void => { await actionsRemover.removeAll(); }); - it('should push a case', async () => { - const { body: connector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - ...getServiceNowConnector(), - config: { apiUrl: servicenowSimulatorURL }, - }) - .expect(200); + const createCaseWithConnector = async ( + configureReq = {} + ): Promise<{ + postedCase: CaseResponse; + connector: CreateConnectorResponse; + }> => { + const connector = await createConnector(supertest, { + ...getServiceNowConnector(), + config: { apiUrl: servicenowSimulatorURL }, + }); actionsRemover.add('default', connector.id, 'action', 'actions'); - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send( - getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - }) - ) - .expect(200); + await createConfiguration(supertest, { + ...getConfigurationRequest({ + id: connector.id, + name: connector.name, + type: connector.connector_type_id as ConnectorTypes, + }), + ...configureReq, + }); - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - connector: getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }).connector, - }) - .expect(200); + const postedCase = await createCase(supertest, { + ...postCaseReq, + connector: { + id: connector.id, + name: connector.name, + type: connector.connector_type_id, + fields: { + urgency: '2', + impact: '2', + severity: '2', + category: 'software', + subcategory: 'os', + }, + } as CaseConnector, + }); - const { body } = await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/${connector.id}/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(200); + return { postedCase, connector }; + }; + + it('should push a case', async () => { + const { postedCase, connector } = await createCaseWithConnector(); + const theCase = await pushCase(supertest, postedCase.id, connector.id); - // eslint-disable-next-line @typescript-eslint/naming-convention - const { pushed_at, external_url, ...rest } = body.external_service; + const { pushed_at, external_url, ...rest } = theCase.external_service!; expect(rest).to.eql({ pushed_by: defaultUser, @@ -123,259 +127,83 @@ export default ({ getService }: FtrProviderContext): void => { }); it('pushes a comment appropriately', async () => { - const { body: connector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - ...getServiceNowConnector(), - config: { apiUrl: servicenowSimulatorURL }, - }) - .expect(200); + const { postedCase, connector } = await createCaseWithConnector(); + await createComment(supertest, postedCase.id, postCommentUserReq); + const theCase = await pushCase(supertest, postedCase.id, connector.id); - actionsRemover.add('default', connector.id, 'action', 'actions'); - - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send( - getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - }) - ) - .expect(200); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - connector: getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }).connector, - }) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/comments`) - .set('kbn-xsrf', 'true') - .send(postCommentUserReq) - .expect(200); - - const { body } = await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/${connector.id}/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(200); - - expect(body.comments[0].pushed_by).to.eql(defaultUser); + expect(theCase.comments![0].pushed_by).to.eql(defaultUser); }); it('should pushes a case and closes when closure_type: close-by-pushing', async () => { - const { body: connector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - ...getServiceNowConnector(), - config: { apiUrl: servicenowSimulatorURL }, - }) - .expect(200); - - actionsRemover.add('default', connector.id, 'action', 'actions'); - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send({ - ...getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - }), - closure_type: 'close-by-pushing', - }) - .expect(200); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - connector: getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }).connector, - }) - .expect(200); + const { postedCase, connector } = await createCaseWithConnector({ + closure_type: 'close-by-pushing', + }); + const theCase = await pushCase(supertest, postedCase.id, connector.id); - const { body } = await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/${connector.id}/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(200); + expect(theCase.status).to.eql('closed'); + }); - expect(body.status).to.eql('closed'); + it('should create the correct user action', async () => { + const { postedCase, connector } = await createCaseWithConnector(); + const pushedCase = await pushCase(supertest, postedCase.id, connector.id); + const userActions = await getAllUserAction(supertest, pushedCase.id); + const pushUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); + + expect(pushUserAction).to.eql({ + action_field: ['pushed'], + action: 'push-to-service', + action_by: defaultUser, + new_value: `{"pushed_at":"${ + pushedCase.external_service!.pushed_at + }","pushed_by":${JSON.stringify({ + username: 'elastic', + full_name: null, + email: null, + })},"connector_id":"${connector.id}","connector_name":"${ + connector.name + }","external_id":"123","external_title":"INC01","external_url":"${servicenowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123"}`, + old_value: null, + case_id: `${postedCase.id}`, + comment_id: null, + sub_case_id: '', + }); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests it.skip('should push a collection case but not close it when closure_type: close-by-pushing', async () => { - const { body: connector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - ...getServiceNowConnector(), - config: { apiUrl: servicenowSimulatorURL }, - }) - .expect(200); - - actionsRemover.add('default', connector.id, 'action', 'actions'); - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send({ - ...getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - }), - closure_type: 'close-by-pushing', - }) - .expect(200); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCollectionReq, - connector: getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }).connector, - }) - .expect(200); - - const { body } = await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/${connector.id}/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(200); + const { postedCase, connector } = await createCaseWithConnector({ + closure_type: 'close-by-pushing', + }); - expect(body.status).to.eql(CaseStatuses.open); + const theCase = await pushCase(supertest, postedCase.id, connector.id); + expect(theCase.status).to.eql(CaseStatuses.open); }); it('unhappy path - 404s when case does not exist', async () => { - await supertest - .post(`${CASES_URL}/fake-id/connector/fake-connector/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(404); + await pushCase(supertest, 'fake-id', 'fake-connector', 404); }); it('unhappy path - 404s when connector does not exist', async () => { - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - connector: getConfigurationRequest().connector, - }) - .expect(200); - - await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/fake-connector/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(404); + const postedCase = await createCase(supertest, { + ...postCaseReq, + connector: getConfigurationRequest().connector, + }); + await pushCase(supertest, postedCase.id, 'fake-connector', 404); }); it('unhappy path = 409s when case is closed', async () => { - const { body: connector } = await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'true') - .send({ - ...getServiceNowConnector(), - config: { apiUrl: servicenowSimulatorURL }, - }) - .expect(200); - - actionsRemover.add('default', connector.id, 'action', 'actions'); - - await supertest - .post(CASE_CONFIGURE_URL) - .set('kbn-xsrf', 'true') - .send( - getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - }) - ) - .expect(200); - - const { body: postedCase } = await supertest - .post(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - ...postCaseReq, - connector: getConfigurationRequest({ - id: connector.id, - name: connector.name, - type: connector.connector_type_id, - fields: { - urgency: '2', - impact: '2', - severity: '2', - category: 'software', - subcategory: 'os', - }, - }).connector, - }) - .expect(200); - - await supertest - .patch(CASES_URL) - .set('kbn-xsrf', 'true') - .send({ - cases: [ - { - id: postedCase.id, - version: postedCase.version, - status: 'closed', - }, - ], - }) - .expect(200); + const { postedCase, connector } = await createCaseWithConnector(); + await updateCase(supertest, { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + status: CaseStatuses.closed, + }, + ], + }); - await supertest - .post(`${CASES_URL}/${postedCase.id}/connector/${connector.id}/_push`) - .set('kbn-xsrf', 'true') - .send({}) - .expect(409); + await pushCase(supertest, postedCase.id, connector.id, 409); }); }); }; From 25736b3fdfcb830ae90a0806373dfad9f56894b2 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 18:25:52 +0300 Subject: [PATCH 21/25] Fix status codes --- x-pack/test/case_api_integration/common/lib/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 9491815023239..195fafbb30fe2 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -661,7 +661,7 @@ export const deleteComment = async ( export const getAllComments = async ( supertest: st.SuperTest, caseId: string, - expectedHttpCode: number = 204 + expectedHttpCode: number = 200 ): Promise => { const { body: comments } = await supertest .get(`${CASES_URL}/${caseId}/comments`) @@ -676,7 +676,7 @@ export const getComment = async ( supertest: st.SuperTest, caseId: string, commentId: string, - expectedHttpCode: number = 204 + expectedHttpCode: number = 200 ): Promise => { const { body: comment } = await supertest .get(`${CASES_URL}/${caseId}/comments/${commentId}`) @@ -691,7 +691,7 @@ export const updateComment = async ( supertest: st.SuperTest, caseId: string, req: CommentPatchRequest, - expectedHttpCode: number = 204 + expectedHttpCode: number = 200 ): Promise => { const { body: res } = await supertest .patch(`${CASES_URL}/${caseId}/comments`) From 1aaeb4fc692d24b1cdea5ff911288b3b3d06dcaf Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 13 Apr 2021 19:42:18 +0300 Subject: [PATCH 22/25] Fixes --- .../api/cases/comments/delete_comment.ts | 4 +-- .../case_api_integration/common/lib/utils.ts | 2 +- .../tests/common/comments/delete_comment.ts | 13 +++++++--- .../tests/common/comments/patch_comment.ts | 25 ++++++++----------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts index 4818ec607cc26..da4064f64be77 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/comments/delete_comment.ts @@ -70,9 +70,7 @@ export function initDeleteCommentApi({ const caseRef = myComment.references.find((c) => c.type === type); if (caseRef == null || (caseRef != null && caseRef.id !== id)) { - throw Boom.notFound( - `This comment ${request.params.comment_id} does not exist in ${id}).` - ); + throw Boom.notFound(`This comment ${request.params.comment_id} does not exist in ${id}.`); } await attachmentService.delete({ diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 195fafbb30fe2..7b4035715044b 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -648,7 +648,7 @@ export const deleteComment = async ( caseId: string, commentId: string, expectedHttpCode: number = 204 -): Promise => { +): Promise<{} | Error> => { const { body: comment } = await supertest .delete(`${CASES_URL}/${caseId}/comments/${commentId}`) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts index 7db6cfa5322eb..07c928c0ada01 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/delete_comment.ts @@ -49,10 +49,15 @@ export default ({ getService }: FtrProviderContext): void => { it('404s when comment belongs to different case', async () => { const postedCase = await createCase(supertest, postCaseReq); const patchedCase = await createComment(supertest, postedCase.id, postCommentUserReq); - const message = await deleteComment(supertest, 'fake-id', patchedCase.comments![0].id, 404); - - expect(message).to.eql( - `This comment ${patchedCase.comments![0].id} does not exist in fake-id).` + const error = (await deleteComment( + supertest, + 'fake-id', + patchedCase.comments![0].id, + 404 + )) as Error; + + expect(error.message).to.be( + `This comment ${patchedCase.comments![0].id} does not exist in fake-id.` ); }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts index 21d9837cc316d..2f0f0d681b112 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/patch_comment.ts @@ -21,6 +21,7 @@ import { postCaseReq, postCommentUserReq, postCommentAlertReq, + postCommentGenAlertReq, } from '../../../../common/lib/mock'; import { createCaseAction, @@ -352,24 +353,18 @@ export default ({ getService }: FtrProviderContext): void => { describe('alert format', () => { type AlertComment = CommentType.alert | CommentType.generatedAlert; + // ENABLE_CASE_CONNECTOR: once the case connector feature is completed create a test case for generated alerts here for (const [alertId, index, type] of [ ['1', ['index1', 'index2'], CommentType.alert], [['1', '2'], 'index', CommentType.alert], - ['1', ['index1', 'index2'], CommentType.generatedAlert], - [['1', '2'], 'index', CommentType.generatedAlert], ]) { it(`throws an error with an alert comment with contents id: ${alertId} indices: ${index} type: ${type}`, async () => { const postedCase = await createCase(supertest, postCaseReq); - const patchedCase = await createComment( - supertest, - postedCase.id, - { ...postCommentAlertReq, alertId, index, type: type as AlertComment }, - 400 - ); + const patchedCase = await createComment(supertest, postedCase.id, postCommentAlertReq); await updateComment( supertest, - postedCase.id, + patchedCase.id, { id: patchedCase.comments![0].id, version: patchedCase.comments![0].version, @@ -389,12 +384,12 @@ export default ({ getService }: FtrProviderContext): void => { ]) { it(`does not throw an error with an alert comment with contents id: ${alertId} indices: ${index} type: ${type}`, async () => { const postedCase = await createCase(supertest, postCaseReq); - const patchedCase = await createComment( - supertest, - postedCase.id, - { ...postCommentAlertReq, alertId, index, type: type as AlertComment }, - 400 - ); + const patchedCase = await createComment(supertest, postedCase.id, { + ...postCommentAlertReq, + alertId, + index, + type: type as AlertComment, + }); await updateComment(supertest, postedCase.id, { id: patchedCase.comments![0].id, From d12d9c28888cf005b156882634eb983bf684e40e Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 13 Apr 2021 15:41:54 -0400 Subject: [PATCH 23/25] Adding mocks --- x-pack/plugins/cases/server/client/client.ts | 1 - .../plugins/cases/server/client/index.test.ts | 53 ------ x-pack/plugins/cases/server/client/mocks.ts | 170 +++++++----------- .../server/connectors/case/index.test.ts | 60 ++----- x-pack/plugins/cases/server/services/mocks.ts | 126 ++++++++----- 5 files changed, 163 insertions(+), 247 deletions(-) delete mode 100644 x-pack/plugins/cases/server/client/index.test.ts diff --git a/x-pack/plugins/cases/server/client/client.ts b/x-pack/plugins/cases/server/client/client.ts index 702329f7bcca2..cb2201b8721f2 100644 --- a/x-pack/plugins/cases/server/client/client.ts +++ b/x-pack/plugins/cases/server/client/client.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import Boom from '@hapi/boom'; import { CasesClientArgs } from './types'; import { CasesSubClient, createCasesSubClient } from './cases/client'; diff --git a/x-pack/plugins/cases/server/client/index.test.ts b/x-pack/plugins/cases/server/client/index.test.ts deleted file mode 100644 index 455e4ae106688..0000000000000 --- a/x-pack/plugins/cases/server/client/index.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { - elasticsearchServiceMock, - loggingSystemMock, - savedObjectsClientMock, -} from '../../../../../src/core/server/mocks'; -import { nullUser } from '../common'; -import { - connectorMappingsServiceMock, - createCaseServiceMock, - createConfigureServiceMock, - createUserActionServiceMock, - createAlertServiceMock, -} from '../services/mocks'; -import { createAuthorizationMock } from '../authorization/mock'; - -jest.mock('./client'); -import { CasesClientHandler } from './client'; -import { createExternalCasesClient } from './index'; - -const logger = loggingSystemMock.create().get('case'); -const esClient = elasticsearchServiceMock.createElasticsearchClient(); -const caseConfigureService = createConfigureServiceMock(); -const alertsService = createAlertServiceMock(); -const caseService = createCaseServiceMock(); -const connectorMappingsService = connectorMappingsServiceMock(); -const savedObjectsClient = savedObjectsClientMock.create(); -const userActionService = createUserActionServiceMock(); -const authorization = createAuthorizationMock(); - -describe('createExternalCasesClient()', () => { - test('it creates the client correctly', async () => { - createExternalCasesClient({ - scopedClusterClient: esClient, - alertsService, - caseConfigureService, - caseService, - connectorMappingsService, - user: nullUser, - savedObjectsClient, - userActionService, - logger, - authorization, - }); - expect(CasesClientHandler).toHaveBeenCalledTimes(1); - }); -}); diff --git a/x-pack/plugins/cases/server/client/mocks.ts b/x-pack/plugins/cases/server/client/mocks.ts index cf964e5e53c4f..03ad31fc2c1bb 100644 --- a/x-pack/plugins/cases/server/client/mocks.ts +++ b/x-pack/plugins/cases/server/client/mocks.ts @@ -5,115 +5,81 @@ * 2.0. */ -import { ElasticsearchClient, KibanaRequest } from 'kibana/server'; -import { DeeplyMockedKeys } from 'packages/kbn-utility-types/target/jest'; -import { - loggingSystemMock, - elasticsearchServiceMock, - savedObjectsServiceMock, -} from '../../../../../src/core/server/mocks'; -import { - AlertServiceContract, - CaseConfigureService, - CaseService, - CaseUserActionService, - ConnectorMappingsService, -} from '../services'; -import { CasesClient } from './types'; -import { authenticationMock } from '../routes/api/__fixtures__'; -import { featuresPluginMock } from '../../../features/server/mocks'; +import { PublicContract, PublicMethodsOf } from '@kbn/utility-types'; + +import { CasesClient, CasesClientInternal } from '.'; +import { AttachmentsSubClient } from './attachments/client'; +import { CasesSubClient } from './cases/client'; import { CasesClientFactory } from './factory'; -import { KibanaFeature } from '../../../features/common'; - -export type CasesClientPluginContractMock = jest.Mocked; -export const createExternalCasesClientMock = (): CasesClientPluginContractMock => ({ - addComment: jest.fn(), - create: jest.fn(), - get: jest.fn(), - push: jest.fn(), - getAlerts: jest.fn(), - getFields: jest.fn(), - getMappings: jest.fn(), - getUserActions: jest.fn(), - update: jest.fn(), - updateAlertsStatus: jest.fn(), - find: jest.fn(), -}); - -export const createCasesClientWithMockSavedObjectsClient = async ({ - savedObjectsClient, - badAuth = false, - omitFromContext = [], -}: { - savedObjectsClient: any; - badAuth?: boolean; - omitFromContext?: string[]; -}): Promise<{ - client: CasesClient; - services: { - userActionService: jest.Mocked; - alertsService: jest.Mocked; +import { SubCasesClient } from './sub_cases/client'; +import { UserActionsSubClient } from './user_actions/client'; + +type CasesSubClientMock = jest.Mocked; + +const createCasesSubClientMock = (): CasesSubClientMock => { + return { + create: jest.fn(), + find: jest.fn(), + get: jest.fn(), + push: jest.fn(), + update: jest.fn(), }; - esClient: DeeplyMockedKeys; -}> => { - const esClient = elasticsearchServiceMock.createElasticsearchClient(); - const log = loggingSystemMock.create().get('case'); - - const auth = badAuth ? authenticationMock.createInvalid() : authenticationMock.create(); - const caseService = new CaseService(log, auth); - const caseConfigureServicePlugin = new CaseConfigureService(log); - const connectorMappingsServicePlugin = new ConnectorMappingsService(log); - - const caseConfigureService = await caseConfigureServicePlugin.setup(); - - const connectorMappingsService = await connectorMappingsServicePlugin.setup(); - const userActionService = { - getUserActions: jest.fn(), - postUserActions: jest.fn(), +}; + +type AttachmentsSubClientMock = jest.Mocked; + +const createAttachmentsSubClientMock = (): AttachmentsSubClientMock => { + return { + add: jest.fn(), }; +}; - const alertsService = { - initialize: jest.fn(), - updateAlertsStatus: jest.fn(), - getAlerts: jest.fn(), +type UserActionsSubClientMock = jest.Mocked; + +const createUserActionsSubClientMock = (): UserActionsSubClientMock => { + return { + getAll: jest.fn(), }; +}; - // since the cases saved objects are hidden we need to use getScopedClient(), we'll just have it return the mock client - // that is passed in to createRouteContext - const savedObjectsService = savedObjectsServiceMock.createStartContract(); - savedObjectsService.getScopedClient.mockReturnValue(savedObjectsClient); - - // create a fake feature - const featureStart = featuresPluginMock.createStart(); - featureStart.getKibanaFeatures.mockReturnValue([ - // all the authorization class cares about is the `cases` field in the kibana feature so just cast it to that - ({ cases: ['securitySolution'] } as unknown) as KibanaFeature, - ]); - - const factory = new CasesClientFactory(log); - factory.initialize({ - alertsService, - caseConfigureService, - caseService, - connectorMappingsService, - userActionService, - featuresPluginStart: featureStart, - getSpace: async (req: KibanaRequest) => undefined, - // intentionally not passing the security plugin so that security will be disabled - }); - - // create a single reference to the caseClient so we can mock its methods - const casesClient = await factory.create({ - savedObjectsService, - // Since authorization is disabled for these unit tests we don't need any information from the request object - // so just pass in an empty one - request: {} as KibanaRequest, - scopedClusterClient: esClient, - }); +type SubCasesClientMock = jest.Mocked; +const createSubCasesClientMock = (): SubCasesClientMock => { return { - client: casesClient, - services: { userActionService, alertsService }, - esClient, + delete: jest.fn(), + find: jest.fn(), + get: jest.fn(), + update: jest.fn(), + }; +}; + +type CasesClientInternalMock = jest.Mocked; + +export interface CasesClientMock extends CasesClient { + cases: CasesSubClientMock; + attachments: AttachmentsSubClientMock; + userActions: UserActionsSubClientMock; + subCases: SubCasesClientMock; +} + +export const createCasesClientMock = (): CasesClientMock => { + const client: PublicContract = { + casesClientInternal: (jest.fn() as unknown) as CasesClientInternalMock, + cases: createCasesSubClientMock(), + attachments: createAttachmentsSubClientMock(), + userActions: createUserActionsSubClientMock(), + subCases: createSubCasesClientMock(), }; + return (client as unknown) as CasesClientMock; +}; + +export type CasesClientFactoryMock = jest.Mocked; + +export const createCasesClientFactory = (): CasesClientFactoryMock => { + const factory: PublicMethodsOf = { + initialize: jest.fn(), + create: jest.fn(), + }; + + return (factory as unknown) as CasesClientFactoryMock; }; diff --git a/x-pack/plugins/cases/server/connectors/case/index.test.ts b/x-pack/plugins/cases/server/connectors/case/index.test.ts index edf7e3d3fdbf1..876b8909b9317 100644 --- a/x-pack/plugins/cases/server/connectors/case/index.test.ts +++ b/x-pack/plugins/cases/server/connectors/case/index.test.ts @@ -6,7 +6,7 @@ */ import { omit } from 'lodash/fp'; -import { KibanaRequest, Logger } from '../../../../../../src/core/server'; +import { Logger } from '../../../../../../src/core/server'; import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; import { actionsMock } from '../../../../actions/server/mocks'; import { validateParams } from '../../../../actions/server/lib'; @@ -19,52 +19,28 @@ import { CaseResponse, CasesResponse, } from '../../../common/api'; -import { - connectorMappingsServiceMock, - createCaseServiceMock, - createConfigureServiceMock, - createUserActionServiceMock, - createAlertServiceMock, -} from '../../services/mocks'; import { CaseActionType, CaseActionTypeExecutorOptions, CaseExecutorParams } from './types'; import { getActionType } from '.'; -import { createExternalCasesClientMock } from '../../client/mocks'; -import { CasesClientFactory } from '../../client/factory'; -import { featuresPluginMock } from '../../../../features/server/mocks'; -import { securityMock } from '../../../../security/server/mocks'; - -const mockCasesClient = createExternalCasesClientMock(); -jest.mock('../../client', () => ({ - createExternalCasesClient: () => mockCasesClient, -})); +import { + CasesClientMock, + createCasesClientFactory, + createCasesClientMock, +} from '../../client/mocks'; const services = actionsMock.createServices(); let caseActionType: CaseActionType; describe('case connector', () => { + let mockCasesClient: CasesClientMock; + beforeEach(() => { - jest.resetAllMocks(); const logger = loggingSystemMock.create().get() as jest.Mocked; - const caseService = createCaseServiceMock(); - const caseConfigureService = createConfigureServiceMock(); - const connectorMappingsService = connectorMappingsServiceMock(); - const userActionService = createUserActionServiceMock(); - const alertsService = createAlertServiceMock(); - const factory = new CasesClientFactory(logger); - - factory.initialize({ - alertsService, - caseConfigureService, - caseService, - connectorMappingsService, - userActionService, - featuresPluginStart: featuresPluginMock.createStart(), - getSpace: async (req: KibanaRequest) => undefined, - securityPluginSetup: securityMock.createSetup(), - securityPluginStart: securityMock.createStart(), - }); + mockCasesClient = createCasesClientMock(); + + const factory = createCasesClientFactory(); + factory.create.mockReturnValue(Promise.resolve(mockCasesClient)); caseActionType = getActionType({ logger, factory, @@ -983,7 +959,7 @@ describe('case connector', () => { owner: 'securitySolution', }; - mockCasesClient.create.mockReturnValue(Promise.resolve(createReturn)); + mockCasesClient.cases.create.mockReturnValue(Promise.resolve(createReturn)); const actionId = 'some-id'; const params: CaseExecutorParams = { @@ -1019,7 +995,7 @@ describe('case connector', () => { const result = await caseActionType.executor(executorOptions); expect(result).toEqual({ actionId, status: 'ok', data: createReturn }); - expect(mockCasesClient.create).toHaveBeenCalledWith({ + expect(mockCasesClient.cases.create).toHaveBeenCalledWith({ ...params.subActionParams, connector: { id: 'jira', @@ -1081,7 +1057,7 @@ describe('case connector', () => { }, ]; - mockCasesClient.update.mockReturnValue(Promise.resolve(updateReturn)); + mockCasesClient.cases.update.mockReturnValue(Promise.resolve(updateReturn)); const actionId = 'some-id'; const params: CaseExecutorParams = { @@ -1109,7 +1085,7 @@ describe('case connector', () => { const result = await caseActionType.executor(executorOptions); expect(result).toEqual({ actionId, status: 'ok', data: updateReturn }); - expect(mockCasesClient.update).toHaveBeenCalledWith({ + expect(mockCasesClient.cases.update).toHaveBeenCalledWith({ // Null values have been striped out. cases: [ { @@ -1171,7 +1147,7 @@ describe('case connector', () => { owner: 'securitySolution', }; - mockCasesClient.addComment.mockReturnValue(Promise.resolve(commentReturn)); + mockCasesClient.attachments.add.mockReturnValue(Promise.resolve(commentReturn)); const actionId = 'some-id'; const params: CaseExecutorParams = { @@ -1196,7 +1172,7 @@ describe('case connector', () => { const result = await caseActionType.executor(executorOptions); expect(result).toEqual({ actionId, status: 'ok', data: commentReturn }); - expect(mockCasesClient.addComment).toHaveBeenCalledWith({ + expect(mockCasesClient.attachments.add).toHaveBeenCalledWith({ caseId: 'case-id', comment: { comment: 'a comment', diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index 77129e45348b1..5e5b4ff31309e 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -5,12 +5,14 @@ * 2.0. */ +import { PublicMethodsOf } from '@kbn/utility-types'; import { AlertServiceContract, CaseConfigureService, CaseService, CaseUserActionService, ConnectorMappingsService, + AttachmentService, } from '.'; export type CaseServiceMock = jest.Mocked; @@ -18,61 +20,87 @@ export type CaseConfigureServiceMock = jest.Mocked; export type ConnectorMappingsServiceMock = jest.Mocked; export type CaseUserActionServiceMock = jest.Mocked; export type AlertServiceMock = jest.Mocked; +export type AttachmentServiceMock = jest.Mocked; -export const createCaseServiceMock = (): CaseServiceMock => ({ - createSubCase: jest.fn(), - deleteCase: jest.fn(), - deleteComment: jest.fn(), - deleteSubCase: jest.fn(), - findCases: jest.fn(), - findSubCases: jest.fn(), - findSubCasesByCaseId: jest.fn(), - getAllCaseComments: jest.fn(), - getAllSubCaseComments: jest.fn(), - getCase: jest.fn(), - getCases: jest.fn(), - getComment: jest.fn(), - getMostRecentSubCase: jest.fn(), - getSubCase: jest.fn(), - getSubCases: jest.fn(), - getTags: jest.fn(), - getReporters: jest.fn(), - getUser: jest.fn(), - postNewCase: jest.fn(), - postNewComment: jest.fn(), - patchCase: jest.fn(), - patchCases: jest.fn(), - patchComment: jest.fn(), - patchComments: jest.fn(), - patchSubCase: jest.fn(), - patchSubCases: jest.fn(), - findSubCaseStatusStats: jest.fn(), - getCommentsByAssociation: jest.fn(), - getCaseCommentStats: jest.fn(), - findSubCasesGroupByCase: jest.fn(), - findCaseStatusStats: jest.fn(), - findCasesGroupedByID: jest.fn(), -}); +export const createCaseServiceMock = (): CaseServiceMock => { + const service: PublicMethodsOf = { + createSubCase: jest.fn(), + deleteCase: jest.fn(), + deleteSubCase: jest.fn(), + findCases: jest.fn(), + findSubCases: jest.fn(), + findSubCasesByCaseId: jest.fn(), + getAllCaseComments: jest.fn(), + getAllSubCaseComments: jest.fn(), + getCase: jest.fn(), + getCases: jest.fn(), + getMostRecentSubCase: jest.fn(), + getSubCase: jest.fn(), + getSubCases: jest.fn(), + getTags: jest.fn(), + getReporters: jest.fn(), + getUser: jest.fn(), + postNewCase: jest.fn(), + patchCase: jest.fn(), + patchCases: jest.fn(), + patchSubCase: jest.fn(), + patchSubCases: jest.fn(), + findSubCaseStatusStats: jest.fn(), + getCommentsByAssociation: jest.fn(), + getCaseCommentStats: jest.fn(), + findSubCasesGroupByCase: jest.fn(), + findCaseStatusStats: jest.fn(), + findCasesGroupedByID: jest.fn(), + }; -export const createConfigureServiceMock = (): CaseConfigureServiceMock => ({ - delete: jest.fn(), - get: jest.fn(), - find: jest.fn(), - patch: jest.fn(), - post: jest.fn(), -}); + // the cast here is required because jest.Mocked tries to include private members and would throw an error + return (service as unknown) as CaseServiceMock; +}; -export const connectorMappingsServiceMock = (): ConnectorMappingsServiceMock => ({ - find: jest.fn(), - post: jest.fn(), -}); +export const createConfigureServiceMock = (): CaseConfigureServiceMock => { + const service: PublicMethodsOf = { + delete: jest.fn(), + get: jest.fn(), + find: jest.fn(), + patch: jest.fn(), + post: jest.fn(), + }; -export const createUserActionServiceMock = (): CaseUserActionServiceMock => ({ - getUserActions: jest.fn(), - postUserActions: jest.fn(), -}); + // the cast here is required because jest.Mocked tries to include private members and would throw an error + return (service as unknown) as CaseConfigureServiceMock; +}; + +export const connectorMappingsServiceMock = (): ConnectorMappingsServiceMock => { + const service: PublicMethodsOf = { find: jest.fn(), post: jest.fn() }; + + // the cast here is required because jest.Mocked tries to include private members and would throw an error + return (service as unknown) as ConnectorMappingsServiceMock; +}; + +export const createUserActionServiceMock = (): CaseUserActionServiceMock => { + const service: PublicMethodsOf = { + getAll: jest.fn(), + bulkCreate: jest.fn(), + }; + + // the cast here is required because jest.Mocked tries to include private members and would throw an error + return (service as unknown) as CaseUserActionServiceMock; +}; export const createAlertServiceMock = (): AlertServiceMock => ({ updateAlertsStatus: jest.fn(), getAlerts: jest.fn(), }); + +export const createAttachmentServiceMock = (): AttachmentServiceMock => { + const service: PublicMethodsOf = { + get: jest.fn(), + delete: jest.fn(), + create: jest.fn(), + update: jest.fn(), + bulkUpdate: jest.fn(), + }; + + // the cast here is required because jest.Mocked tries to include private members and would throw an error + return (service as unknown) as AttachmentServiceMock; +}; From be2897f6a45a885579866ed91e86184be857a1e7 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner Date: Tue, 13 Apr 2021 16:24:01 -0400 Subject: [PATCH 24/25] fixing types and removing unneeded functions --- .../__fixtures__/create_mock_so_repository.ts | 305 ------------------ .../server/routes/api/__fixtures__/index.ts | 4 - .../api/__fixtures__/mock_actions_client.ts | 34 -- .../routes/api/__fixtures__/mock_router.ts | 42 --- .../routes/api/__fixtures__/route_contexts.ts | 95 ------ .../routes/api/__mocks__/request_responses.ts | 140 +------- 6 files changed, 1 insertion(+), 619 deletions(-) delete mode 100644 x-pack/plugins/cases/server/routes/api/__fixtures__/create_mock_so_repository.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/__fixtures__/mock_actions_client.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/__fixtures__/mock_router.ts delete mode 100644 x-pack/plugins/cases/server/routes/api/__fixtures__/route_contexts.ts diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/create_mock_so_repository.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/create_mock_so_repository.ts deleted file mode 100644 index a6acd917e4eea..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/create_mock_so_repository.ts +++ /dev/null @@ -1,305 +0,0 @@ -/* - * 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 { - SavedObjectsClientContract, - SavedObjectsErrorHelpers, - SavedObjectsBulkGetObject, - SavedObjectsBulkUpdateObject, - SavedObjectsFindOptions, -} from 'src/core/server'; - -import { - CASE_COMMENT_SAVED_OBJECT, - CASE_SAVED_OBJECT, - CASE_CONFIGURE_SAVED_OBJECT, - CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT, - SUB_CASE_SAVED_OBJECT, - CASE_USER_ACTION_SAVED_OBJECT, -} from '../../../../common/constants'; - -export const createMockSavedObjectsRepository = ({ - caseSavedObject = [], - caseCommentSavedObject = [], - caseConfigureSavedObject = [], - caseMappingsSavedObject = [], - caseUserActionsSavedObject = [], -}: { - caseSavedObject?: any[]; - caseCommentSavedObject?: any[]; - caseConfigureSavedObject?: any[]; - caseMappingsSavedObject?: any[]; - caseUserActionsSavedObject?: any[]; -} = {}) => { - const mockSavedObjectsClientContract = ({ - bulkGet: jest.fn((objects: SavedObjectsBulkGetObject[]) => { - return { - saved_objects: objects.map(({ id, type }) => { - if (type === CASE_COMMENT_SAVED_OBJECT) { - const result = caseCommentSavedObject.filter((s) => s.id === id); - if (!result.length) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return result; - } - const result = caseSavedObject.filter((s) => s.id === id); - if (!result.length) { - return { - id, - type, - error: { - statusCode: 404, - error: 'Not Found', - message: 'Saved object [cases/not-exist] not found', - }, - }; - } - return result[0]; - }), - }; - }), - bulkCreate: jest.fn(), - bulkUpdate: jest.fn((objects: Array>) => { - return { - saved_objects: objects.map(({ id, type, attributes }) => { - if (type === CASE_COMMENT_SAVED_OBJECT) { - if (!caseCommentSavedObject.find((s) => s.id === id)) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - } else if (type === CASE_SAVED_OBJECT) { - if (!caseSavedObject.find((s) => s.id === id)) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - } - - return { - id, - type, - updated_at: '2019-11-22T22:50:55.191Z', - version: 'WzE3LDFd', - attributes, - }; - }), - }; - }), - get: jest.fn((type, id) => { - if (type === CASE_COMMENT_SAVED_OBJECT) { - const result = caseCommentSavedObject.filter((s) => s.id === id); - if (!result.length) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return result[0]; - } else if (type === CASE_SAVED_OBJECT) { - const result = caseSavedObject.filter((s) => s.id === id); - if (!result.length) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return result[0]; - } else { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - }), - find: jest.fn((findArgs: SavedObjectsFindOptions) => { - // References can be an array so we need to loop through it looking for the bad-guy - const hasReferenceIncludeBadGuy = (args: SavedObjectsFindOptions) => { - const references = args.hasReference; - if (references) { - return Array.isArray(references) - ? references.some((ref) => ref.id === 'bad-guy') - : references.id === 'bad-guy'; - } else { - return false; - } - }; - if (hasReferenceIncludeBadGuy(findArgs)) { - throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing'); - } - - if ( - (findArgs.type === CASE_CONFIGURE_SAVED_OBJECT && - caseConfigureSavedObject[0] && - caseConfigureSavedObject[0].id === 'throw-error-find') || - (findArgs.type === CASE_SAVED_OBJECT && - caseSavedObject[0] && - caseSavedObject[0].id === 'throw-error-find') - ) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError('Error thrown for testing'); - } - if (findArgs.type === CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT && caseMappingsSavedObject[0]) { - return { - page: 1, - per_page: 5, - total: 1, - saved_objects: caseMappingsSavedObject, - }; - } - - if (findArgs.type === CASE_CONFIGURE_SAVED_OBJECT) { - return { - page: 1, - per_page: 5, - total: caseConfigureSavedObject.length, - saved_objects: caseConfigureSavedObject, - }; - } - - if (findArgs.type === CASE_COMMENT_SAVED_OBJECT) { - return { - page: 1, - per_page: 5, - total: caseCommentSavedObject.length, - saved_objects: caseCommentSavedObject, - }; - } - - // Currently not supporting sub cases in this mock library - if (findArgs.type === SUB_CASE_SAVED_OBJECT) { - return { - page: 1, - per_page: 0, - total: 0, - saved_objects: [], - }; - } - - if (findArgs.type === CASE_USER_ACTION_SAVED_OBJECT) { - return { - page: 1, - per_page: 5, - total: caseUserActionsSavedObject.length, - saved_objects: caseUserActionsSavedObject, - }; - } - - return { - page: 1, - per_page: 5, - total: caseSavedObject.length, - saved_objects: caseSavedObject, - }; - }), - create: jest.fn((type, attributes, references) => { - if (attributes.description === 'Throw an error' || attributes.comment === 'Throw an error') { - throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing'); - } - - if ( - type === CASE_CONFIGURE_SAVED_OBJECT && - attributes.connector.id === 'throw-error-create' - ) { - throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing'); - } - - if (type === CASE_COMMENT_SAVED_OBJECT) { - const newCommentObj = { - type, - id: 'mock-comment', - attributes, - ...references, - updated_at: '2019-12-02T22:48:08.327Z', - version: 'WzksMV0=', - }; - caseCommentSavedObject = [...caseCommentSavedObject, newCommentObj]; - return newCommentObj; - } - - if (type === CASE_CONFIGURE_SAVED_OBJECT) { - const newConfiguration = { - type, - id: 'mock-configuration', - attributes, - updated_at: '2020-04-09T09:43:51.778Z', - version: attributes.connector.id === 'no-version' ? undefined : 'WzksMV0=', - }; - - caseConfigureSavedObject = [newConfiguration]; - return newConfiguration; - } - - return { - type, - id: 'mock-it', - attributes, - references: [], - updated_at: '2019-12-02T22:48:08.327Z', - version: 'WzksMV0=', - }; - }), - update: jest.fn((type, id, attributes) => { - if (type === CASE_COMMENT_SAVED_OBJECT) { - const foundComment = caseCommentSavedObject.findIndex((s: { id: string }) => s.id === id); - if (foundComment === -1) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - const comment = caseCommentSavedObject[foundComment]; - caseCommentSavedObject.splice(foundComment, 1, { - ...comment, - id, - type, - updated_at: '2019-11-22T22:50:55.191Z', - version: 'WzE3LDFd', - attributes: { - ...comment.attributes, - ...attributes, - }, - }); - } else if (type === CASE_SAVED_OBJECT) { - if (!caseSavedObject.find((s) => s.id === id)) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - } - - if (type === CASE_CONFIGURE_SAVED_OBJECT) { - return { - id, - type, - updated_at: '2019-11-22T22:50:55.191Z', - attributes, - version: attributes.connector?.id === 'no-version' ? undefined : 'WzE3LDFd', - }; - } - - return { - id, - type, - updated_at: '2019-11-22T22:50:55.191Z', - version: 'WzE3LDFd', - attributes, - }; - }), - delete: jest.fn((type: string, id: string) => { - let result = caseSavedObject.filter((s) => s.id === id); - - if (type === CASE_COMMENT_SAVED_OBJECT) { - result = caseCommentSavedObject.filter((s) => s.id === id); - } - - if (type === CASE_CONFIGURE_SAVED_OBJECT) { - result = caseConfigureSavedObject.filter((s) => s.id === id); - } - - if (type === CASE_COMMENT_SAVED_OBJECT && id === 'bad-guy') { - throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing'); - } - - if (!result.length) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - - if ( - type === CASE_CONFIGURE_SAVED_OBJECT && - caseConfigureSavedObject[0].id === 'throw-error-delete' - ) { - throw new Error('Error thrown for testing'); - } - return {}; - }), - deleteByNamespace: jest.fn(), - } as unknown) as jest.Mocked; - - return mockSavedObjectsClientContract; -}; diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/index.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/index.ts index 1abd44aec1552..25f9b05471a0d 100644 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/index.ts +++ b/x-pack/plugins/cases/server/routes/api/__fixtures__/index.ts @@ -6,8 +6,4 @@ */ export * from './mock_saved_objects'; -export { createMockSavedObjectsRepository } from './create_mock_so_repository'; -export { createRouteContext } from './route_contexts'; export { authenticationMock } from './authc_mock'; -export { createRoute } from './mock_router'; -export { createActionsClient } from './mock_actions_client'; diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_actions_client.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_actions_client.ts deleted file mode 100644 index d153c328cbb91..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_actions_client.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 { SavedObjectsErrorHelpers } from 'src/core/server'; -import { actionsClientMock } from '../../../../../actions/server/mocks'; -import { - getActions, - getActionTypes, - getActionExecuteResults, -} from '../__mocks__/request_responses'; - -export const createActionsClient = () => { - const actionsMock = actionsClientMock.create(); - actionsMock.getAll.mockImplementation(() => Promise.resolve(getActions())); - actionsMock.listTypes.mockImplementation(() => Promise.resolve(getActionTypes())); - actionsMock.get.mockImplementation(({ id }) => { - const actions = getActions(); - const action = actions.find((a) => a.id === id); - if (action) { - return Promise.resolve(action); - } else { - return Promise.reject(SavedObjectsErrorHelpers.createGenericNotFoundError('action', id)); - } - }); - actionsMock.execute.mockImplementation(({ actionId }) => - Promise.resolve(getActionExecuteResults(actionId)) - ); - - return actionsMock; -}; diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_router.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_router.ts deleted file mode 100644 index 18cce1b087e5d..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_router.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 { loggingSystemMock, httpServiceMock } from '../../../../../../../src/core/server/mocks'; -import { CaseService, CaseConfigureService, ConnectorMappingsService } from '../../../services'; -import { authenticationMock } from '../__fixtures__'; -import { RouteDeps } from '../types'; - -export const createRoute = async ( - api: (deps: RouteDeps) => void, - method: 'get' | 'post' | 'delete' | 'patch', - badAuth = false -) => { - const httpService = httpServiceMock.createSetupContract(); - const router = httpService.createRouter(); - - const log = loggingSystemMock.create().get('cases'); - const auth = badAuth ? authenticationMock.createInvalid() : authenticationMock.create(); - const caseService = new CaseService(log, auth); - const caseConfigureServicePlugin = new CaseConfigureService(log); - const connectorMappingsServicePlugin = new ConnectorMappingsService(log); - const caseConfigureService = await caseConfigureServicePlugin.setup(); - const connectorMappingsService = await connectorMappingsServicePlugin.setup(); - - api({ - caseConfigureService, - caseService, - connectorMappingsService, - router, - userActionService: { - postUserActions: jest.fn(), - getUserActions: jest.fn(), - }, - logger: log, - }); - - return router[method].mock.calls[0][1]; -}; diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/route_contexts.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/route_contexts.ts deleted file mode 100644 index 284b01ce99325..0000000000000 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/route_contexts.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 { - elasticsearchServiceMock, - loggingSystemMock, - savedObjectsServiceMock, -} from 'src/core/server/mocks'; - -import { KibanaRequest } from 'kibana/server'; -import { - AlertService, - CaseService, - CaseConfigureService, - ConnectorMappingsService, - CaseUserActionService, -} from '../../../services'; -import { authenticationMock } from '../__fixtures__'; -import { createActionsClient } from './mock_actions_client'; -import { featuresPluginMock } from '../../../../../features/server/mocks'; -import { CasesClientFactory } from '../../../client/factory'; -import { xpackMocks } from '../../../../../../mocks'; -import { KibanaFeature } from '../../../../../features/common'; - -export const createRouteContext = async (client: any, badAuth = false) => { - const actionsMock = createActionsClient(); - - const log = loggingSystemMock.create().get('case'); - const esClient = elasticsearchServiceMock.createElasticsearchClient(); - - const authc = badAuth ? authenticationMock.createInvalid() : authenticationMock.create(); - - const caseService = new CaseService(log, authc); - const caseConfigureServicePlugin = new CaseConfigureService(log); - const connectorMappingsServicePlugin = new ConnectorMappingsService(log); - const caseUserActionsServicePlugin = new CaseUserActionService(log); - - const connectorMappingsService = await connectorMappingsServicePlugin.setup(); - const caseConfigureService = await caseConfigureServicePlugin.setup(); - const userActionService = await caseUserActionsServicePlugin.setup(); - const alertsService = new AlertService(); - - // since the cases saved objects are hidden we need to use getScopedClient(), we'll just have it return the mock client - // that is passed in to createRouteContext - const savedObjectsService = savedObjectsServiceMock.createStartContract(); - savedObjectsService.getScopedClient.mockReturnValue(client); - - const contextMock = xpackMocks.createRequestHandlerContext(); - // The tests check the calls on the saved object soClient, so we need to make sure it is the same one returned by - // getScopedClient and .client - contextMock.core.savedObjects.getClient = jest.fn(() => client); - contextMock.core.savedObjects.client = client; - - // create a fake feature - const featureStart = featuresPluginMock.createStart(); - featureStart.getKibanaFeatures.mockReturnValue([ - // all the authorization class cares about is the `cases` field in the kibana feature so just cast it to that - ({ cases: ['securitySolution'] } as unknown) as KibanaFeature, - ]); - - const factory = new CasesClientFactory(log); - factory.initialize({ - alertsService, - caseConfigureService, - caseService, - connectorMappingsService, - userActionService, - featuresPluginStart: featureStart, - getSpace: async (req: KibanaRequest) => undefined, - // intentionally not passing the security plugin so that security will be disabled - }); - - // create a single reference to the caseClient so we can mock its methods - const caseClient = await factory.create({ - savedObjectsService, - // Since authorization is disabled for these unit tests we don't need any information from the request object - // so just pass in an empty one - request: {} as KibanaRequest, - scopedClusterClient: esClient, - }); - - const context = { - ...contextMock, - actions: { getActionsClient: () => actionsMock }, - cases: { - getCasesClient: async () => caseClient, - }, - }; - - return { context, services: { userActionService } }; -}; diff --git a/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts b/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts index 7419452f27c0a..32e42fea5c207 100644 --- a/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts +++ b/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts @@ -5,14 +5,7 @@ * 2.0. */ -import { - ActionTypeConnector, - CasePostRequest, - CasesConfigureRequest, - ConnectorTypes, -} from '../../../../common/api'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FindActionResult } from '../../../../../actions/server/types'; +import { CasePostRequest, ConnectorTypes } from '../../../../common/api'; export const newCase: CasePostRequest = { title: 'My new case', @@ -29,134 +22,3 @@ export const newCase: CasePostRequest = { }, owner: 'securitySolution', }; - -export const getActions = (): FindActionResult[] => [ - { - id: 'e90075a5-c386-41e3-ae21-ba4e61510695', - actionTypeId: '.webhook', - name: 'Test', - config: { - method: 'post', - url: 'https://example.com', - headers: null, - }, - isPreconfigured: false, - referencedByCount: 0, - }, - { - id: '123', - actionTypeId: '.servicenow', - name: 'ServiceNow', - config: { - apiUrl: 'https://dev102283.service-now.com', - }, - isPreconfigured: false, - referencedByCount: 0, - }, - { - id: '456', - actionTypeId: '.jira', - name: 'Connector without isCaseOwned', - config: { - apiUrl: 'https://elastic.jira.com', - }, - isPreconfigured: false, - referencedByCount: 0, - }, - { - id: '789', - actionTypeId: '.resilient', - name: 'Connector without mapping', - config: { - apiUrl: 'https://elastic.resilient.com', - }, - isPreconfigured: false, - referencedByCount: 0, - }, - { - id: 'for-mock-case-id-3', - actionTypeId: '.jira', - name: 'For mock case id 3', - config: { - apiUrl: 'https://elastic.jira.com', - }, - isPreconfigured: false, - referencedByCount: 0, - }, -]; - -export const getActionTypes = (): ActionTypeConnector[] => [ - { - id: '.email', - name: 'Email', - minimumLicenseRequired: 'gold', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.index', - name: 'Index', - minimumLicenseRequired: 'basic', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.servicenow', - name: 'ServiceNow', - minimumLicenseRequired: 'platinum', - enabled: false, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.jira', - name: 'Jira', - minimumLicenseRequired: 'gold', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - }, - { - id: '.resilient', - name: 'IBM Resilient', - minimumLicenseRequired: 'platinum', - enabled: false, - enabledInConfig: true, - enabledInLicense: true, - }, -]; - -export const getActionExecuteResults = (actionId = '123') => ({ - status: 'ok' as const, - data: { - title: 'RJ2-200', - id: '10663', - pushedDate: '2020-12-17T00:32:40.738Z', - url: 'https://siem-kibana.atlassian.net/browse/RJ2-200', - comments: [], - }, - actionId, -}); - -export const newConfiguration: CasesConfigureRequest = { - connector: { - id: '456', - name: 'My connector 2', - type: ConnectorTypes.jira, - fields: null, - }, - closure_type: 'close-by-pushing', -}; - -export const executePushResponse = { - status: 'ok', - data: { - title: 'RJ2-200', - id: '10663', - pushedDate: '2020-12-17T00:32:40.738Z', - url: 'https://siem-kibana.atlassian.net/browse/RJ2-200', - comments: [], - }, -}; From 6db02cd80810be522fe2915eed82a699a02bc6a9 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 14 Apr 2021 11:55:06 +0300 Subject: [PATCH 25/25] PR feedback --- .../cases/server/client/cases/create.ts | 2 +- .../case_api_integration/common/config.ts | 1 + .../case_api_integration/common/lib/utils.ts | 5 +-- .../tests/common/cases/post_case.ts | 35 ++++++++++++++----- .../tests/common/configure/get_connectors.ts | 2 +- .../tests/trial/cases/push_case.ts | 25 +++++++------ 6 files changed, 47 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 61f3605075850..67496599d225d 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -132,7 +132,7 @@ export const create = async ({ actionAt: createdDate, actionBy: { username, full_name, email }, caseId: newCase.id, - fields: ['description', 'status', 'tags', 'title', 'connector', 'settings'], + fields: ['description', 'status', 'tags', 'title', 'connector', 'settings', 'owner'], newValue: JSON.stringify(query), }), ], diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts index 888ae107f221d..d9dacc649c9f5 100644 --- a/x-pack/test/case_api_integration/common/config.ts +++ b/x-pack/test/case_api_integration/common/config.ts @@ -62,6 +62,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) fs.statSync(path.resolve(__dirname, 'fixtures', 'plugins', file)).isDirectory() ); + // This is needed so that we can correctly use the alerting test frameworks mock implementation for the connectors. const alertingAllFiles = fs.readdirSync( path.resolve( __dirname, diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 70b1f249f0b87..32094e60832a9 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -613,7 +613,8 @@ export const deleteCases = async ({ expectedHttpCode?: number; }) => { const { body } = await supertest - .delete(`${CASES_URL}?ids=${JSON.stringify(caseIDs)}`) + .delete(`${CASES_URL}`) + .query({ ids: caseIDs }) .set('kbn-xsrf', 'true') .send() .expect(expectedHttpCode); @@ -640,7 +641,7 @@ export const getAllUserAction = async ( supertest: st.SuperTest, caseId: string, expectedHttpCode: number = 200 -) => { +): Promise => { const { body: userActions } = await supertest .get(`${CASES_URL}/${caseId}/user_actions`) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index aa6f33f029472..1971cb5398b52 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable @typescript-eslint/naming-convention */ + import expect from '@kbn/expect'; import { CASES_URL } from '../../../../../../plugins/cases/common/constants'; @@ -12,6 +14,7 @@ import { ConnectorTypes, ConnectorJiraTypeFields, CaseStatuses, + CaseUserActionResponse, } from '../../../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCaseResp, defaultUser } from '../../../../common/lib/mock'; import { @@ -107,22 +110,36 @@ export default ({ getService }: FtrProviderContext): void => { const userActions = await getAllUserAction(supertest, postedCase.id); const creationUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[0]); - expect(creationUserAction).to.eql({ - action_field: ['description', 'status', 'tags', 'title', 'connector', 'settings'], + const { new_value, ...rest } = creationUserAction as CaseUserActionResponse; + const parsedNewValue = JSON.parse(new_value!); + + expect(rest).to.eql({ + action_field: [ + 'description', + 'status', + 'tags', + 'title', + 'connector', + 'settings', + 'owner', + ], action: 'create', action_by: defaultUser, - new_value: `{"type":"${postedCase.type}","description":"${ - postedCase.description - }","title":"${postedCase.title}","tags":${JSON.stringify( - postedCase.tags - )},"connector":${JSON.stringify(postedCase.connector)},"settings":${JSON.stringify( - postedCase.settings - )},"owner":"${postedCase.owner}"}`, old_value: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', }); + + expect(parsedNewValue).to.eql({ + type: postedCase.type, + description: postedCase.description, + title: postedCase.title, + tags: postedCase.tags, + connector: postedCase.connector, + settings: postedCase.settings, + owner: postedCase.owner, + }); }); it('creates the case without connector in the configuration', async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts index bee8ffec233cb..cfa23a968182f 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/configure/get_connectors.ts @@ -89,7 +89,7 @@ export default ({ getService }: FtrProviderContext): void => { }); it.skip('filters out connectors that are not enabled in license', async () => { - // Should find a way to downgrade license to gold and upgrade back to trial + // TODO: Should find a way to downgrade license to gold and upgrade back to trial }); }); }; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index d78e6a157e7f5..67773067ad2d4 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -37,6 +37,7 @@ import { CaseConnector, CaseResponse, CaseStatuses, + CaseUserActionResponse, ConnectorTypes, } from '../../../../../../plugins/cases/common/api'; @@ -149,24 +150,28 @@ export default ({ getService }: FtrProviderContext): void => { const userActions = await getAllUserAction(supertest, pushedCase.id); const pushUserAction = removeServerGeneratedPropertiesFromUserAction(userActions[1]); - expect(pushUserAction).to.eql({ + const { new_value, ...rest } = pushUserAction as CaseUserActionResponse; + const parsedNewValue = JSON.parse(new_value!); + + expect(rest).to.eql({ action_field: ['pushed'], action: 'push-to-service', action_by: defaultUser, - new_value: `{"pushed_at":"${ - pushedCase.external_service!.pushed_at - }","pushed_by":${JSON.stringify({ - username: 'elastic', - full_name: null, - email: null, - })},"connector_id":"${connector.id}","connector_name":"${ - connector.name - }","external_id":"123","external_title":"INC01","external_url":"${servicenowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123"}`, old_value: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', }); + + expect(parsedNewValue).to.eql({ + pushed_at: pushedCase.external_service!.pushed_at, + pushed_by: defaultUser, + connector_id: connector.id, + connector_name: connector.name, + external_id: '123', + external_title: 'INC01', + external_url: `${servicenowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, + }); }); // ENABLE_CASE_CONNECTOR: once the case connector feature is completed unskip these tests