mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-24 18:56:32 -05:00
refactor: deduplicate code in history service unit test
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
57365bb727
commit
d9ef44766d
3 changed files with 82 additions and 161 deletions
|
@ -1,6 +1,9 @@
|
||||||
# SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
# SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- "src/utils/test-utils"
|
||||||
|
|
||||||
codecov:
|
codecov:
|
||||||
notify:
|
notify:
|
||||||
# We currently have integration tests and E2E tests. Codecov should wait until both are done.
|
# We currently have integration tests and E2E tests. Codecov should wait until both are done.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
|
import { getDataSourceToken, 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';
|
||||||
|
@ -28,6 +29,7 @@ import { Revision } from '../revisions/revision.entity';
|
||||||
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 { HistoryEntryImportDto } from './history-entry-import.dto';
|
import { HistoryEntryImportDto } from './history-entry-import.dto';
|
||||||
import { HistoryEntry } from './history-entry.entity';
|
import { HistoryEntry } from './history-entry.entity';
|
||||||
import { HistoryService } from './history.service';
|
import { HistoryService } from './history.service';
|
||||||
|
@ -35,18 +37,11 @@ import { HistoryService } from './history.service';
|
||||||
describe('HistoryService', () => {
|
describe('HistoryService', () => {
|
||||||
let service: HistoryService;
|
let service: HistoryService;
|
||||||
let historyRepo: Repository<HistoryEntry>;
|
let historyRepo: Repository<HistoryEntry>;
|
||||||
let dataSource: DataSource;
|
|
||||||
let noteRepo: Repository<Note>;
|
let noteRepo: Repository<Note>;
|
||||||
|
let mockedTransaction: jest.Mock<
|
||||||
type MockConnection = {
|
Promise<void>,
|
||||||
transaction: () => void;
|
[(entityManager: EntityManager) => Promise<void>]
|
||||||
};
|
>;
|
||||||
|
|
||||||
function mockConnection(): MockConnection {
|
|
||||||
return {
|
|
||||||
transaction: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
noteRepo = new Repository<Note>(
|
noteRepo = new Repository<Note>(
|
||||||
|
@ -64,7 +59,12 @@ describe('HistoryService', () => {
|
||||||
HistoryService,
|
HistoryService,
|
||||||
{
|
{
|
||||||
provide: getDataSourceToken(),
|
provide: getDataSourceToken(),
|
||||||
useFactory: mockConnection,
|
useFactory: () => {
|
||||||
|
mockedTransaction = jest.fn();
|
||||||
|
return Mock.of<DataSource>({
|
||||||
|
transaction: mockedTransaction,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(HistoryEntry),
|
provide: getRepositoryToken(HistoryEntry),
|
||||||
|
@ -117,7 +117,6 @@ describe('HistoryService', () => {
|
||||||
historyRepo = module.get<Repository<HistoryEntry>>(
|
historyRepo = module.get<Repository<HistoryEntry>>(
|
||||||
getRepositoryToken(HistoryEntry),
|
getRepositoryToken(HistoryEntry),
|
||||||
);
|
);
|
||||||
dataSource = module.get<DataSource>(DataSource);
|
|
||||||
noteRepo = module.get<Repository<Note>>(getRepositoryToken(Note));
|
noteRepo = module.get<Repository<Note>>(getRepositoryToken(Note));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -163,23 +162,11 @@ describe('HistoryService', () => {
|
||||||
Note.create(user, alias) as Note,
|
Note.create(user, alias) as Note,
|
||||||
) as HistoryEntry;
|
) as HistoryEntry;
|
||||||
it('without an preexisting entry', async () => {
|
it('without an preexisting entry', async () => {
|
||||||
const createQueryBuilder = {
|
mockSelectQueryBuilderInRepo(historyRepo, null);
|
||||||
where: () => createQueryBuilder,
|
|
||||||
andWhere: () => createQueryBuilder,
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
|
||||||
getOne: async () => {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(historyRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
jest
|
jest
|
||||||
.spyOn(historyRepo, 'save')
|
.spyOn(historyRepo, 'save')
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
async (entry): Promise<HistoryEntry> => entry as HistoryEntry,
|
||||||
);
|
);
|
||||||
const createHistoryEntry = await service.updateHistoryEntryTimestamp(
|
const createHistoryEntry = await service.updateHistoryEntryTimestamp(
|
||||||
Note.create(user, alias) as Note,
|
Note.create(user, alias) as Note,
|
||||||
|
@ -193,23 +180,11 @@ describe('HistoryService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with an preexisting entry', async () => {
|
it('with an preexisting entry', async () => {
|
||||||
const createQueryBuilder = {
|
mockSelectQueryBuilderInRepo(historyRepo, historyEntry);
|
||||||
where: () => createQueryBuilder,
|
|
||||||
andWhere: () => createQueryBuilder,
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
|
||||||
getOne: async () => {
|
|
||||||
return historyEntry;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(historyRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
jest
|
jest
|
||||||
.spyOn(historyRepo, 'save')
|
.spyOn(historyRepo, 'save')
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
async (entry): Promise<HistoryEntry> => entry as HistoryEntry,
|
||||||
);
|
);
|
||||||
const createHistoryEntry = await service.updateHistoryEntryTimestamp(
|
const createHistoryEntry = await service.updateHistoryEntryTimestamp(
|
||||||
Note.create(user, alias) as Note,
|
Note.create(user, alias) as Note,
|
||||||
|
@ -232,39 +207,16 @@ describe('HistoryService', () => {
|
||||||
const alias = 'alias';
|
const alias = 'alias';
|
||||||
const note = Note.create(user, alias) as Note;
|
const note = Note.create(user, alias) as Note;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
describe('works', () => {
|
describe('works', () => {
|
||||||
it('with an entry', async () => {
|
it('with an entry', async () => {
|
||||||
const historyEntry = HistoryEntry.create(user, note) as HistoryEntry;
|
const historyEntry = HistoryEntry.create(user, note) as HistoryEntry;
|
||||||
const createQueryBuilder = {
|
mockSelectQueryBuilderInRepo(historyRepo, historyEntry);
|
||||||
where: () => createQueryBuilder,
|
|
||||||
andWhere: () => createQueryBuilder,
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
|
||||||
getOne: async () => {
|
|
||||||
return historyEntry;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(historyRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
jest
|
jest
|
||||||
.spyOn(historyRepo, 'save')
|
.spyOn(historyRepo, 'save')
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
async (entry): Promise<HistoryEntry> => entry as HistoryEntry,
|
||||||
);
|
);
|
||||||
const updatedHistoryEntry = await service.updateHistoryEntry(
|
const updatedHistoryEntry = await service.updateHistoryEntry(
|
||||||
note,
|
note,
|
||||||
|
@ -281,19 +233,7 @@ describe('HistoryService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('without an entry', async () => {
|
it('without an entry', async () => {
|
||||||
const createQueryBuilder = {
|
mockSelectQueryBuilderInRepo(historyRepo, null);
|
||||||
where: () => createQueryBuilder,
|
|
||||||
andWhere: () => createQueryBuilder,
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
|
||||||
getOne: async () => {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(historyRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
await expect(
|
await expect(
|
||||||
service.updateHistoryEntry(note, user, {
|
service.updateHistoryEntry(note, user, {
|
||||||
pinStatus: true,
|
pinStatus: true,
|
||||||
|
@ -359,31 +299,8 @@ describe('HistoryService', () => {
|
||||||
const alias = 'alias';
|
const alias = 'alias';
|
||||||
const note = Note.create(user, alias) as Note;
|
const note = Note.create(user, alias) as Note;
|
||||||
const historyEntry = HistoryEntry.create(user, note) as HistoryEntry;
|
const historyEntry = HistoryEntry.create(user, note) as HistoryEntry;
|
||||||
const historyQueryBuilder = {
|
mockSelectQueryBuilderInRepo(historyRepo, historyEntry);
|
||||||
where: () => historyQueryBuilder,
|
mockSelectQueryBuilderInRepo(noteRepo, note);
|
||||||
andWhere: () => historyQueryBuilder,
|
|
||||||
leftJoinAndSelect: () => historyQueryBuilder,
|
|
||||||
getOne: async () => {
|
|
||||||
return historyEntry;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(historyRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => historyQueryBuilder);
|
|
||||||
const noteQueryBuilder = {
|
|
||||||
leftJoinAndSelect: () => noteQueryBuilder,
|
|
||||||
where: () => noteQueryBuilder,
|
|
||||||
orWhere: () => noteQueryBuilder,
|
|
||||||
setParameter: () => noteQueryBuilder,
|
|
||||||
getOne: () => note,
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(noteRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => noteQueryBuilder);
|
|
||||||
jest
|
jest
|
||||||
.spyOn(historyRepo, 'remove')
|
.spyOn(historyRepo, 'remove')
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
|
@ -400,31 +317,9 @@ describe('HistoryService', () => {
|
||||||
const alias = 'alias';
|
const alias = 'alias';
|
||||||
it('without an entry', async () => {
|
it('without an entry', async () => {
|
||||||
const note = Note.create(user, alias) as Note;
|
const note = Note.create(user, alias) as Note;
|
||||||
const createQueryBuilder = {
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
mockSelectQueryBuilderInRepo(historyRepo, null);
|
||||||
where: () => createQueryBuilder,
|
mockSelectQueryBuilderInRepo(noteRepo, note);
|
||||||
orWhere: () => createQueryBuilder,
|
|
||||||
setParameter: () => createQueryBuilder,
|
|
||||||
getOne: () => note,
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(noteRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
const historyQueryBuilder = {
|
|
||||||
where: () => historyQueryBuilder,
|
|
||||||
andWhere: () => historyQueryBuilder,
|
|
||||||
leftJoinAndSelect: () => historyQueryBuilder,
|
|
||||||
getOne: async () => {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(historyRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => historyQueryBuilder);
|
|
||||||
await expect(service.deleteHistoryEntry(note, user)).rejects.toThrow(
|
await expect(service.deleteHistoryEntry(note, user)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
@ -448,19 +343,9 @@ describe('HistoryService', () => {
|
||||||
pinStatus: historyEntryImport.pinStatus,
|
pinStatus: historyEntryImport.pinStatus,
|
||||||
updatedAt: historyEntryImport.lastVisitedAt,
|
updatedAt: historyEntryImport.lastVisitedAt,
|
||||||
};
|
};
|
||||||
const createQueryBuilder = {
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
const createQueryBuilder = mockSelectQueryBuilderInRepo(noteRepo, note);
|
||||||
where: () => createQueryBuilder,
|
const mockedManager = Mock.of<EntityManager>({
|
||||||
orWhere: () => createQueryBuilder,
|
|
||||||
setParameter: () => createQueryBuilder,
|
|
||||||
getOne: () => note,
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(noteRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
const mockedManager = {
|
|
||||||
find: jest.fn().mockResolvedValueOnce([historyEntry]),
|
find: jest.fn().mockResolvedValueOnce([historyEntry]),
|
||||||
createQueryBuilder: () => createQueryBuilder,
|
createQueryBuilder: () => createQueryBuilder,
|
||||||
remove: jest
|
remove: jest
|
||||||
|
@ -477,14 +362,8 @@ describe('HistoryService', () => {
|
||||||
expect(entry.pinStatus).toEqual(newlyCreatedHistoryEntry.pinStatus);
|
expect(entry.pinStatus).toEqual(newlyCreatedHistoryEntry.pinStatus);
|
||||||
expect(entry.updatedAt).toEqual(newlyCreatedHistoryEntry.updatedAt);
|
expect(entry.updatedAt).toEqual(newlyCreatedHistoryEntry.updatedAt);
|
||||||
}),
|
}),
|
||||||
};
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
||||||
dataSource.transaction.mockImplementation((cb) => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
||||||
cb(mockedManager);
|
|
||||||
});
|
});
|
||||||
|
mockedTransaction.mockImplementation((cb) => cb(mockedManager));
|
||||||
await service.setHistory(user, [historyEntryImport]);
|
await service.setHistory(user, [historyEntryImport]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -507,17 +386,8 @@ describe('HistoryService', () => {
|
||||||
);
|
);
|
||||||
const historyEntry = HistoryEntry.create(user, note) as HistoryEntry;
|
const historyEntry = HistoryEntry.create(user, note) as HistoryEntry;
|
||||||
historyEntry.pinStatus = true;
|
historyEntry.pinStatus = true;
|
||||||
const createQueryBuilder = {
|
|
||||||
leftJoinAndSelect: () => createQueryBuilder,
|
mockSelectQueryBuilderInRepo(noteRepo, note);
|
||||||
where: () => createQueryBuilder,
|
|
||||||
orWhere: () => createQueryBuilder,
|
|
||||||
getOne: () => note,
|
|
||||||
};
|
|
||||||
jest
|
|
||||||
.spyOn(noteRepo, 'createQueryBuilder')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
.mockImplementation(() => createQueryBuilder);
|
|
||||||
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);
|
||||||
|
|
48
src/utils/test-utils/mockSelectQueryBuilder.ts
Normal file
48
src/utils/test-utils/mockSelectQueryBuilder.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
|
import { Repository, SelectQueryBuilder } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks a {@link SelectQueryBuilder} that returns a given entity.
|
||||||
|
*
|
||||||
|
* @param returnValue The entity to return
|
||||||
|
* @return The mocked query builder
|
||||||
|
*/
|
||||||
|
export function mockSelectQueryBuilder<T>(
|
||||||
|
returnValue: T | null,
|
||||||
|
): SelectQueryBuilder<T> {
|
||||||
|
const mockedQueryBuilder: SelectQueryBuilder<T> = Mock.of<
|
||||||
|
SelectQueryBuilder<T>
|
||||||
|
>({
|
||||||
|
where: () => mockedQueryBuilder,
|
||||||
|
andWhere: () => mockedQueryBuilder,
|
||||||
|
leftJoinAndSelect: () => mockedQueryBuilder,
|
||||||
|
getOne: () => Promise.resolve(returnValue),
|
||||||
|
orWhere: () => mockedQueryBuilder,
|
||||||
|
setParameter: () => mockedQueryBuilder,
|
||||||
|
});
|
||||||
|
return mockedQueryBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks an {@link SelectQueryBuilder} and injects it into the given {@link Repository}.
|
||||||
|
*
|
||||||
|
* @param repository The repository whose query builder function should be mocked
|
||||||
|
* @param returnValue The value that should be found by the query builder
|
||||||
|
* @return The mocked query builder
|
||||||
|
* @see mockSelectQueryBuilder
|
||||||
|
*/
|
||||||
|
export function mockSelectQueryBuilderInRepo<T>(
|
||||||
|
repository: Repository<T>,
|
||||||
|
returnValue: T | null,
|
||||||
|
): SelectQueryBuilder<T> {
|
||||||
|
const selectQueryBuilder = mockSelectQueryBuilder(returnValue);
|
||||||
|
jest
|
||||||
|
.spyOn(repository, 'createQueryBuilder')
|
||||||
|
.mockImplementation(() => selectQueryBuilder);
|
||||||
|
return selectQueryBuilder;
|
||||||
|
}
|
Loading…
Reference in a new issue