docs/e2e: indexes out of 'not implemented'; e2e3 covers driver index APIs

README documents supported key patterns, unique/sparse/multikey behavior,
planner rules (multikey two-bound range fallback, sparse/null bail, the
_id fast-path guards), and v1 limits plus the two pre-existing issues the
work surfaces (drop-collection resurrection, compact log_bytes).
e2e3.js exercises createIndex/getIndexes/dropIndex/dropIndexes, the
unique-constraint 11000 path, compound, sparse, and descending indexes
through the official Node driver.
This commit is contained in:
2026-08-02 12:40:30 +03:00
parent 7f2f7c6977
commit adcf0014a9
2 changed files with 151 additions and 2 deletions

104
tests/e2e/e2e3.js Normal file
View File

@@ -0,0 +1,104 @@
// E2E part 3: secondary indexes through the official Node driver.
// createIndex / getIndexes / dropIndex, unique + sparse + compound indexes,
// and a find constrained by a non-_id unique index.
const { MongoClient } = require('mongodb');
const URL = 'mongodb://127.0.0.1:27020';
const results = [];
function check(name, cond, detail = '') {
results.push({ name, ok: !!cond, detail: String(detail) });
if (!cond) console.error(`${name} ${detail}`);
}
async function main() {
const client = new MongoClient(URL, { serverSelectionTimeoutMS: 5000 });
await client.connect();
const db = client.db('e2e3');
const coll = db.collection('users');
await coll.drop().catch(() => {});
// --- createIndex + getIndexes shape ---
await coll.createIndex({ email: 1 }, { unique: true });
const idxs = await coll.indexes();
const names = idxs.map((i) => i.name).sort();
check('getIndexes has _id_', names.includes('_id_'), names.join(','));
check('getIndexes has email_1', names.includes('email_1'), names.join(','));
const emailIdx = idxs.find((i) => i.name === 'email_1');
check('index spec shape', emailIdx && emailIdx.key.email === 1 && emailIdx.unique === true, JSON.stringify(emailIdx));
// Idempotent re-create of the same spec.
await coll.createIndex({ email: 1 }, { unique: true });
check('idempotent re-create', (await coll.indexes()).filter((i) => i.name === 'email_1').length === 1);
// --- unique constraint: _id-less unique index constrains finds ---
await coll.insertOne({ name: 'alice', email: 'a@x.io' });
let dupErr = null;
try {
await coll.insertOne({ name: 'bob', email: 'a@x.io' });
} catch (e) {
dupErr = e;
}
check('unique constraint rejected (11000)', dupErr && dupErr.code === 11000, dupErr && dupErr.message);
check('dup key message names the index', dupErr && dupErr.message.includes('email_1'), dupErr && dupErr.message);
// The unique index is a real index: find by the indexed field works and
// the constrained doc is found.
const found = await coll.find({ email: 'a@x.io' }).toArray();
check('find via unique index', found.length === 1 && found[0].name === 'alice', JSON.stringify(found));
const cnt = await coll.countDocuments({ email: 'a@x.io' });
check('count via unique index', cnt === 1, cnt);
// --- compound index ---
await coll.createIndex({ name: 1, age: 1 }, { name: 'name_1_age_1' });
await coll.insertMany([
{ name: 'carol', age: 30, email: 'c@x.io' },
{ name: 'carol', age: 35, email: 'd@x.io' },
{ name: 'dave', age: 40, email: 'e@x.io' },
]);
const compound = await coll.find({ name: 'carol' }).sort({ age: 1 }).toArray();
check('compound prefix find', compound.length === 2 && compound[0].age === 30, JSON.stringify(compound.map((d) => d.age)));
const compound2 = await coll.find({ name: 'carol', age: { $gt: 32 } }).count();
check('compound equality + range', compound2 === 1, compound2);
// --- sparse index ---
await coll.createIndex({ nickname: 1 }, { sparse: true, name: 'nickname_1_sparse' });
// Only one doc has a nickname; the sparse index covers it.
await coll.updateOne({ name: 'dave' }, { $set: { nickname: 'davie' } });
const sparse = await coll.find({ nickname: 'davie' }).toArray();
check('sparse index find', sparse.length === 1 && sparse[0].name === 'dave', JSON.stringify(sparse));
// Docs without the field stay findable by other fields (scan fallback).
const all = await coll.countDocuments({});
check('count unaffected by sparse index', all === 4, all);
// --- descending key + default names ---
await coll.createIndex({ score: -1 });
const after = await coll.indexes();
const scoreIdx = after.find((i) => i.key && i.key.score === -1);
check('descending index created', !!scoreIdx, JSON.stringify(after));
// --- dropIndex ---
await coll.dropIndex('name_1_age_1');
const afterDrop = await coll.indexes();
check('dropIndex removes it', !afterDrop.some((i) => i.name === 'name_1_age_1'), JSON.stringify(afterDrop.map((i) => i.name)));
check('_id_ still present after drops', afterDrop.some((i) => i.name === '_id_'));
// dropIndexes("*") removes the rest.
await coll.dropIndexes();
const afterAll = await coll.indexes();
check('dropIndexes("*") keeps only _id_', afterAll.length === 1 && afterAll[0].name === '_id_', JSON.stringify(afterAll));
await client.close();
const failed = results.filter((r) => !r.ok);
console.log(`\n${results.length - failed.length}/${results.length} checks passed`);
if (failed.length) {
console.log('FAILED:', failed.map((f) => f.name).join(', '));
process.exit(1);
}
console.log('E2E3_OK');
}
main().catch((e) => {
console.error('E2E3_FAIL', e);
process.exit(1);
});