Skip to content

Commit da55ff3

Browse files
test: Add e2e tests for insert, edit and query on the FLE2 collection COMPASS-5861 (#3143)
* test: create FLE2 collection and check badge COMPASS-5850 COMPASS-5851 * refactor: remove encryptedFieldsMap from connect in e2e * refactor: update test ids * refactor: reformat * test: set fle feature flag * test: e2e to insert documents * refactor: move icon to a separate test * test: e2e for querying the encrypted field * test: find by the encrypted field * refactor: fix typos
1 parent a3b548e commit da55ff3

File tree

5 files changed

+199
-30
lines changed

5 files changed

+199
-30
lines changed

packages/compass-crud/src/components/insert-csfle-warning-banner.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function InsertCSFLEWarningBanner({ csfleState }) {
3838

3939
case 'has-known-schema':
4040
return (
41-
<Banner variant={BannerVariant.Info}>
41+
<Banner data-test-id="insert-csfle-has-known-schema" variant={BannerVariant.Info}>
4242
This insert operation will encrypt all fields that are specified in the schema
4343
or In-Use Encryption configuration associated with the collection.
4444

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import chai from 'chai';
2+
const { expect } = chai;
3+
4+
import type { CompassBrowser } from '../helpers/compass-browser';
5+
import * as Selectors from '../helpers/selectors';
6+
7+
export async function getFirstListDocument(browser: CompassBrowser) {
8+
// We check the total from the header area so it is probably good enough to
9+
// just check the first document on screen to make sure the included fields
10+
// and their values are what we expected.
11+
12+
const fieldNameElements = await browser.$$(
13+
Selectors.documentListDocumentKey(1)
14+
);
15+
const fieldNames = await Promise.all(
16+
fieldNameElements.map((el) => el.getText())
17+
);
18+
19+
const fieldValueElements = await browser.$$(
20+
Selectors.documentListDocumentValue(1)
21+
);
22+
const fieldValues = await Promise.all(
23+
fieldValueElements.map((el) => el.getText())
24+
);
25+
26+
expect(fieldValues).to.have.lengthOf(fieldNames.length);
27+
28+
return Object.fromEntries(fieldNames.map((k, i) => [k, fieldValues[i]]));
29+
}

packages/compass-e2e-tests/helpers/selectors.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,8 @@ export const InsertConfirm =
511511
'.insert-document-dialog [role=dialog] > div:nth-child(2) button:first-child';
512512
export const InsertCancel =
513513
'.insert-document-dialog [role=dialog] > div:nth-child(2) button:last-child';
514+
export const insertCSFLEHasKnownSchemaMsg =
515+
'[data-test-id="insert-csfle-has-known-schema"]';
514516

515517
// Import File modal
516518

@@ -562,6 +564,8 @@ export const HadronDocumentRevertElement =
562564
'[data-testid="hadron-document-revert"]';
563565
export const HadronDocumentRemoveElement =
564566
'[data-testid="hadron-document-remove"]';
567+
export const HadronDocumentElementDecryptedIcon =
568+
'[data-test-id="hadron-document-element-decrypted-icon"]';
565569

566570
// Document list view
567571

@@ -575,6 +579,9 @@ export const documentListDocumentKey = (n: number): string => {
575579
export const documentListDocumentValue = (n: number): string => {
576580
return `${DocumentListItem}:nth-child(${n}) ${HadronDocumentValue}`;
577581
};
582+
export const documentListDecryptedIcon = (n: number): string => {
583+
return `${DocumentListItem}:nth-child(${n}) ${HadronDocumentElementDecryptedIcon}`;
584+
};
578585

579586
// Query bar history
580587

packages/compass-e2e-tests/tests/collection-import.test.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'path';
22
import chai from 'chai';
33
import type { CompassBrowser } from '../helpers/compass-browser';
44
import { beforeTests, afterTests, afterTest } from '../helpers/compass';
5+
import { getFirstListDocument } from '../helpers/read-first-document-content';
56
import type { Compass } from '../helpers/compass';
67
import * as Selectors from '../helpers/selectors';
78
import {
@@ -64,30 +65,6 @@ async function unselectFieldName(browser: CompassBrowser, fieldName: string) {
6465
expect(await checkboxElement.isSelected()).to.be.false;
6566
}
6667

67-
async function getFirstListDocument(browser: CompassBrowser) {
68-
// We check the total from the header area so it is probably good enough to
69-
// just check the first document on screen to make sure the included fields
70-
// and their values are what we expected.
71-
72-
const fieldNameElements = await browser.$$(
73-
Selectors.documentListDocumentKey(1)
74-
);
75-
const fieldNames = await Promise.all(
76-
fieldNameElements.map((el) => el.getText())
77-
);
78-
79-
const fieldValueElements = await browser.$$(
80-
Selectors.documentListDocumentValue(1)
81-
);
82-
const fieldValues = await Promise.all(
83-
fieldValueElements.map((el) => el.getText())
84-
);
85-
86-
expect(fieldValues).to.have.lengthOf(fieldNames.length);
87-
88-
return Object.fromEntries(fieldNames.map((k, i) => [k, fieldValues[i]]));
89-
}
90-
9168
describe('Collection import', function () {
9269
let compass: Compass;
9370
let browser: CompassBrowser;

packages/compass-e2e-tests/tests/in-use-encryption.test.ts

Lines changed: 161 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { Compass } from '../helpers/compass';
66
import { MONGODB_VERSION } from '../helpers/compass';
77
import * as Selectors from '../helpers/selectors';
88
import { createDummyCollections } from '../helpers/insert-data';
9+
import { getFirstListDocument } from '../helpers/read-first-document-content';
910

1011
describe('FLE2', function () {
1112
let initialEnvVars: NodeJS.ProcessEnv;
@@ -96,15 +97,17 @@ describe('FLE2', function () {
9697
});
9798

9899
describe('when fleEncryptedFieldsMap is specified while connecting', function () {
99-
const databaseName = 'test';
100+
const databaseName = 'fle-test';
100101
const collectionName = 'my-another-collection';
101102
let compass: Compass;
102103
let browser: CompassBrowser;
103104

104105
before(async function () {
105106
compass = await beforeTests();
106107
browser = compass.browser;
108+
});
107109

110+
beforeEach(async function () {
108111
await browser.connectWithConnectionForm({
109112
hosts: ['localhost:27091'],
110113
fleKeyVaultNamespace: `${databaseName}.keyvault`,
@@ -114,13 +117,26 @@ describe('FLE2', function () {
114117
fields: [
115118
{
116119
path: 'phoneNumber',
117-
keyId: UUID("fd6275d7-9260-4e6c-a86b-68ec5240814a"),
118-
bsonType: 'string'
120+
keyId: UUID("28bbc608-524e-4717-9246-33633361788e"),
121+
bsonType: 'string',
122+
queries: { queryType: 'equality' }
119123
}
120124
]
121125
}
122126
}`,
123127
});
128+
await browser.shellEval(`use ${databaseName}`);
129+
await browser.shellEval(
130+
'db.keyvault.insertOne({' +
131+
'"_id": UUID("28bbc608-524e-4717-9246-33633361788e"),' +
132+
'"keyMaterial": BinData(0, "/yeYyj8IxowIIZGOs5iUcJaUm7KHhoBDAAzNxBz8c5mr2hwBIsBWtDiMU4nhx3fCBrrN3cqXG6jwPgR22gZDIiMZB5+xhplcE9EgNoEEBtRufBE2VjtacpXoqrMgW0+m4Dw76qWUCsF/k1KxYBJabM35KkEoD6+BI1QxU0rwRsR1rE/OLuBPKOEq6pmT5x74i+ursFlTld+5WiOySRDcZg=="),' +
133+
'"creationDate": ISODate("2022-05-27T18:28:33.925Z"),' +
134+
'"updateDate": ISODate("2022-05-27T18:28:33.925Z"),' +
135+
'"status": 0,' +
136+
'"masterKey": { "provider" : "local" }' +
137+
'})'
138+
);
139+
await browser.clickVisible(Selectors.SidebarInstanceRefreshButton);
124140
});
125141

126142
after(async function () {
@@ -129,13 +145,27 @@ describe('FLE2', function () {
129145
}
130146
});
131147

148+
afterEach(async function () {
149+
await browser.shellEval(
150+
`db.getMongo().getDB('${databaseName}').dropDatabase()`
151+
);
152+
});
153+
132154
it('can create a fle2 collection without encryptedFields', async function () {
133155
await browser.navigateToDatabaseTab(databaseName, 'Collections');
134-
135-
// open the create collection modal from the button at the top
136156
await browser.clickVisible(Selectors.DatabaseCreateCollectionButton);
137157
await browser.addCollection(collectionName);
138158

159+
const selector = Selectors.collectionCard(databaseName, collectionName);
160+
await browser.scrollToVirtualItem(
161+
Selectors.CollectionsGrid,
162+
selector,
163+
'grid'
164+
);
165+
166+
const collectionCard = await browser.$(selector);
167+
await collectionCard.waitForDisplayed();
168+
139169
const collectionListFLE2BadgeElement = await browser.$(
140170
Selectors.CollectionListFLE2Badge
141171
);
@@ -160,5 +190,131 @@ describe('FLE2', function () {
160190
'QUERYABLE ENCRYPTION'
161191
);
162192
});
193+
194+
it('can insert a document with an encrypted field and a non-encrypted field', async function () {
195+
await browser.shellEval(`db.createCollection('${collectionName}')`);
196+
197+
await browser.clickVisible(Selectors.SidebarInstanceRefreshButton);
198+
199+
await browser.navigateToCollectionTab(
200+
databaseName,
201+
collectionName,
202+
'Documents'
203+
);
204+
205+
// browse to the "Insert to Collection" modal
206+
await browser.clickVisible(Selectors.AddDataButton);
207+
const insertDocumentOption = await browser.$(
208+
Selectors.InsertDocumentOption
209+
);
210+
await insertDocumentOption.waitForDisplayed();
211+
await browser.clickVisible(Selectors.InsertDocumentOption);
212+
213+
// wait for the modal to appear
214+
const insertDialog = await browser.$(Selectors.InsertDialog);
215+
await insertDialog.waitForDisplayed();
216+
217+
// set the text in the editor
218+
await browser.setAceValue(
219+
Selectors.InsertJSONEditor,
220+
'{ "phoneNumber": "30303030", "name": "Person X" }'
221+
);
222+
223+
const insertCSFLEHasKnownSchemaMsg = await browser.$(
224+
Selectors.insertCSFLEHasKnownSchemaMsg
225+
);
226+
const insertCSFLEHasKnownSchemaMsgText =
227+
await insertCSFLEHasKnownSchemaMsg.getText();
228+
expect(insertCSFLEHasKnownSchemaMsgText).to.include('phoneNumber');
229+
230+
// confirm
231+
const insertConfirm = await browser.$(Selectors.InsertConfirm);
232+
await insertConfirm.waitForEnabled();
233+
await browser.clickVisible(Selectors.InsertConfirm);
234+
235+
// wait for the modal to go away
236+
await insertDialog.waitForDisplayed({ reverse: true });
237+
await browser.clickVisible(Selectors.SidebarInstanceRefreshButton);
238+
239+
const result = await getFirstListDocument(browser);
240+
241+
expect(result._id).to.exist;
242+
expect(result.__safeContent__).to.exist;
243+
delete result._id;
244+
delete result.__safeContent__;
245+
246+
expect(result).to.deep.equal({
247+
phoneNumber: '"30303030"',
248+
name: '"Person X"',
249+
});
250+
});
251+
252+
it('shows decrypted field icon', async function () {
253+
await browser.shellEval(`db.createCollection('${collectionName}')`);
254+
await browser.shellEval(
255+
`db['${collectionName}'].insertOne({ "phoneNumber": "30303030", "name": "Person X" })`
256+
);
257+
258+
await browser.navigateToCollectionTab(
259+
databaseName,
260+
collectionName,
261+
'Documents'
262+
);
263+
264+
const decryptedIconElements = await browser.$$(
265+
Selectors.documentListDecryptedIcon(1)
266+
);
267+
const decryptedIcons = await Promise.all(
268+
decryptedIconElements.map((el) => el.getAttribute('title'))
269+
);
270+
271+
expect(decryptedIcons).to.have.lengthOf(1);
272+
expect(decryptedIcons[0]).to.be.equal('Encrypted Field');
273+
});
274+
275+
it('can edit and query the encrypted field', async function () {
276+
await browser.shellEval(`db.createCollection('${collectionName}')`);
277+
await browser.shellEval(
278+
`db['${collectionName}'].insertOne({ "phoneNumber": "30303030", "name": "Person X" })`
279+
);
280+
281+
await browser.navigateToCollectionTab(
282+
databaseName,
283+
collectionName,
284+
'Documents'
285+
);
286+
287+
const result = await getFirstListDocument(browser);
288+
expect(result.phoneNumber).to.be.equal('"30303030"');
289+
290+
const document = await browser.$(Selectors.DocumentListEntry);
291+
const value = await document.$(
292+
`${Selectors.HadronDocumentElement}:nth-child(2) ${Selectors.HadronDocumentClickableValue}`
293+
);
294+
await value.doubleClick();
295+
296+
const input = await document.$(
297+
`${Selectors.HadronDocumentElement}:nth-child(2) ${Selectors.HadronDocumentValueEditor}`
298+
);
299+
await input.setValue('10101010');
300+
301+
const footer = await document.$(Selectors.DocumentFooterMessage);
302+
expect(await footer.getText()).to.equal('Document Modified.');
303+
304+
const button = await document.$(
305+
'[data-test-id="update-document-button"]'
306+
);
307+
await button.click();
308+
await footer.waitForDisplayed({ reverse: true });
309+
310+
await browser.runFindOperation(
311+
'Documents',
312+
"{ phoneNumber: '10101010' }"
313+
);
314+
315+
const modifiedResult = await getFirstListDocument(browser);
316+
expect(modifiedResult.phoneNumber).to.be.equal('"10101010"');
317+
expect(modifiedResult._id).to.be.equal(result._id);
318+
});
163319
});
164320
});

0 commit comments

Comments
 (0)