Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions packages/e2e-tests/test/e2e-fle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,173 @@ describe('FLE tests', function () {
});
});

context('8.2+', function () {
skipIfServerVersion(testServer, '< 8.2');

context(
'Queryable Encryption Prefix/Suffix/Substring Support',
function () {
// Substring prefix support is enterprise-only 8.2+
skipIfCommunityServer(testServer);

let shell: TestShell;
let uri: string;

const testCollection = 'qeSubstringTest';

before(async function () {
shell = this.startTestShell({
args: ['--nodb', `--cryptSharedLibPath=${cryptLibrary}`],
});
uri = JSON.stringify(await testServer.connectionString());
await shell.waitForPrompt();

// Shared setup for all substring search tests - create collection once
await shell.executeLine(`{
opts = {
keyVaultNamespace: '${dbname}.__keyVault',
kmsProviders: { local: { key: 'A'.repeat(128) } },
bypassQueryAnalysis: false
};

autoMongo = Mongo(${uri}, { ...opts });
autoMongo.getDB('${dbname}').test.drop();

keyId = autoMongo.getKeyVault().createKey('local');

substringOptions = {
strMinQueryLength: 2,
strMaxQueryLength: 10,
strMaxLength: 60,
};

autoMongo.getClientEncryption().createEncryptedCollection('${dbname}', '${testCollection}', {
provider: 'local',
createCollectionOptions: {
encryptedFields: {
fields: [{
keyId,
path: 'data',
bsonType: 'string',
queries: [{
queryType: 'substringPreview',
...substringOptions,
caseSensitive: false,
diacriticSensitive: false,
contention: 4
}]
}]
}
}
});

coll = autoMongo.getDB('${dbname}').${testCollection};

// Setup explicit encryption client
explicitMongo = Mongo(${uri}, { ...opts, bypassQueryAnalysis: true });
ce = explicitMongo.getClientEncryption();
ecoll = explicitMongo.getDB('${dbname}').${testCollection};

explicitOpts = {
algorithm: 'TextPreview',
contentionFactor: 4,
textOptions: { caseSensitive: false, diacriticSensitive: false, substring: substringOptions }
};
}`);
});

after(async function () {
await shell.executeLine(`${testCollection}.drop()`);
});

afterEach(async function () {
await shell.executeLine(`${testCollection}.deleteMany({})`);
});

it('allows queryable encryption with prefix searches', async function () {
// Insert test data for prefix searches
await shell.executeLine(`{
coll.insertOne({ data: 'admin_user_123.txt' });
coll.insertOne({ data: 'admin_super_456.pdf' });
coll.insertOne({ data: 'user_regular_789.pdf' });
coll.insertOne({ data: 'guest_access_000.txt' });

// Add explicit encryption data
ecoll.insertOne({ data: ce.encrypt(keyId, 'admin_explicit_test.pdf', explicitOpts) });
}`);
const prefixResults = await shell.executeLine(
'coll.find({$expr: { $and: [{$encStrContains: {substring: "admin_", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()'
);
expect(prefixResults).to.have.length(2);
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test expects 2 results but then checks for 3 specific strings. This will cause the test to fail since 'admin_explicit_test.pdf' is the third result but the length assertion expects only 2.

Suggested change
expect(prefixResults).to.have.length(2);
expect(prefixResults).to.have.length(3);

Copilot uses AI. Check for mistakes.
expect(prefixResults).to.include('admin_user_123.txt');
expect(prefixResults).to.include('admin_super_456.pdf');
expect(prefixResults).to.include('admin_explicit_test.pdf');
});

it('allows queryable encryption with suffix searches', async function () {
// Insert test data for suffix searches
await shell.executeLine(`{
coll.insertOne({ data: 'admin_user_123.txt' });
coll.insertOne({ data: 'admin_super_456.pdf' });
coll.insertOne({ data: 'user_regular_789.pdf' });
coll.insertOne({ data: 'guest_access_000.txt' });

// Add explicit encryption data
ecoll.insertOne({ data: ce.encrypt(keyId, 'admin_explicit_test.pdf', explicitOpts) });
}`);

const suffixResults = await shell.executeLine(
'coll.find({$expr: { $and: [{$encStrContains: {substring: ".pdf", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()'
);
expect(suffixResults).to.have.length(3);
expect(suffixResults).to.include('admin_super_456.pdf');
expect(suffixResults).to.include('user_regular_789.pdf');
expect(suffixResults).to.include('admin_explicit_test.pdf');
});

it('allows queryable encryption with substring searches', async function () {
// Insert test data for substring searches
// Insert test data for prefix searches
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment 'Insert test data for prefix searches' is incorrect for a substring search test. It should be 'Insert test data for substring searches'.

Suggested change
// Insert test data for prefix searches
// Insert test data for substring searches

Copilot uses AI. Check for mistakes.
await shell.executeLine(`{
coll.insertOne({ data: 'admin_user_123.txt' });
coll.insertOne({ data: 'admin_super_456.pdf' });
coll.insertOne({ data: 'user_regular_789.pdf' });
coll.insertOne({ data: 'guest_access_000.txt' });

// Add explicit encryption data
ecoll.insertOne({ data: ce.encrypt(keyId, 'explicit_user', explicitOpts) });
}`);
// Test substring search returning multiple documents
const substringResults = await shell.executeLine(
'coll.find({$expr: { $and: [{$encStrContains: {substring: "user", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()'
);
expect(substringResults).to.have.length(2);
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same query is executed twice with identical parameters but different expected result counts (2 vs 3). This is logically inconsistent since the same query should return the same results.

Copilot uses AI. Check for mistakes.
expect(substringResults).to.include('user_regular_789.pdf');
expect(substringResults).to.include('admin_user_123.txt');

const testingSubstringResult = await shell.executeLine(
'coll.find({$expr: { $and: [{$encStrContains: {substring: "user", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()'
);
expect(testingSubstringResult).to.have.length(3);
expect(testingSubstringResult).to.include('user_regular_789.pdf');
expect(testingSubstringResult).to.include('admin_user_123.txt');
expect(testingSubstringResult).to.include('explicit_user');

// Test explicit encryption substring search
const explicitSubstringResult = await shell.executeLine(`
ecoll.findOne({$expr: { $and: [{$encStrContains: {substring:
ce.encrypt(keyId, 'user', { ...explicitOpts, queryType: 'substringPreview' }), input: '$data'}}] }},
{ __safeContent__: 0 })
`);
expect(explicitSubstringResult).to.have.length(3);
expect(explicitSubstringResult).to.include('user_regular_789.pdf');
expect(explicitSubstringResult).to.include('admin_user_123.txt');
expect(explicitSubstringResult).to.include('explicit_user');
});
}
);
});

context('pre-6.0', function () {
skipIfServerVersion(testServer, '>= 6.0'); // FLE2 available on 6.0+

Expand Down
Loading