fix: improve and adjust tests

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-06-11 12:59:04 +02:00
parent eb986b1504
commit bb355feddc
4 changed files with 490 additions and 342 deletions

View file

@ -30,6 +30,8 @@ import { NoteGroupPermission } from '../permissions/note-group-permission.entity
import { NoteUserPermission } from '../permissions/note-user-permission.entity'; import { NoteUserPermission } from '../permissions/note-user-permission.entity';
import { Edit } from '../revisions/edit.entity'; import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity'; import { Revision } from '../revisions/revision.entity';
import { RevisionsModule } from '../revisions/revisions.module';
import { RevisionsService } from '../revisions/revisions.service';
import { Session } from '../users/session.entity'; import { Session } from '../users/session.entity';
import { User } from '../users/user.entity'; import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module'; import { UsersModule } from '../users/users.module';
@ -40,6 +42,7 @@ import { HistoryService } from './history.service';
describe('HistoryService', () => { describe('HistoryService', () => {
let service: HistoryService; let service: HistoryService;
let revisionsService: RevisionsService;
let historyRepo: Repository<HistoryEntry>; let historyRepo: Repository<HistoryEntry>;
let noteRepo: Repository<Note>; let noteRepo: Repository<Note>;
let mockedTransaction: jest.Mock< let mockedTransaction: jest.Mock<
@ -94,6 +97,7 @@ describe('HistoryService', () => {
LoggerModule, LoggerModule,
UsersModule, UsersModule,
NotesModule, NotesModule,
RevisionsModule,
ConfigModule.forRoot({ ConfigModule.forRoot({
isGlobal: true, isGlobal: true,
load: [ load: [
@ -135,6 +139,7 @@ describe('HistoryService', () => {
.compile(); .compile();
service = module.get<HistoryService>(HistoryService); service = module.get<HistoryService>(HistoryService);
revisionsService = module.get<RevisionsService>(RevisionsService);
historyRepo = module.get<Repository<HistoryEntry>>( historyRepo = module.get<Repository<HistoryEntry>>(
getRepositoryToken(HistoryEntry), getRepositoryToken(HistoryEntry),
); );
@ -419,8 +424,17 @@ describe('HistoryService', () => {
const title = 'title'; const title = 'title';
const tags = ['tag1', 'tag2']; const tags = ['tag1', 'tag2'];
const note = Note.create(user, alias) as Note; const note = Note.create(user, alias) as Note;
note.title = title; const revision = Revision.create(
note.tags = Promise.resolve( '',
'',
note,
null,
'',
'',
[],
) as Revision;
revision.title = title;
revision.tags = Promise.resolve(
tags.map((tag) => { tags.map((tag) => {
const newTag = new Tag(); const newTag = new Tag();
newTag.name = tag; newTag.name = tag;
@ -431,6 +445,13 @@ describe('HistoryService', () => {
historyEntry.pinStatus = true; historyEntry.pinStatus = true;
mockSelectQueryBuilderInRepo(noteRepo, note); mockSelectQueryBuilderInRepo(noteRepo, note);
jest
.spyOn(revisionsService, 'getLatestRevision')
.mockImplementation((requestedNote) => {
expect(note).toBe(requestedNote);
return Promise.resolve(revision);
});
const historyEntryDto = await service.toHistoryEntryDto(historyEntry); const historyEntryDto = await service.toHistoryEntryDto(historyEntry);
expect(historyEntryDto.pinStatus).toEqual(true); expect(historyEntryDto.pinStatus).toEqual(true);
expect(historyEntryDto.identifier).toEqual(alias); expect(historyEntryDto.identifier).toEqual(alias);

View file

@ -7,6 +7,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { EventEmitterModule } from '@nestjs/event-emitter'; import { EventEmitterModule } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { Mock } from 'ts-mockery';
import { DataSource, EntityManager, Repository } from 'typeorm'; import { DataSource, EntityManager, Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity'; import { AuthToken } from '../auth/auth-token.entity';
@ -35,6 +36,7 @@ import { RevisionsModule } from '../revisions/revisions.module';
import { Session } from '../users/session.entity'; import { Session } from '../users/session.entity';
import { User } from '../users/user.entity'; import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module'; import { UsersModule } from '../users/users.module';
import { mockSelectQueryBuilderInRepo } from '../utils/test-utils/mockSelectQueryBuilder';
import { Alias } from './alias.entity'; import { Alias } from './alias.entity';
import { AliasService } from './alias.service'; import { AliasService } from './alias.service';
import { Note } from './note.entity'; import { Note } from './note.entity';
@ -259,15 +261,13 @@ describe('AliasService', () => {
.spyOn(aliasRepo, 'save') .spyOn(aliasRepo, 'save')
.mockImplementationOnce(async (alias: Alias): Promise<Alias> => alias) .mockImplementationOnce(async (alias: Alias): Promise<Alias> => alias)
.mockImplementationOnce(async (alias: Alias): Promise<Alias> => alias); .mockImplementationOnce(async (alias: Alias): Promise<Alias> => alias);
const createQueryBuilder = {
leftJoinAndSelect: () => createQueryBuilder, mockSelectQueryBuilderInRepo(
where: () => createQueryBuilder, noteRepo,
orWhere: () => createQueryBuilder, Mock.of<Note>({
setParameter: () => createQueryBuilder, ...note,
getOne: async () => { aliases: Promise.resolve(
return { (await note.aliases).map((anAlias) => {
...note,
aliases: (await note.aliases).map((anAlias) => {
if (anAlias.primary) { if (anAlias.primary) {
anAlias.primary = false; anAlias.primary = false;
} }
@ -276,14 +276,10 @@ describe('AliasService', () => {
} }
return anAlias; return anAlias;
}), }),
}; ),
}, }),
}; );
jest
.spyOn(noteRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
const savedAlias = await service.makeAliasPrimary(note, alias2.name); const savedAlias = await service.makeAliasPrimary(note, alias2.name);
expect(savedAlias.name).toEqual(alias2.name); expect(savedAlias.name).toEqual(alias2.name);
expect(savedAlias.primary).toBeTruthy(); expect(savedAlias.primary).toBeTruthy();

View file

@ -7,6 +7,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter'; import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { Mock } from 'ts-mockery';
import { import {
DataSource, DataSource,
EntityManager, EntityManager,
@ -43,20 +44,24 @@ import { RealtimeNoteModule } from '../realtime/realtime-note/realtime-note.modu
import { Edit } from '../revisions/edit.entity'; import { Edit } from '../revisions/edit.entity';
import { Revision } from '../revisions/revision.entity'; import { Revision } from '../revisions/revision.entity';
import { RevisionsModule } from '../revisions/revisions.module'; import { RevisionsModule } from '../revisions/revisions.module';
import { RevisionsService } from '../revisions/revisions.service';
import { Session } from '../users/session.entity'; import { Session } from '../users/session.entity';
import { User } from '../users/user.entity'; import { User } from '../users/user.entity';
import { UsersModule } from '../users/users.module'; import { UsersModule } from '../users/users.module';
import { mockSelectQueryBuilderInRepo } from '../utils/test-utils/mockSelectQueryBuilder';
import { Alias } from './alias.entity'; import { Alias } from './alias.entity';
import { AliasService } from './alias.service'; import { AliasService } from './alias.service';
import { Note } from './note.entity'; import { Note } from './note.entity';
import { NotesService } from './notes.service'; import { NotesService } from './notes.service';
import { Tag } from './tag.entity'; import { Tag } from './tag.entity';
jest.mock('../revisions/revisions.service');
describe('NotesService', () => { describe('NotesService', () => {
let service: NotesService; let service: NotesService;
let revisionsService: RevisionsService;
const noteMockConfig: NoteConfig = createDefaultMockNoteConfig(); const noteMockConfig: NoteConfig = createDefaultMockNoteConfig();
let noteRepo: Repository<Note>; let noteRepo: Repository<Note>;
let revisionRepo: Repository<Revision>;
let userRepo: Repository<User>; let userRepo: Repository<User>;
let groupRepo: Repository<Group>; let groupRepo: Repository<Group>;
let forbiddenNoteId: string; let forbiddenNoteId: string;
@ -74,99 +79,10 @@ describe('NotesService', () => {
true, true,
); );
/**
* Creates a Note and a corresponding User and Group for testing.
* The Note does not have any aliases.
*/
async function getMockData(): Promise<[Note, User, Group]> {
const user = User.create('hardcoded', 'Testy') as User;
const author = Author.create(1);
author.user = Promise.resolve(user);
const group = Group.create('testGroup', 'testGroup', false) as Group;
const content = 'testContent';
jest
.spyOn(noteRepo, 'save')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(async (note: Note): Promise<Note> => note);
mockGroupRepo();
const note = await service.createNote(content, null);
const revisions = await note.revisions;
revisions[0].edits = Promise.resolve([
{
revisions: Promise.resolve(revisions),
startPos: 0,
endPos: 1,
updatedAt: new Date(1549312452000),
author: Promise.resolve(author),
} as Edit,
{
revisions: Promise.resolve(revisions),
startPos: 0,
endPos: 1,
updatedAt: new Date(1549312452001),
author: Promise.resolve(author),
} as Edit,
]);
revisions[0].createdAt = new Date(1549312452000);
jest.spyOn(revisionRepo, 'findOne').mockResolvedValue(revisions[0]);
const createQueryBuilder = {
innerJoin: () => createQueryBuilder,
where: () => createQueryBuilder,
getMany: () => [user],
};
jest
.spyOn(userRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
note.publicId = 'testId';
note.title = 'testTitle';
note.description = 'testDescription';
note.owner = Promise.resolve(user);
note.userPermissions = Promise.resolve([
{
id: 1,
note: Promise.resolve(note),
user: Promise.resolve(user),
canEdit: true,
},
]);
note.groupPermissions = Promise.resolve([
{
id: 1,
note: Promise.resolve(note),
group: Promise.resolve(group),
canEdit: true,
},
]);
note.tags = Promise.resolve([
{
id: 1,
name: 'testTag',
notes: Promise.resolve([note]),
},
]);
note.viewCount = 1337;
return [note, user, group];
}
function mockGroupRepo() {
jest.spyOn(groupRepo, 'findOne').mockReset();
jest.spyOn(groupRepo, 'findOne').mockImplementation((args) => {
const groupName = (args.where as FindOptionsWhere<Group>).name;
if (groupName === loggedin.name) {
return Promise.resolve(loggedin as Group);
} else if (groupName === everyone.name) {
return Promise.resolve(everyone as Group);
} else {
return Promise.resolve(null);
}
});
}
beforeEach(async () => { beforeEach(async () => {
jest.resetAllMocks();
jest.resetModules();
/** /**
* We need to have *one* userRepo for both the providers array and * We need to have *one* userRepo for both the providers array and
* the overrideProvider call, as otherwise we have two instances * the overrideProvider call, as otherwise we have two instances
@ -202,9 +118,19 @@ describe('NotesService', () => {
), ),
undefined, undefined,
); );
revisionsService = Mock.of<RevisionsService>({
getLatestRevision: jest.fn(),
createRevision: jest.fn(),
});
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [ providers: [
NotesService, NotesService,
{
provide: RevisionsService,
useValue: revisionsService,
},
AliasService, AliasService,
{ {
provide: getRepositoryToken(Note), provide: getRepositoryToken(Note),
@ -214,6 +140,10 @@ describe('NotesService', () => {
provide: getRepositoryToken(Tag), provide: getRepositoryToken(Tag),
useClass: Repository, useClass: Repository,
}, },
{
provide: getRepositoryToken(Revision),
useClass: Repository,
},
{ {
provide: getRepositoryToken(Alias), provide: getRepositoryToken(Alias),
useClass: Repository, useClass: Repository,
@ -280,12 +210,106 @@ describe('NotesService', () => {
loggedinDefaultAccessPermission = noteConfig.permissions.default.loggedIn; loggedinDefaultAccessPermission = noteConfig.permissions.default.loggedIn;
service = module.get<NotesService>(NotesService); service = module.get<NotesService>(NotesService);
noteRepo = module.get<Repository<Note>>(getRepositoryToken(Note)); noteRepo = module.get<Repository<Note>>(getRepositoryToken(Note));
revisionRepo = module.get<Repository<Revision>>(
getRepositoryToken(Revision),
);
eventEmitter = module.get<EventEmitter2>(EventEmitter2); eventEmitter = module.get<EventEmitter2>(EventEmitter2);
}); });
/**
* Creates a Note and a corresponding User and Group for testing.
* The Note does not have any aliases.
*/
async function getMockData(): Promise<[Note, User, Group, Revision]> {
const user = User.create('hardcoded', 'Testy') as User;
const author = Author.create(1);
author.user = Promise.resolve(user);
const group = Group.create('testGroup', 'testGroup', false) as Group;
jest
.spyOn(noteRepo, 'save')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(async (note: Note): Promise<Note> => note);
mockGroupRepo();
const revision = Mock.of<Revision>({
edits: Promise.resolve([
{
startPos: 0,
endPos: 1,
updatedAt: new Date(1549312452000),
author: Promise.resolve(author),
} as Edit,
{
startPos: 0,
endPos: 1,
updatedAt: new Date(1549312452001),
author: Promise.resolve(author),
} as Edit,
]),
createdAt: new Date(1549312452000),
tags: Promise.resolve([
{
id: 0,
name: 'tag1',
} as Tag,
]),
content: 'mockContent',
description: 'mockDescription',
title: 'mockTitle',
});
const note = Mock.of<Note>({
revisions: Promise.resolve([revision]),
aliases: Promise.resolve([]),
});
mockRevisionService(note, revision);
mockSelectQueryBuilderInRepo(userRepo, user);
note.publicId = 'testId';
note.owner = Promise.resolve(user);
note.userPermissions = Promise.resolve([
{
id: 1,
note: Promise.resolve(note),
user: Promise.resolve(user),
canEdit: true,
},
]);
note.groupPermissions = Promise.resolve([
{
id: 1,
note: Promise.resolve(note),
group: Promise.resolve(group),
canEdit: true,
},
]);
note.viewCount = 1337;
return [note, user, group, revision];
}
function mockRevisionService(note: Note, revision: Revision) {
jest
.spyOn(revisionsService, 'getLatestRevision')
.mockImplementation((requestedNote) => {
expect(requestedNote).toBe(note);
return Promise.resolve(revision);
});
}
function mockGroupRepo() {
jest.spyOn(groupRepo, 'findOne').mockReset();
jest.spyOn(groupRepo, 'findOne').mockImplementation((args) => {
const groupName = (args.where as FindOptionsWhere<Group>).name;
if (groupName === loggedin.name) {
return Promise.resolve(loggedin as Group);
} else if (groupName === everyone.name) {
return Promise.resolve(everyone as Group);
} else {
return Promise.resolve(null);
}
});
}
it('should be defined', () => { it('should be defined', () => {
expect(service).toBeDefined(); expect(service).toBeDefined();
}); });
@ -297,52 +321,19 @@ describe('NotesService', () => {
const note = Note.create(user, alias) as Note; const note = Note.create(user, alias) as Note;
it('with no note', async () => { it('with no note', async () => {
const createQueryBuilder = { mockSelectQueryBuilderInRepo(noteRepo, null);
leftJoinAndSelect: () => createQueryBuilder,
where: () => createQueryBuilder,
getMany: async () => {
return null;
},
};
jest
.spyOn(noteRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
const notes = await service.getUserNotes(user); const notes = await service.getUserNotes(user);
expect(notes).toEqual([]); expect(notes).toEqual([]);
}); });
it('with one note', async () => { it('with one note', async () => {
const createQueryBuilder = { mockSelectQueryBuilderInRepo(noteRepo, note);
leftJoinAndSelect: () => createQueryBuilder,
where: () => createQueryBuilder,
getMany: async () => {
return [note];
},
};
jest
.spyOn(noteRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
const notes = await service.getUserNotes(user); const notes = await service.getUserNotes(user);
expect(notes).toEqual([note]); expect(notes).toEqual([note]);
}); });
it('with multiple note', async () => { it('with multiple note', async () => {
const createQueryBuilder = { mockSelectQueryBuilderInRepo(noteRepo, [note, note]);
leftJoinAndSelect: () => createQueryBuilder,
where: () => createQueryBuilder,
getMany: async () => {
return [note, note];
},
};
jest
.spyOn(noteRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
const notes = await service.getUserNotes(user); const notes = await service.getUserNotes(user);
expect(notes).toEqual([note, note]); expect(notes).toEqual([note, note]);
}); });
@ -353,6 +344,9 @@ describe('NotesService', () => {
const user = User.create('hardcoded', 'Testy') as User; const user = User.create('hardcoded', 'Testy') as User;
const alias = 'alias'; const alias = 'alias';
const content = 'testContent'; const content = 'testContent';
const newRevision = Mock.of<Revision>({});
let createRevisionSpy: jest.SpyInstance;
describe('works', () => { describe('works', () => {
beforeEach(() => { beforeEach(() => {
jest jest
@ -361,12 +355,16 @@ describe('NotesService', () => {
// @ts-ignore // @ts-ignore
.mockImplementation(async (note: Note): Promise<Note> => note); .mockImplementation(async (note: Note): Promise<Note> => note);
mockGroupRepo(); mockGroupRepo();
createRevisionSpy = jest
.spyOn(revisionsService, 'createRevision')
.mockResolvedValue(newRevision);
}); });
it('without alias, without owner', async () => { it('without alias, without owner', async () => {
const newNote = await service.createNote(content, null); const newNote = await service.createNote(content, null);
const revisions = await newNote.revisions;
expect(revisions).toHaveLength(1); expect(createRevisionSpy).toHaveBeenCalledWith(newNote, content);
expect(revisions[0].content).toEqual(content); expect(await newNote.revisions).toStrictEqual([newRevision]);
expect(await newNote.historyEntries).toHaveLength(0); expect(await newNote.historyEntries).toHaveLength(0);
expect(await newNote.userPermissions).toHaveLength(0); expect(await newNote.userPermissions).toHaveLength(0);
const groupPermissions = await newNote.groupPermissions; const groupPermissions = await newNote.groupPermissions;
@ -383,15 +381,13 @@ describe('NotesService', () => {
expect((await groupPermissions[1].group).name).toEqual( expect((await groupPermissions[1].group).name).toEqual(
SpecialGroup.LOGGED_IN, SpecialGroup.LOGGED_IN,
); );
expect(await newNote.tags).toHaveLength(0);
expect(await newNote.owner).toBeNull(); expect(await newNote.owner).toBeNull();
expect(await newNote.aliases).toHaveLength(0); expect(await newNote.aliases).toHaveLength(0);
}); });
it('without alias, with owner', async () => { it('without alias, with owner', async () => {
const newNote = await service.createNote(content, user); const newNote = await service.createNote(content, user);
const revisions = await newNote.revisions; expect(createRevisionSpy).toHaveBeenCalledWith(newNote, content);
expect(revisions).toHaveLength(1); expect(await newNote.revisions).toStrictEqual([newRevision]);
expect(revisions[0].content).toEqual(content);
expect(await newNote.historyEntries).toHaveLength(1); expect(await newNote.historyEntries).toHaveLength(1);
expect(await (await newNote.historyEntries)[0].user).toEqual(user); expect(await (await newNote.historyEntries)[0].user).toEqual(user);
expect(await newNote.userPermissions).toHaveLength(0); expect(await newNote.userPermissions).toHaveLength(0);
@ -409,15 +405,13 @@ describe('NotesService', () => {
expect((await groupPermissions[1].group).name).toEqual( expect((await groupPermissions[1].group).name).toEqual(
SpecialGroup.LOGGED_IN, SpecialGroup.LOGGED_IN,
); );
expect(await newNote.tags).toHaveLength(0);
expect(await newNote.owner).toEqual(user); expect(await newNote.owner).toEqual(user);
expect(await newNote.aliases).toHaveLength(0); expect(await newNote.aliases).toHaveLength(0);
}); });
it('with alias, without owner', async () => { it('with alias, without owner', async () => {
const newNote = await service.createNote(content, null, alias); const newNote = await service.createNote(content, null, alias);
const revisions = await newNote.revisions; expect(createRevisionSpy).toHaveBeenCalledWith(newNote, content);
expect(revisions).toHaveLength(1); expect(await newNote.revisions).toStrictEqual([newRevision]);
expect(revisions[0].content).toEqual(content);
expect(await newNote.historyEntries).toHaveLength(0); expect(await newNote.historyEntries).toHaveLength(0);
expect(await newNote.userPermissions).toHaveLength(0); expect(await newNote.userPermissions).toHaveLength(0);
const groupPermissions = await newNote.groupPermissions; const groupPermissions = await newNote.groupPermissions;
@ -434,15 +428,14 @@ describe('NotesService', () => {
expect((await groupPermissions[1].group).name).toEqual( expect((await groupPermissions[1].group).name).toEqual(
SpecialGroup.LOGGED_IN, SpecialGroup.LOGGED_IN,
); );
expect(await newNote.tags).toHaveLength(0);
expect(await newNote.owner).toBeNull(); expect(await newNote.owner).toBeNull();
expect(await newNote.aliases).toHaveLength(1); expect(await newNote.aliases).toHaveLength(1);
}); });
it('with alias, with owner', async () => { it('with alias, with owner', async () => {
const newNote = await service.createNote(content, user, alias); const newNote = await service.createNote(content, user, alias);
const revisions = await newNote.revisions;
expect(revisions).toHaveLength(1); expect(createRevisionSpy).toHaveBeenCalledWith(newNote, content);
expect(revisions[0].content).toEqual(content); expect(await newNote.revisions).toStrictEqual([newRevision]);
expect(await newNote.historyEntries).toHaveLength(1); expect(await newNote.historyEntries).toHaveLength(1);
expect(await (await newNote.historyEntries)[0].user).toEqual(user); expect(await (await newNote.historyEntries)[0].user).toEqual(user);
expect(await newNote.userPermissions).toHaveLength(0); expect(await newNote.userPermissions).toHaveLength(0);
@ -460,7 +453,6 @@ describe('NotesService', () => {
expect((await groupPermissions[1].group).name).toEqual( expect((await groupPermissions[1].group).name).toEqual(
SpecialGroup.LOGGED_IN, SpecialGroup.LOGGED_IN,
); );
expect(await newNote.tags).toHaveLength(0);
expect(await newNote.owner).toEqual(user); expect(await newNote.owner).toEqual(user);
expect(await newNote.aliases).toHaveLength(1); expect(await newNote.aliases).toHaveLength(1);
expect((await newNote.aliases)[0].name).toEqual(alias); expect((await newNote.aliases)[0].name).toEqual(alias);
@ -470,9 +462,9 @@ describe('NotesService', () => {
it('and content has length maxDocumentLength', async () => { it('and content has length maxDocumentLength', async () => {
const content = 'x'.repeat(noteMockConfig.maxDocumentLength); const content = 'x'.repeat(noteMockConfig.maxDocumentLength);
const newNote = await service.createNote(content, user, alias); const newNote = await service.createNote(content, user, alias);
const revisions = await newNote.revisions;
expect(revisions).toHaveLength(1); expect(createRevisionSpy).toHaveBeenCalledWith(newNote, content);
expect(revisions[0].content).toEqual(content); expect(await newNote.revisions).toStrictEqual([newRevision]);
expect(await newNote.historyEntries).toHaveLength(1); expect(await newNote.historyEntries).toHaveLength(1);
expect(await (await newNote.historyEntries)[0].user).toEqual(user); expect(await (await newNote.historyEntries)[0].user).toEqual(user);
expect(await newNote.userPermissions).toHaveLength(0); expect(await newNote.userPermissions).toHaveLength(0);
@ -490,7 +482,6 @@ describe('NotesService', () => {
expect((await groupPermissions[1].group).name).toEqual( expect((await groupPermissions[1].group).name).toEqual(
SpecialGroup.LOGGED_IN, SpecialGroup.LOGGED_IN,
); );
expect(await newNote.tags).toHaveLength(0);
expect(await newNote.owner).toEqual(user); expect(await newNote.owner).toEqual(user);
expect(await newNote.aliases).toHaveLength(1); expect(await newNote.aliases).toHaveLength(1);
expect((await newNote.aliases)[0].name).toEqual(alias); expect((await newNote.aliases)[0].name).toEqual(alias);
@ -505,9 +496,9 @@ describe('NotesService', () => {
it('default permissions', async () => { it('default permissions', async () => {
mockGroupRepo(); mockGroupRepo();
const newNote = await service.createNote(content, user, alias); const newNote = await service.createNote(content, user, alias);
const revisions = await newNote.revisions;
expect(revisions).toHaveLength(1); expect(createRevisionSpy).toHaveBeenCalledWith(newNote, content);
expect(revisions[0].content).toEqual(content); expect(await newNote.revisions).toStrictEqual([newRevision]);
expect(await newNote.historyEntries).toHaveLength(1); expect(await newNote.historyEntries).toHaveLength(1);
expect(await (await newNote.historyEntries)[0].user).toEqual(user); expect(await (await newNote.historyEntries)[0].user).toEqual(user);
expect(await newNote.userPermissions).toHaveLength(0); expect(await newNote.userPermissions).toHaveLength(0);
@ -519,7 +510,6 @@ describe('NotesService', () => {
expect((await groupPermissions[0].group).name).toEqual( expect((await groupPermissions[0].group).name).toEqual(
SpecialGroup.LOGGED_IN, SpecialGroup.LOGGED_IN,
); );
expect(await newNote.tags).toHaveLength(0);
expect(await newNote.owner).toEqual(user); expect(await newNote.owner).toEqual(user);
expect(await newNote.aliases).toHaveLength(1); expect(await newNote.aliases).toHaveLength(1);
expect((await newNote.aliases)[0].name).toEqual(alias); expect((await newNote.aliases)[0].name).toEqual(alias);
@ -561,54 +551,25 @@ describe('NotesService', () => {
describe('getNoteContent', () => { describe('getNoteContent', () => {
it('works', async () => { it('works', async () => {
const content = 'testContent'; const content = 'testContent';
jest const revision = Mock.of<Revision>({ content: content });
.spyOn(noteRepo, 'save') const newNote = Mock.of<Note>();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment mockRevisionService(newNote, revision);
// @ts-ignore const result = await service.getNoteContent(newNote);
.mockImplementation(async (note: Note): Promise<Note> => note); expect(result).toEqual(content);
mockGroupRepo();
const newNote = await service.createNote(content, null);
const revisions = await newNote.revisions;
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revisions[0]);
await service.getNoteContent(newNote).then((result) => {
expect(result).toEqual(content);
});
}); });
}); });
describe('getNoteByIdOrAlias', () => { describe('getNoteByIdOrAlias', () => {
it('works', async () => { it('works', async () => {
const user = User.create('hardcoded', 'Testy') as User; const user = User.create('hardcoded', 'Testy') as User;
const note = Note.create(user); const note = Note.create(user) as Note;
const createQueryBuilder = { mockSelectQueryBuilderInRepo(noteRepo, note);
leftJoinAndSelect: () => createQueryBuilder,
where: () => createQueryBuilder,
orWhere: () => createQueryBuilder,
setParameter: () => createQueryBuilder,
getOne: () => note,
};
jest
.spyOn(noteRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
const foundNote = await service.getNoteByIdOrAlias('noteThatExists'); const foundNote = await service.getNoteByIdOrAlias('noteThatExists');
expect(foundNote).toEqual(note); expect(foundNote).toEqual(note);
}); });
describe('fails:', () => { describe('fails:', () => {
it('no note found', async () => { it('no note found', async () => {
const createQueryBuilder = { mockSelectQueryBuilderInRepo(noteRepo, null);
leftJoinAndSelect: () => createQueryBuilder,
where: () => createQueryBuilder,
orWhere: () => createQueryBuilder,
setParameter: () => createQueryBuilder,
getOne: () => null,
};
jest
.spyOn(noteRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilder);
await expect( await expect(
service.getNoteByIdOrAlias('noteThatDoesNoteExist'), service.getNoteByIdOrAlias('noteThatDoesNoteExist'),
).rejects.toThrow(NotInDBError); ).rejects.toThrow(NotInDBError);
@ -644,81 +605,111 @@ describe('NotesService', () => {
}); });
describe('updateNote', () => { describe('updateNote', () => {
it('works', async () => { it('adds a new revision if content is different', async () => {
const [note, ,] = await getMockData(); const [note, , , revision] = await getMockData();
const revisionLength = (await note.revisions).length;
const updatedNote = await service.updateNote(note, 'newContent');
expect(await updatedNote.revisions).toHaveLength(revisionLength + 1);
});
});
describe('toTagList', () => { const mockRevision = Mock.of<Revision>({});
it('works', async () => { const createRevisionSpy = jest
const note = {} as Note; .spyOn(revisionsService, 'createRevision')
note.tags = Promise.resolve([ .mockReturnValue(Promise.resolve(mockRevision));
{
id: 1, const newContent = 'newContent';
name: 'testTag', const updatedNote = await service.updateNote(note, newContent);
notes: Promise.resolve([note]), expect(await updatedNote.revisions).toStrictEqual([
}, revision,
mockRevision,
]); ]);
const tagList = await service.toTagList(note); expect(createRevisionSpy).toHaveBeenCalledWith(note, newContent);
expect(tagList).toHaveLength(1); });
expect(tagList[0]).toEqual((await note.tags)[0].name);
it("won't create a new revision if content is same", async () => {
const [note, , , revision] = await getMockData();
const createRevisionSpy = jest
.spyOn(revisionsService, 'createRevision')
.mockReturnValue(Promise.resolve(undefined));
const newContent = 'newContent';
const updatedNote = await service.updateNote(note, newContent);
expect(await updatedNote.revisions).toStrictEqual([revision]);
expect(createRevisionSpy).toHaveBeenCalledWith(note, newContent);
}); });
}); });
describe('toNotePermissionsDto', () => { describe('toNotePermissionsDto', () => {
it('works', async () => { it('works', async () => {
const [note, user, group] = await getMockData(); const [note] = await getMockData();
const permissions = await service.toNotePermissionsDto(note); const permissions = await service.toNotePermissionsDto(note);
expect(permissions.owner).toEqual(user.username); expect(permissions).toMatchInlineSnapshot(`
expect(permissions.sharedToUsers).toHaveLength(1); {
expect(permissions.sharedToUsers[0].username).toEqual(user.username); "owner": "hardcoded",
expect(permissions.sharedToUsers[0].canEdit).toEqual(true); "sharedToGroups": [
expect(permissions.sharedToGroups).toHaveLength(1); {
expect(permissions.sharedToGroups[0].groupName).toEqual( "canEdit": true,
group.displayName, "groupName": "testGroup",
); },
expect(permissions.sharedToGroups[0].canEdit).toEqual(true); ],
"sharedToUsers": [
{
"canEdit": true,
"username": "hardcoded",
},
],
}
`);
}); });
}); });
describe('toNoteMetadataDto', () => { describe('toNoteMetadataDto', () => {
it('works', async () => { it('works', async () => {
const [note, user, group] = await getMockData(); const [note] = await getMockData();
note.aliases = Promise.resolve([ note.aliases = Promise.resolve([
Alias.create('testAlias', note, true) as Alias, Alias.create('testAlias', note, true) as Alias,
]); ]);
const metadataDto = await service.toNoteMetadataDto(note); const metadataDto = await service.toNoteMetadataDto(note);
expect(metadataDto.id).toEqual(note.publicId); expect(metadataDto).toMatchInlineSnapshot(`
expect(metadataDto.aliases).toHaveLength(1); {
expect(metadataDto.aliases[0].name).toEqual((await note.aliases)[0].name); "aliases": [
expect(metadataDto.primaryAddress).toEqual('testAlias'); {
expect(metadataDto.title).toEqual(note.title); "name": "testAlias",
expect(metadataDto.description).toEqual(note.description); "noteId": "testId",
expect(metadataDto.editedBy).toHaveLength(1); "primaryAlias": true,
expect(metadataDto.editedBy[0]).toEqual(user.username); },
expect(metadataDto.permissions.owner).toEqual(user.username); ],
expect(metadataDto.permissions.sharedToUsers).toHaveLength(1); "createdAt": undefined,
expect(metadataDto.permissions.sharedToUsers[0].username).toEqual( "description": "mockDescription",
user.username, "editedBy": [
); "hardcoded",
expect(metadataDto.permissions.sharedToUsers[0].canEdit).toEqual(true); ],
expect(metadataDto.permissions.sharedToGroups).toHaveLength(1); "id": "testId",
expect(metadataDto.permissions.sharedToGroups[0].groupName).toEqual( "permissions": {
group.displayName, "owner": "hardcoded",
); "sharedToGroups": [
expect(metadataDto.permissions.sharedToGroups[0].canEdit).toEqual(true); {
expect(metadataDto.tags).toHaveLength(1); "canEdit": true,
expect(metadataDto.tags[0]).toEqual((await note.tags)[0].name); "groupName": "testGroup",
expect(metadataDto.updatedAt).toEqual( },
(await note.revisions)[0].createdAt, ],
); "sharedToUsers": [
expect(metadataDto.updateUsername).toEqual(user.username); {
expect(metadataDto.viewCount).toEqual(note.viewCount); "canEdit": true,
"username": "hardcoded",
},
],
},
"primaryAddress": "testAlias",
"tags": [
"tag1",
],
"title": "mockTitle",
"updateUsername": "hardcoded",
"updatedAt": 2019-02-04T20:34:12.000Z,
"version": undefined,
"viewCount": 1337,
}
`);
}); });
it('returns publicId if no alias exists', async () => { it('returns publicId if no alias exists', async () => {
const [note, ,] = await getMockData(); const [note, ,] = await getMockData();
const metadataDto = await service.toNoteMetadataDto(note); const metadataDto = await service.toNoteMetadataDto(note);
@ -728,41 +719,57 @@ describe('NotesService', () => {
describe('toNoteDto', () => { describe('toNoteDto', () => {
it('works', async () => { it('works', async () => {
const [note, user, group] = await getMockData(); const [note] = await getMockData();
note.aliases = Promise.resolve([ note.aliases = Promise.resolve([
Alias.create('testAlias', note, true) as Alias, Alias.create('testAlias', note, true) as Alias,
]); ]);
const noteDto = await service.toNoteDto(note); const noteDto = await service.toNoteDto(note);
expect(noteDto.metadata.id).toEqual(note.publicId); expect(noteDto).toMatchInlineSnapshot(`
expect(noteDto.metadata.aliases).toHaveLength(1); {
expect(noteDto.metadata.aliases[0].name).toEqual( "content": "mockContent",
(await note.aliases)[0].name, "editedByAtPosition": [],
); "metadata": {
expect(noteDto.metadata.title).toEqual(note.title); "aliases": [
expect(noteDto.metadata.description).toEqual(note.description); {
expect(noteDto.metadata.editedBy).toHaveLength(1); "name": "testAlias",
expect(noteDto.metadata.editedBy[0]).toEqual(user.username); "noteId": "testId",
expect(noteDto.metadata.permissions.owner).toEqual(user.username); "primaryAlias": true,
expect(noteDto.metadata.permissions.sharedToUsers).toHaveLength(1); },
expect(noteDto.metadata.permissions.sharedToUsers[0].username).toEqual( ],
user.username, "createdAt": undefined,
); "description": "mockDescription",
expect(noteDto.metadata.permissions.sharedToUsers[0].canEdit).toEqual( "editedBy": [
true, "hardcoded",
); ],
expect(noteDto.metadata.permissions.sharedToGroups).toHaveLength(1); "id": "testId",
expect(noteDto.metadata.permissions.sharedToGroups[0].groupName).toEqual( "permissions": {
group.displayName, "owner": "hardcoded",
); "sharedToGroups": [
expect(noteDto.metadata.permissions.sharedToGroups[0].canEdit).toEqual( {
true, "canEdit": true,
); "groupName": "testGroup",
expect(noteDto.metadata.tags).toHaveLength(1); },
expect(noteDto.metadata.tags[0]).toEqual((await note.tags)[0].name); ],
expect(noteDto.metadata.updateUsername).toEqual(user.username); "sharedToUsers": [
expect(noteDto.metadata.viewCount).toEqual(note.viewCount); {
expect(noteDto.content).toEqual('testContent'); "canEdit": true,
"username": "hardcoded",
},
],
},
"primaryAddress": "testAlias",
"tags": [
"tag1",
],
"title": "mockTitle",
"updateUsername": "hardcoded",
"updatedAt": 2019-02-04T20:34:12.000Z,
"version": undefined,
"viewCount": 1337,
},
}
`);
}); });
}); });
}); });

View file

@ -104,9 +104,9 @@ describe('RevisionsService', () => {
describe('getRevision', () => { describe('getRevision', () => {
it('returns a revision', async () => { it('returns a revision', async () => {
const note = Mock.of<Note>({}); const note = Mock.of<Note>({});
const revision = Revision.create('', '', note) as Revision; const revision = Mock.of<Revision>({});
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revision); jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revision);
expect(await service.getRevision({} as Note, 1)).toEqual(revision); expect(await service.getRevision(note, 1)).toBe(revision);
}); });
it('throws if the revision is not in the databse', async () => { it('throws if the revision is not in the databse', async () => {
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(null); jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(null);
@ -117,54 +117,55 @@ describe('RevisionsService', () => {
}); });
describe('purgeRevisions', () => { describe('purgeRevisions', () => {
let revisions: Revision[];
let note: Note;
beforeEach(() => {
note = Mock.of<Note>({});
revisions = [];
jest
.spyOn(revisionRepo, 'remove')
.mockImplementation(
<T extends Revision | Revision[]>(deleteEntities: T): Promise<T> => {
const newRevisions = revisions.filter((item: Revision) =>
Array.isArray(deleteEntities)
? !deleteEntities.includes(item)
: deleteEntities !== item,
);
revisions = newRevisions;
note.revisions = Promise.resolve(newRevisions);
return Promise.resolve(deleteEntities);
},
);
});
it('purges the revision history', async () => { it('purges the revision history', async () => {
const note = {} as Note; const revision1 = Mock.of<Revision>({ id: 1 });
note.id = 4711; const revision2 = Mock.of<Revision>({ id: 2 });
let revisions: Revision[] = []; const revision3 = Mock.of<Revision>({ id: 3 });
const revision1 = Revision.create('a', 'a', note) as Revision; revisions = [revision1, revision2, revision3];
revision1.id = 1;
const revision2 = Revision.create('b', 'b', note) as Revision;
revision2.id = 2;
const revision3 = Revision.create('c', 'c', note) as Revision;
revision3.id = 3;
revisions.push(revision1, revision2, revision3);
note.revisions = Promise.resolve(revisions); note.revisions = Promise.resolve(revisions);
jest.spyOn(revisionRepo, 'find').mockResolvedValueOnce(revisions); jest.spyOn(revisionRepo, 'find').mockResolvedValueOnce(revisions);
jest.spyOn(service, 'getLatestRevision').mockResolvedValueOnce(revision3); jest.spyOn(service, 'getLatestRevision').mockResolvedValueOnce(revision3);
revisionRepo.remove = jest
.fn()
.mockImplementation((deleteList: Revision[]) => {
revisions = revisions.filter(
(item: Revision) => !deleteList.includes(item),
);
return Promise.resolve(deleteList);
});
// expected to return all the purged revisions // expected to return all the purged revisions
expect(await service.purgeRevisions(note)).toHaveLength(2); expect(await service.purgeRevisions(note)).toStrictEqual([
revision1,
revision2,
]);
// expected to have only the latest revision // expected to have only the latest revision
const updatedRevisions: Revision[] = [revision3]; expect(revisions).toStrictEqual([revision3]);
expect(revisions).toEqual(updatedRevisions);
}); });
it('has no effect on revision history when a single revision is present', async () => { it('has no effect on revision history when a single revision is present', async () => {
const note = {} as Note; const revision1 = Mock.of<Revision>({ id: 1 });
note.id = 4711; revisions = [revision1];
let revisions: Revision[] = [];
const revision1 = Revision.create('a', 'a', note) as Revision;
revision1.id = 1;
revisions.push(revision1);
note.revisions = Promise.resolve(revisions); note.revisions = Promise.resolve(revisions);
jest.spyOn(revisionRepo, 'find').mockResolvedValueOnce(revisions); jest.spyOn(revisionRepo, 'find').mockResolvedValueOnce(revisions);
jest.spyOn(service, 'getLatestRevision').mockResolvedValueOnce(revision1); jest.spyOn(service, 'getLatestRevision').mockResolvedValueOnce(revision1);
revisionRepo.remove = jest
.fn()
.mockImplementation((deleteList: Revision[]) => {
revisions = revisions.filter(
(item: Revision) => !deleteList.includes(item),
);
return Promise.resolve(deleteList);
});
// expected to return all the purged revisions // expected to return all the purged revisions
expect(await service.purgeRevisions(note)).toHaveLength(0); expect(await service.purgeRevisions(note)).toHaveLength(0);
@ -188,7 +189,7 @@ describe('RevisionsService', () => {
edits.push(Edit.create(anonAuthor, 29, 20) as Edit); edits.push(Edit.create(anonAuthor, 29, 20) as Edit);
edits.push(Edit.create(anonAuthor, 29, 20) as Edit); edits.push(Edit.create(anonAuthor, 29, 20) as Edit);
edits.push(Edit.create(anonAuthor2, 29, 20) as Edit); edits.push(Edit.create(anonAuthor2, 29, 20) as Edit);
const revision = Revision.create('', '', {} as Note) as Revision; const revision = Mock.of<Revision>({});
revision.edits = Promise.resolve(edits); revision.edits = Promise.resolve(edits);
const userInfo = await service.getRevisionUserInfo(revision); const userInfo = await service.getRevisionUserInfo(revision);
@ -197,13 +198,122 @@ describe('RevisionsService', () => {
}); });
}); });
describe('toRevisionMetadataDto', () => {
it('converts a revision', async () => {
const revision = Mock.of<Revision>({
id: 3246,
content: 'mockContent',
length: 1854,
createdAt: new Date('2020-05-20T09:58:00.000Z'),
title: 'mockTitle',
tags: Promise.resolve([Mock.of<Tag>({ name: 'mockTag' })]),
description: 'mockDescription',
patch: 'mockPatch',
edits: Promise.resolve([
Mock.of<Edit>({
endPos: 93,
startPos: 34,
createdAt: new Date('2020-03-04T20:12:00.000Z'),
updatedAt: new Date('2021-12-10T09:45:00.000Z'),
author: Promise.resolve(
Mock.of<Author>({
user: Promise.resolve(
Mock.of<User>({
username: 'mockusername',
}),
),
}),
),
}),
]),
});
expect(await service.toRevisionMetadataDto(revision))
.toMatchInlineSnapshot(`
{
"anonymousAuthorCount": 0,
"authorUsernames": [
"mockusername",
],
"createdAt": 2020-05-20T09:58:00.000Z,
"description": "mockDescription",
"id": 3246,
"length": 1854,
"tags": [
"mockTag",
],
"title": "mockTitle",
}
`);
});
});
describe('toRevisionDto', () => {
it('converts a revision', async () => {
const revision = Mock.of<Revision>({
id: 3246,
content: 'mockContent',
length: 1854,
createdAt: new Date('2020-05-20T09:58:00.000Z'),
title: 'mockTitle',
tags: Promise.resolve([Mock.of<Tag>({ name: 'mockTag' })]),
description: 'mockDescription',
patch: 'mockPatch',
edits: Promise.resolve([
Mock.of<Edit>({
endPos: 93,
startPos: 34,
createdAt: new Date('2020-03-04T22:32:00.000Z'),
updatedAt: new Date('2021-02-10T12:23:00.000Z'),
author: Promise.resolve(
Mock.of<Author>({
user: Promise.resolve(
Mock.of<User>({
username: 'mockusername',
}),
),
}),
),
}),
]),
});
expect(await service.toRevisionDto(revision)).toMatchInlineSnapshot(`
{
"anonymousAuthorCount": 0,
"authorUsernames": [
"mockusername",
],
"content": "mockContent",
"createdAt": 2020-05-20T09:58:00.000Z,
"description": "mockDescription",
"edits": [
{
"createdAt": 2020-03-04T22:32:00.000Z,
"endPos": 93,
"startPos": 34,
"updatedAt": 2021-02-10T12:23:00.000Z,
"username": "mockusername",
},
],
"id": 3246,
"length": 1854,
"patch": "mockPatch",
"tags": [
"mockTag",
],
"title": "mockTitle",
}
`);
});
});
describe('createRevision', () => { describe('createRevision', () => {
it('creates a new revision', async () => { it('creates a new revision', async () => {
const note = Mock.of<Note>({ publicId: 'test-note' }); const note = Mock.of<Note>({ publicId: 'test-note', id: 1 });
const oldContent = 'old content\n'; const oldContent = 'old content\n';
const newContent = 'new content\n'; const newContent =
'---\ntitle: new title\ndescription: new description\ntags: [ "tag1" ]\n---\nnew content\n';
const oldRevision = Mock.of<Revision>({ content: oldContent }); const oldRevision = Mock.of<Revision>({ content: oldContent, id: 1 });
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(oldRevision); jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(oldRevision);
jest jest
.spyOn(revisionRepo, 'save') .spyOn(revisionRepo, 'save')
@ -214,24 +324,38 @@ describe('RevisionsService', () => {
const createdRevision = await service.createRevision(note, newContent); const createdRevision = await service.createRevision(note, newContent);
expect(createdRevision).not.toBeUndefined(); expect(createdRevision).not.toBeUndefined();
expect(createdRevision?.content).toBe(newContent); expect(createdRevision?.content).toBe(newContent);
await expect(createdRevision?.tags).resolves.toMatchInlineSnapshot(`
[
Tag {
"name": "tag1",
},
]
`);
expect(createdRevision?.title).toBe('new title');
expect(createdRevision?.description).toBe('new description');
await expect(createdRevision?.note).resolves.toBe(note); await expect(createdRevision?.note).resolves.toBe(note);
expect(createdRevision?.patch).toMatchInlineSnapshot(` expect(createdRevision?.patch).toMatchInlineSnapshot(`
"Index: test-note "Index: test-note
=================================================================== ===================================================================
--- test-note --- test-note
+++ test-note +++ test-note
@@ -1,1 +1,1 @@ @@ -1,1 +1,6 @@
-old content -old content
+---
+title: new title
+description: new description
+tags: [ "tag1" ]
+---
+new content +new content
" "
`); `);
}); });
it("won't create a revision if content is unchanged", async () => { it("won't create a revision if content is unchanged", async () => {
const note = Mock.of<Note>({}); const note = Mock.of<Note>({ id: 1 });
const oldContent = 'old content\n'; const oldContent = 'old content\n';
const oldRevision = Mock.of<Revision>({ content: oldContent }); const oldRevision = Mock.of<Revision>({ content: oldContent, id: 1 });
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(oldRevision); jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(oldRevision);
const saveSpy = jest.spyOn(revisionRepo, 'save').mockImplementation(); const saveSpy = jest.spyOn(revisionRepo, 'save').mockImplementation();