AFFiNE/apps/server/src/tests/doc.spec.ts

162 lines
3.5 KiB
TypeScript
Raw Normal View History

import { deepEqual, equal, ok } from 'node:assert';
2023-09-01 22:41:29 +03:00
import { mock } from 'node:test';
import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
2023-09-01 22:41:29 +03:00
import test from 'ava';
import { register } from 'prom-client';
import * as Sinon from 'sinon';
import { Doc as YDoc, encodeStateAsUpdate } from 'yjs';
import { Config, ConfigModule } from '../config';
import { MetricsModule } from '../metrics';
import { DocManager, DocModule } from '../modules/doc';
import { PrismaModule, PrismaService } from '../prisma';
import { flushDB } from './utils';
const createModule = () => {
return Test.createTestingModule({
imports: [
PrismaModule,
MetricsModule,
ConfigModule.forRoot(),
DocModule.forRoot(),
],
}).compile();
};
2023-09-01 22:41:29 +03:00
let app: INestApplication;
let m: TestingModule;
let timer: Sinon.SinonFakeTimers;
2023-09-01 22:41:29 +03:00
// cleanup database before each test
test.beforeEach(async () => {
timer = Sinon.useFakeTimers({
toFake: ['setInterval'],
});
2023-09-01 22:41:29 +03:00
await flushDB();
m = await createModule();
app = m.createNestApplication();
app.enableShutdownHooks();
await app.init();
});
2023-09-01 22:41:29 +03:00
test.afterEach(async () => {
await app.close();
timer.restore();
});
2023-09-01 22:41:29 +03:00
test('should setup update poll interval', async t => {
register.clear();
const m = await createModule();
const manager = m.get(DocManager);
const fake = mock.method(manager, 'setup');
2023-09-01 22:41:29 +03:00
await m.createNestApplication().init();
2023-09-01 22:41:29 +03:00
equal(fake.mock.callCount(), 1);
// @ts-expect-error private member
ok(manager.job);
t.pass();
});
2023-09-01 22:41:29 +03:00
test('should be able to stop poll', async t => {
const manager = m.get(DocManager);
const fake = mock.method(manager, 'destroy');
2023-09-01 22:41:29 +03:00
await app.close();
2023-09-01 22:41:29 +03:00
equal(fake.mock.callCount(), 1);
// @ts-expect-error private member
equal(manager.job, null);
t.pass();
});
2023-09-01 22:41:29 +03:00
test('should poll when intervel due', async t => {
const manager = m.get(DocManager);
const interval = m.get(Config).doc.manager.updatePollInterval;
2023-09-01 22:41:29 +03:00
let resolve: any;
const fake = mock.method(manager, 'apply', () => {
return new Promise(_resolve => {
resolve = _resolve;
});
});
2023-09-01 22:41:29 +03:00
timer.tick(interval);
equal(fake.mock.callCount(), 1);
2023-09-01 22:41:29 +03:00
// busy
timer.tick(interval);
// @ts-expect-error private member
equal(manager.busy, true);
equal(fake.mock.callCount(), 1);
2023-09-01 22:41:29 +03:00
resolve();
await timer.tickAsync(1);
2023-09-01 22:41:29 +03:00
// @ts-expect-error private member
equal(manager.busy, false);
timer.tick(interval);
equal(fake.mock.callCount(), 2);
t.pass();
});
2023-09-01 22:41:29 +03:00
test('should merge update when intervel due', async t => {
const db = m.get(PrismaService);
const manager = m.get(DocManager);
2023-09-01 22:41:29 +03:00
const doc = new YDoc();
const text = doc.getText('content');
text.insert(0, 'hello');
const update = encodeStateAsUpdate(doc);
2023-09-01 22:41:29 +03:00
const ws = await db.workspace.create({
data: {
id: '1',
public: false,
},
});
2023-09-01 22:41:29 +03:00
await db.update.createMany({
data: [
{
id: '1',
2023-09-01 22:41:29 +03:00
workspaceId: '1',
blob: Buffer.from([0, 0]),
},
2023-09-01 22:41:29 +03:00
{
id: '1',
workspaceId: '1',
blob: Buffer.from(update),
},
],
});
await manager.apply();
2023-09-01 22:41:29 +03:00
deepEqual(await manager.getLatestUpdate(ws.id, '1'), update);
2023-09-01 22:41:29 +03:00
let appendUpdate = Buffer.from([]);
doc.on('update', update => {
appendUpdate = Buffer.from(update);
});
2023-09-01 22:41:29 +03:00
text.insert(5, 'world');
await db.update.create({
data: {
workspaceId: ws.id,
id: '1',
blob: appendUpdate,
},
});
await manager.apply();
deepEqual(
await manager.getLatestUpdate(ws.id, '1'),
encodeStateAsUpdate(doc)
);
t.pass();
});