undeexcepturi
Version:
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.
163 lines (119 loc) • 5.6 kB
text/typescript
import type { MikroORM } from '@mikro-orm/core';
import { wrap } from '@mikro-orm/core';
import type { MongoDriver } from '@mikro-orm/mongodb';
import { Author, Book, BookTag, Publisher } from '../../entities';
import { initORMMongo, mockLogger } from '../../bootstrap';
describe('result cache (mongo)', () => {
let orm: MikroORM<MongoDriver>;
async function createBooksWithTags() {
const author = new Author('Jon Snow', 'snow@wall.st');
const book1 = new Book('My Life on The Wall, part 1', author);
const book2 = new Book('My Life on The Wall, part 2', author);
const book3 = new Book('My Life on The Wall, part 3', author);
const publisher = new Publisher();
book1.publisher = wrap(publisher).toReference();
book2.publisher = wrap(publisher).toReference();
book3.publisher = wrap(publisher).toReference();
const tag1 = new BookTag('silly');
const tag2 = new BookTag('funny');
const tag3 = new BookTag('sick');
const tag4 = new BookTag('strange');
const tag5 = new BookTag('sexy');
book1.tags.add(tag1, tag3);
book2.tags.add(tag1, tag2, tag5);
book3.tags.add(tag2, tag4, tag5);
await orm.em.persistAndFlush([book1, book2, book3]);
orm.em.clear();
return author;
}
beforeAll(async () => orm = await initORMMongo());
beforeEach(async () => orm.schema.clearDatabase());
afterAll(async () => orm.close(true));
test('result caching (find)', async () => {
const a = await createBooksWithTags();
const mock = mockLogger(orm, ['query']);
jest.useFakeTimers({ doNotFake: ['nextTick'] });
const res1 = await orm.em.find(Book, { author: a.id }, { populate: ['author', 'tags', 'publisher'], cache: 50 });
expect(mock.mock.calls).toHaveLength(4);
orm.em.clear();
jest.advanceTimersByTime(25);
const res2 = await orm.em.find(Book, { author: a.id }, { populate: ['author', 'tags', 'publisher'], cache: 50 });
expect(mock.mock.calls).toHaveLength(4); // cache hit, no new query fired
expect(res1.map(e => wrap(e).toObject())).toEqual(res2.map(e => wrap(e).toObject()));
orm.em.clear();
jest.advanceTimersByTime(25);
const res3 = await orm.em.find(Book, { author: a.id }, { populate: ['author', 'tags', 'publisher'], cache: 50 });
expect(mock.mock.calls).toHaveLength(4); // cache hit, no new query fired
expect(res1.map(e => wrap(e).toObject())).toEqual(res3.map(e => wrap(e).toObject()));
orm.em.clear();
jest.advanceTimersByTime(1); // wait for cache to expire
const res4 = await orm.em.find(Book, { author: a.id }, { populate: ['author', 'tags', 'publisher'], cache: 50 });
expect(mock.mock.calls).toHaveLength(8); // cache miss, new query fired
expect(res1.map(e => wrap(e).toObject())).toEqual(res4.map(e => wrap(e).toObject()));
jest.useRealTimers();
});
test('result caching (findOne)', async () => {
const a = await createBooksWithTags();
const mock = mockLogger(orm, ['query']);
jest.useFakeTimers({ doNotFake: ['nextTick'] });
const call = () => orm.em.findOneOrFail(Book, {
author: a.id,
}, {
populate: ['author', 'tags'],
cache: ['abc', 50],
});
const res1 = await call();
expect(mock.mock.calls).toHaveLength(3);
orm.em.clear();
jest.advanceTimersByTime(25);
const res2 = await call();
expect(mock.mock.calls).toHaveLength(3); // cache hit, no new query fired
expect(wrap(res1).toObject()).toEqual(wrap(res2).toObject());
orm.em.clear();
jest.advanceTimersByTime(25);
const res3 = await call();
expect(mock.mock.calls).toHaveLength(3); // cache hit, no new query fired
expect(wrap(res1).toObject()).toEqual(wrap(res3).toObject());
orm.em.clear();
jest.advanceTimersByTime(1); // wait for cache to expire
const res4 = await call();
expect(mock.mock.calls).toHaveLength(6); // cache miss, new query fired
expect(wrap(res1).toObject()).toEqual(wrap(res4).toObject());
jest.advanceTimersByTime(10);
const res5 = await call();
expect(mock.mock.calls).toHaveLength(6); // cache hit
expect(wrap(res1).toObject()).toEqual(wrap(res5).toObject());
jest.advanceTimersByTime(10);
// clear key
await orm.em.clearCache('abc');
orm.em.clear();
jest.advanceTimersByTime(10);
const res6 = await call();
expect(mock.mock.calls).toHaveLength(9); // cache miss as we just cleared the key
expect(wrap(res1).toObject()).toEqual(wrap(res6).toObject());
jest.useRealTimers();
});
test('result caching (count)', async () => {
const a = await createBooksWithTags();
const mock = mockLogger(orm, ['query']);
jest.useFakeTimers({ doNotFake: ['nextTick'] });
const res1 = await orm.em.count(Book, { author: a.id }, { cache: 50 });
expect(mock.mock.calls).toHaveLength(1);
orm.em.clear();
jest.advanceTimersByTime(25);
const res2 = await orm.em.count(Book, { author: a.id }, { cache: 50 });
expect(mock.mock.calls).toHaveLength(1); // cache hit, no new query fired
expect(res1).toEqual(res2);
orm.em.clear();
jest.advanceTimersByTime(25);
const res3 = await orm.em.count(Book, { author: a.id }, { cache: 50 });
expect(mock.mock.calls).toHaveLength(1); // cache hit, no new query fired
expect(res1).toEqual(res3);
orm.em.clear();
jest.advanceTimersByTime(1);
const res4 = await orm.em.count(Book, { author: a.id }, { cache: 50 });
expect(mock.mock.calls).toHaveLength(2); // cache miss, new query fired
expect(res1).toEqual(res4);
jest.useRealTimers();
});
});