mongoose-delete-ts
Version:
Mongoose soft delete plugin
208 lines (163 loc) • 7.35 kB
text/typescript
import { expect } from 'chai';
import { Deleted, DeletedAt, DeletedMethods, DeletedQueryHelpers, DeletedStaticMethods } from '../source';
import { Model } from 'mongoose';
import { describe } from 'mocha';
import setupModel from './utils/setupModel';
import dropModel from './utils/dropModel';
import { expectDeletedCount, expectModifiedCount, expectOk } from './utils/mongooseExpects';
type Test = { name: string } & Deleted & DeletedAt;
type TestQueryHelpers = DeletedQueryHelpers<Test>;
type TestModel = Model<Test, TestQueryHelpers, DeletedMethods> & DeletedStaticMethods<Test, TestQueryHelpers>;
describe('simple delete', function() {
let TestModel: TestModel;
before(async function() {
TestModel = setupModel<Test, TestModel>('TestSimpleDelete', { name: String });
});
after(async function() {
await dropModel('TestSimpleDelete');
});
it('delete() -> set deleted=true', async function() {
const puffy = await TestModel.create({ name: 'Puffy1' });
const success = await puffy.delete();
expect(success.deleted).to.equal(true);
expect(success.deletedAt).to.not.exist;
});
it('deleteOne() -> set deleted=true', async function() {
await TestModel.create({ name: 'Puffy2' });
const result = await TestModel.deleteOne({ name: 'Puffy2' });
expectOk(result);
expectDeletedCount(result, 1);
});
it('deleteMany() -> set deleted=true', async function() {
await TestModel.create({ name: 'Puffy' });
await TestModel.create({ name: 'Puffy' });
const result = await TestModel.deleteMany({ name: 'Puffy' });
expectOk(result);
expectDeletedCount(result, 2);
});
it('findByIdAndDelete() -> set deleted=true', async function() {
const puffy = await TestModel.create({ name: 'Puffy3' });
await TestModel.findByIdAndDelete(puffy.id);
const result = await TestModel.findById(puffy.id).allDocuments().orFail();
expect(result.deleted).to.equal(true);
});
it('findOneAndDelete() -> set deleted=true', async function() {
await TestModel.create({ name: 'Puffy4' });
await TestModel.findOneAndDelete({ name: 'Puffy4' });
const result = await TestModel.findOne({ name: 'Puffy4' }).allDocuments().orFail();
expect(result.deleted).to.equal(true);
});
it('restore() -> set deleted=false', async function() {
const puffy = await TestModel.findOne({ name: 'Puffy1' }).allDocuments().orFail();
const success = await puffy.restore();
expect(success.deleted).to.equal(false);
});
it('restoreOne() -> set deleted=false', async function() {
const result = await TestModel.restoreOne({ name: 'Puffy2' });
expectOk(result);
expectModifiedCount(result, 1);
const puffy = await TestModel.findOne({ name: 'Puffy2' }).orFail();
expect(puffy.deleted).to.equal(false);
});
it('restoreMany() -> set deleted=false', async function() {
const result = await TestModel.restoreMany({ name: 'Puffy' });
expectOk(result);
expectModifiedCount(result, 2);
const [puffy1, puffy2] = await TestModel.find({ name: 'Puffy' });
expect(puffy1.deleted).to.equal(false);
expect(puffy2.deleted).to.equal(false);
});
});
type Timestamps = { createdAt: Date, updatedAt: Date };
type TestTimestamps = Deleted & Timestamps & { name: string };
type TestTimestampsQueryHelpers = DeletedQueryHelpers<TestTimestamps>;
type TestTimestampsModel = Model<TestTimestamps, TestTimestampsQueryHelpers, DeletedMethods> & DeletedStaticMethods<TestTimestamps, TestTimestampsQueryHelpers>;
describe('delete with timestamps', function() {
let TestModel: TestTimestampsModel;
before(async function() {
TestModel = setupModel<TestTimestamps, TestTimestampsModel>('TestDeleteWithTimestamps', { name: String }, {}, { timestamps: true });
});
after(async function() {
await dropModel('TestDeleteWithTimestamps');
});
it('delete() -> will not change updatedAt', async function() {
const puffy = await TestModel.create({ name: 'Puffy1' });
const updatedAt = new Date(puffy.updatedAt);
const success = await puffy.delete();
expect(success.deleted).to.equal(true);
expect(success.updatedAt).to.deep.equal(updatedAt);
});
it('deleteOne() -> will not change updatedAt', async function() {
const puffy = await TestModel.create({ name: 'Puffy2' });
const updatedAt = new Date(puffy.updatedAt);
await TestModel.deleteOne({ name: 'Puffy2' });
const result = await TestModel.findOne({ name: 'Puffy2' }).allDocuments().orFail();
expect(result.deleted).to.equal(true);
expect(result.updatedAt).to.deep.equal(updatedAt);
});
it('restore() -> will not change updatedAt', async function() {
const puffy = await TestModel.findOne({ name: 'Puffy1' }).allDocuments().orFail();
const updatedAt = new Date(puffy.updatedAt);
const success = await puffy.restore();
expect(success.deleted).to.equal(false);
expect(success.updatedAt).to.deep.equal(updatedAt);
});
it('restoreOne() -> will not change updatedAt', async function() {
const before = await TestModel.findOne({ name: 'Puffy2' }).allDocuments().orFail();
const updatedAt = new Date(before.updatedAt);
await TestModel.restoreOne({ name: 'Puffy2' });
const puffy = await TestModel.findOne({ name: 'Puffy2' }).orFail();
expect(puffy.deleted).to.equal(false);
expect(puffy.updatedAt).to.deep.equal(updatedAt);
});
});
describe('delete with validateBeforeDelete', function() {
let TestModelTrue: TestModel;
let TestModelFalse: TestModel;
before(async function() {
TestModelTrue = setupModel<Test, TestModel>('TestValidateBeforeDeleteTrue', { name: { type: String, required: true } }, { validateBeforeDelete: true });
TestModelFalse = setupModel<Test, TestModel>('TestValidateBeforeDeleteFalse', { name: { type: String, required: true } }, { validateBeforeDelete: false });
});
after(async function() {
await dropModel('TestValidateBeforeDeleteTrue');
await dropModel('TestValidateBeforeDeleteFalse');
});
it('delete() -> will raise ValidationError error', async function() {
const puffy = await TestModelTrue.create({ name: 'Puffy1' });
puffy.name = '';
try {
const doc = await puffy.delete();
expect(doc).to.not.exist;
} catch (error: unknown) {
expectError(error);
expect(error.name).to.equal('ValidationError');
}
});
it('delete() -> will not raise ValidationError error', async function() {
const puffy = await TestModelFalse.create({ name: 'Puffy1' });
puffy.name = '';
const doc = await puffy.delete();
expect(doc.deleted).to.equal(true);
});
});
type Destroyed = { name: string } & Deleted & { destroyed: boolean };
type DestroyedTestModel = Model<Destroyed, TestQueryHelpers, DeletedMethods> & DeletedStaticMethods<Destroyed, TestQueryHelpers>;
describe('deleted schema options', function() {
let DestroyedTestModel: DestroyedTestModel;
before(async function() {
DestroyedTestModel = setupModel<Destroyed, DestroyedTestModel>('TestSchemaOptions', { name: String }, { deleted: { alias: 'destroyed' } });
});
after(async function() {
await dropModel('TestSchemaOptions');
});
it('use custom schema alias', async function() {
const puffy = await DestroyedTestModel.create({ name: 'Puffy1' });
const result = await puffy.delete();
expect(result.destroyed).to.equal(true);
});
});
function expectError(error: unknown): asserts error is Error {
if (!(error instanceof Error)) {
throw new TypeError('error is not of type Error');
}
}