DIRTY: Mock Repository

Signed-off-by: Yannick Bungers <git@innay.de>
This commit is contained in:
Yannick Bungers 2022-11-17 10:11:35 +01:00
parent 2250495d4f
commit 9263d4468a
2 changed files with 324 additions and 18 deletions

View file

@ -9,7 +9,13 @@ import { Test, TestingModule } from '@nestjs/testing';
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm'; import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
import assert from 'assert'; import assert from 'assert';
import { Mock } from 'ts-mockery'; import { Mock } from 'ts-mockery';
import { DataSource, EntityManager, Repository } from 'typeorm'; import {
DataSource,
EntityManager,
EntityTarget,
Repository,
SelectQueryBuilder,
} from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity'; import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity'; import { Author } from '../authors/author.entity';
@ -38,6 +44,8 @@ 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';
import Any = jasmine.Any;
describe('HistoryService', () => { describe('HistoryService', () => {
let service: HistoryService; let service: HistoryService;
let historyRepo: Repository<HistoryEntry>; let historyRepo: Repository<HistoryEntry>;
@ -47,16 +55,17 @@ describe('HistoryService', () => {
[(entityManager: EntityManager) => Promise<void>] [(entityManager: EntityManager) => Promise<void>]
>; >;
class CreateQueryBuilderClass { class CreateQueryBuilderClass<Entity> {
leftJoinAndSelect: () => CreateQueryBuilderClass; leftJoinAndSelect: () => CreateQueryBuilderClass<Entity>;
where: () => CreateQueryBuilderClass; where: () => CreateQueryBuilderClass<Entity>;
orWhere: () => CreateQueryBuilderClass; orWhere: () => CreateQueryBuilderClass<Entity>;
setParameter: () => CreateQueryBuilderClass; setParameter: () => CreateQueryBuilderClass<Entity>;
getOne: () => HistoryEntry; getOne: () => Entity;
getMany: () => HistoryEntry[]; getMany: () => Entity[];
} }
let createQueryBuilderFunc: CreateQueryBuilderClass; let createQueryBuilderFunc: CreateQueryBuilderClass<HistoryEntry>;
let createQueryBuilderFuncNote: CreateQueryBuilderClass<Note>;
beforeEach(async () => { beforeEach(async () => {
noteRepo = new Repository<Note>( noteRepo = new Repository<Note>(
@ -148,12 +157,29 @@ describe('HistoryService', () => {
getOne: () => historyEntry, getOne: () => historyEntry,
getMany: () => [historyEntry], getMany: () => [historyEntry],
}; };
createQueryBuilderFunc = createQueryBuilder as CreateQueryBuilderClass; createQueryBuilderFunc =
createQueryBuilder as CreateQueryBuilderClass<HistoryEntry>;
const note = Note.create(null);
jest jest
.spyOn(historyRepo, 'createQueryBuilder') .spyOn(historyRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
.mockImplementation(() => createQueryBuilder); .mockImplementation(() => createQueryBuilder);
const createQueryBuilderNote = {
leftJoinAndSelect: () => createQueryBuilderNote,
where: () => createQueryBuilderNote,
orWhere: () => createQueryBuilderNote,
setParameter: () => createQueryBuilderNote,
getOne: () => note,
getMany: () => [note],
};
createQueryBuilderFuncNote =
createQueryBuilderNote as CreateQueryBuilderClass<Note>;
jest
.spyOn(historyRepo, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation(() => createQueryBuilderNote);
}); });
it('should be defined', () => { it('should be defined', () => {
@ -388,16 +414,41 @@ describe('HistoryService', () => {
updatedAt: historyEntryImport.lastVisitedAt, updatedAt: historyEntryImport.lastVisitedAt,
}; };
const createQueryBuilder = mockSelectQueryBuilderInRepo(noteRepo, note); function createQueryBuilder(entityClass: any): SelectQueryBuilder<any> {
if (entityClass.name == 'HistoryEntry') {
return mockSelectQueryBuilderInRepo(
historyRepo,
newlyCreatedHistoryEntry,
);
} else {
return mockSelectQueryBuilderInRepo(noteRepo, note);
}
}
/*
class QueryBuilderExtension<Entity> extends SelectQueryBuilder<Entity>{
createQueryBuilder(): this {
if (Entity.toString() == 'HistoryEntry') {
return mockSelectQueryBuilderInRepo(
historyRepo,
newlyCreatedHistoryEntry,
);
} else {
return mockSelectQueryBuilderInRepo(noteRepo, note);
}
}
}
*/
const mockedManager = Mock.of<EntityManager>({ const mockedManager = Mock.of<EntityManager>({
find: jest.fn().mockResolvedValueOnce([historyEntry]), find: jest.fn().mockResolvedValueOnce([historyEntry]),
createQueryBuilder: () => createQueryBuilder, remove: jest
remove: jest.fn().mockImplementationOnce(async (_: HistoryEntry) => { .fn()
// TODO: reimplement checks below .mockImplementationOnce(async (entry: HistoryEntry) => {
//expect(await (await entry.note).aliases).toHaveLength(1); expect(await (await entry.note).aliases).toHaveLength(1);
//expect((await (await entry.note).aliases)[0].name).toEqual(alias); expect((await (await entry.note).aliases)[0].name).toEqual(alias);
//expect(entry.pinStatus).toEqual(false); expect(entry.pinStatus).toEqual(true);
}), }),
createQueryBuilder: jest.fn(),
save: jest.fn().mockImplementationOnce(async (entry: HistoryEntry) => { save: jest.fn().mockImplementationOnce(async (entry: HistoryEntry) => {
expect((await entry.note).aliases).toEqual( expect((await entry.note).aliases).toEqual(
(await newlyCreatedHistoryEntry.note).aliases, (await newlyCreatedHistoryEntry.note).aliases,
@ -406,6 +457,11 @@ describe('HistoryService', () => {
expect(entry.updatedAt).toEqual(newlyCreatedHistoryEntry.updatedAt); expect(entry.updatedAt).toEqual(newlyCreatedHistoryEntry.updatedAt);
}), }),
}); });
jest
.spyOn(mockedManager, 'createQueryBuilder')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.mockImplementation((type) => createQueryBuilder(type));
mockedTransaction.mockImplementation((cb) => cb(mockedManager)); mockedTransaction.mockImplementation((cb) => cb(mockedManager));
await service.setHistory(user, [historyEntryImport]); await service.setHistory(user, [historyEntryImport]);
}); });

View file

@ -0,0 +1,250 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
/*
import {
DeepPartial,
DeleteResult,
EntityManager,
EntityTarget,
FindManyOptions,
FindOptionsWhere,
InsertResult,
ObjectID,
QueryRunner,
RemoveOptions,
Repository,
SaveOptions,
SelectQueryBuilder,
UpdateResult,
} from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import { UpsertOptions } from 'typeorm/repository/UpsertOptions';
class MockRepository<T> implements Repository<T> {
entities: T[]
createQueryBuilder(
alias?: string,
queryRunner?: QueryRunner,
): SelectQueryBuilder<T> {
new SelectQueryBuilder()
//return super.createQueryBuilder(alias, queryRunner);
}
find(options?: FindManyOptions<T>): Promise<T[]> {
//return super.find(options);
}
clear(): Promise<void> {
return Promise.resolve(undefined);
}
count(options: FindManyOptions<T> | undefined): Promise<number> {
return Promise.resolve(0);
}
countBy(where: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number> {
return Promise.resolve(0);
}
create(): T {
return undefined;
}
decrement(
conditions: FindOptionsWhere<T>,
propertyPath: string,
value: number | string,
): Promise<UpdateResult> {
return Promise.resolve(undefined);
}
delete(
criteria:
| string
| string[]
| number
| number[]
| Date
| Date[]
| ObjectID
| ObjectID[]
| FindOptionsWhere<T>,
): Promise<DeleteResult> {
return Promise.resolve(undefined);
}
extend<CustomRepository>(
custom: CustomRepository & ThisType<Repository<T> & CustomRepository>,
): Repository<T> & CustomRepository {
return undefined;
}
findAndCount(
options: FindManyOptions<T> | undefined,
): Promise<[T[], number]> {
return Promise.resolve([[], 0]);
}
findAndCountBy(
where: FindOptionsWhere<T> | FindOptionsWhere<T>[],
): Promise<[T[], number]> {
return Promise.resolve([[], 0]);
}
findBy(where: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<T[]> {
return Promise.resolve([]);
}
findByIds(ids: any[]): Promise<T[]> {
return Promise.resolve([]);
}
findOne(options: FindOneOptions<T>): Promise<T | null> {
return Promise.resolve(undefined);
}
findOneBy(
where: FindOptionsWhere<T> | FindOptionsWhere<T>[],
): Promise<T | null> {
return Promise.resolve(undefined);
}
findOneById(id: number | string | Date | ObjectID): Promise<T | null> {
return Promise.resolve(undefined);
}
findOneByOrFail(
where: FindOptionsWhere<T> | FindOptionsWhere<T>[],
): Promise<T> {
return Promise.resolve(undefined);
}
findOneOrFail(options: FindOneOptions<T>): Promise<T> {
return Promise.resolve(undefined);
}
getId(entity: T): any {}
hasId(entity: T): boolean {
return false;
}
increment(
conditions: FindOptionsWhere<T>,
propertyPath: string,
value: number | string,
): Promise<UpdateResult> {
return Promise.resolve(undefined);
}
insert(
entity: QueryDeepPartialEntity<T> | QueryDeepPartialEntity<T>[],
): Promise<InsertResult> {
return Promise.resolve(undefined);
}
readonly manager: EntityManager;
merge(mergeIntoEntity: T, entityLikes: DeepPartial<T>): T {
return undefined;
}
get metadata(): import('..').EntityMetadata {
return undefined;
}
preload(entityLike: DeepPartial<T>): Promise<T | undefined> {
return Promise.resolve(undefined);
}
query(query: string, parameters: any[] | undefined): Promise<any> {
return Promise.resolve(undefined);
}
readonly queryRunner: QueryRunner;
recover<T>(
entities: T[],
options: SaveOptions & { reload: false },
): Promise<T[]> {
return Promise.resolve([]);
}
remove(entities: T[], options: RemoveOptions | undefined): Promise<T[]> {
return Promise.resolve([]);
}
restore(
criteria:
| string
| string[]
| number
| number[]
| Date
| Date[]
| ObjectID
| ObjectID[]
| FindOptionsWhere<T>,
): Promise<UpdateResult> {
return Promise.resolve(undefined);
}
save<T>(
entities: T[],
options: SaveOptions & { reload: false },
): Promise<T[]> {
return Promise.resolve([]);
}
softDelete(
criteria:
| string
| string[]
| number
| number[]
| Date
| Date[]
| ObjectID
| ObjectID[]
| FindOptionsWhere<T>,
): Promise<UpdateResult> {
return Promise.resolve(undefined);
}
softRemove<T>(
entities: T[],
options: SaveOptions & { reload: false },
): Promise<T[]> {
return Promise.resolve([]);
}
readonly target: EntityTarget<T>;
update(
criteria:
| string
| string[]
| number
| number[]
| Date
| Date[]
| ObjectID
| ObjectID[]
| FindOptionsWhere<T>,
partialEntity: QueryDeepPartialEntity<T>,
): Promise<UpdateResult> {
return Promise.resolve(undefined);
}
upsert(
entityOrEntities: QueryDeepPartialEntity<T> | QueryDeepPartialEntity<T>[],
conflictPathsOrOptions: string[] | UpsertOptions<T>,
): Promise<InsertResult> {
return Promise.resolve(undefined);
}
}
*/