import { faker } from '@faker-js/faker/locale/en_US';
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewCycleDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewTargetUserDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormReviewDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewUserDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormTemplateDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormReviewEditRequestDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewTemplateDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewReviewerDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewExternalReviewerDAO');
jest.mock('../../../../src/api/modules/formTemplates/infrastructure/dataAccessObjects/formTemplateDAO');
jest.mock('../../../../src/api/modules/questionTemplates/infrastructure/dataAccessObjects/questionTemplateDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewGoalQuestionWithAnswerDAO');
jest.mock('../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewCompetencyQuestionWithAnswerDAO');

import { CursorIdPagination, DatabaseError, IllegalStateError } from '@humand-packages/common';
import { CreationAttributes, literal, Op } from 'sequelize';
import { getSequelize } from '../../../../src/api/config/sequelize';
import { DuplicatePerformanceReviewFormReviewInterface } from '../../../../src/api/modules/performanceReviews/business/interfaces/duplicatePerformanceReviewFormReviewInterface';
import { DuplicatePerformanceReviewFormTemplateInterface } from '../../../../src/api/modules/performanceReviews/business/interfaces/duplicatePerformanceReviewFormTemplateInterface';
import {
    PerformanceReviewCycle,
    PerformanceReviewCycleForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewCycle';
import {
    PerformanceReviewExternalReviewer,
    PerformanceReviewExternalReviewerForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/PerformanceReviewExternalReviewer';
import {
    PerformanceReviewFormReview,
    PerformanceReviewFormReviewForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewFormReview';
import {
    PerformanceReviewFormReviewEditRequest,
    PerformanceReviewFormReviewEditRequestForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewFormReviewEditRequest';
import {
    PerformanceReviewFormTemplate,
    PerformanceReviewFormTemplateForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewFormTemplate';
import {
    PerformanceReviewGoalQuestionWithAnswer,
    PerformanceReviewGoalQuestionWithAnswerForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewGoalQuestionWithAnswer';
import {
    PerformanceReviewReviewer,
    PerformanceReviewReviewerForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewReviewer';
import {
    PerformanceReviewTargetUser,
    PerformanceReviewTargetUserForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewTargetUser';
import {
    PerformanceReviewTemplate,
    PerformanceReviewTemplateForCreation,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewTemplate';
import {
    PerformanceReviewUser,
    PerformanceReviewUserWithStats,
} from '../../../../src/api/modules/performanceReviews/business/models/performanceReviewUser';
import { ListPerformanceReviewFormReviewsOptions } from '../../../../src/api/modules/performanceReviews/business/types/listPerformanceReviewFormTemplateAnswersOptions';
import { ListPerformanceReviewReviewerUsersOptions } from '../../../../src/api/modules/performanceReviews/business/types/listPerformanceReviewReviewerUsersOptions';
import { ListPerformanceReviewUsersOptions } from '../../../../src/api/modules/performanceReviews/business/types/listPerformanceReviewUsersOptions';
import { PerformanceReviewCycleParticipantType } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewCycleRole';
import {
    PerformanceReviewEvaluationTypes,
    PerformanceReviewEvaluationTypesValues,
    PerformanceReviewPeerEvaluationTypes,
    PerformanceReviewPeerEvaluationTypesValues,
} from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewEvaluationTypes';
import { PerformanceReviewFormEditRequestStatus } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewFormEditRequestStatus';
import { PerformanceReviewFormShareStatus } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewFormShareStatus';
import { PerformanceReviewFormStatus } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewFormStatus';
import { UserStatsByType } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewUserStats';
import { PerformanceReviewUserStatus } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewUserStatus';
import { PerformanceReviewsRepositoryAdapter } from '../../../../src/api/modules/performanceReviews/infrastructure/adapters/repositories/performanceReviewsRepositoryAdapter';
import {
    PerformanceReviewCycleCreationFields,
    PerformanceReviewCycleDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewCycleDAO';
import {
    PerformanceReviewExternalReviewerCreationFields,
    PerformanceReviewExternalReviewerDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewExternalReviewerDAO';
import {
    PerformanceReviewFormReviewCreationFields,
    PerformanceReviewFormReviewDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormReviewDAO';
import {
    PerformanceReviewFormReviewEditRequestCreationFields,
    PerformanceReviewFormReviewEditRequestDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormReviewEditRequestDAO';
import {
    PerformanceReviewFormTemplateCreationFields,
    PerformanceReviewFormTemplateDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormTemplateDAO';
import {
    PerformanceReviewGoalQuestionWithAnswerCreationFields,
    PerformanceReviewGoalQuestionWithAnswerDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewGoalQuestionWithAnswerDAO';
import { PerformanceReviewCompetencyQuestionWithAnswerDAO } from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewCompetencyQuestionWithAnswerDAO';
import {
    PerformanceReviewReviewerCreationFields,
    PerformanceReviewReviewerDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewReviewerDAO';
import {
    PerformanceReviewTargetUserCreationFields,
    PerformanceReviewTargetUserDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewTargetUserDAO';
import {
    PerformanceReviewTemplateCreationFields,
    PerformanceReviewTemplateDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewTemplateDAO';
import {
    PerformanceReviewUserCreationFields,
    PerformanceReviewUserDAO,
} from '../../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewUserDAO';
import { QuestionTemplate } from '../../../../src/api/modules/questionTemplates/business/models/questionTemplate';
import { SequelizeQueryGenerator } from '../../../../src/api/types/sequelize';
import { fakePositiveInteger, generateRandomCursorPagination } from '../../../common/common';
import { mockImplementationTimes } from '../../../common/mock/mockHelpers';
import { generateRandomQuestionTemplate } from '../../questionTemplates/common';
import {
    addStatsToPerformanceReviewUserDAO,
    createEditRequestDAO,
    createExternalReviewerDAO,
    createPerformanceReviewCycleDAO,
    createPerformanceReviewFormReviewDAO,
    createPerformanceReviewFormTemplateDAO,
    createPerformanceReviewTargetUserDAO,
    createPerformanceReviewTemplateDAO,
    createPerformanceReviewUserDAO,
    generateFormTemplateDuplication,
    generateGoalQuestionWithAnswer,
    generateGoalQuestionWithAnswerDAO,
    generatePerformanceReviewFormReviewWithValues,
    generatePerformanceReviewReviewer,
    generatePerformanceReviewReviewerDAO,
    generateRandomEditRequest,
    generateRandomEditRequestForCreation,
    generateRandomExternalReviewer,
    generateRandomExternalReviewerForCreation,
    generateRandomGoalQuestionWithAnswerForCreation,
    generateRandomPerformanceReviewCycle,
    generateRandomPerformanceReviewCycleForCreation,
    generateRandomPerformanceReviewFormReview,
    generateRandomPerformanceReviewFormReviewForCreation,
    generateRandomPerformanceReviewFormTemplate,
    generateRandomPerformanceReviewFormTemplateForCreation,
    generateRandomPerformanceReviewReviewerForCreation,
    generateRandomPerformanceReviewTargetUser,
    generateRandomPerformanceReviewTargetUserForCreation,
    generateRandomPerformanceReviewUser,
    generateRandomReviewDuplication,
    generateRandomReviewUserStatsByType,
    generateRandomTemplate,
    generateRandomTemplateForCreation,
} from '../common';

jest.mock('../../../../src/api/config/sequelize', () => {
    return {
        getSequelize: jest.fn().mockReturnValue({
            query: jest.fn(),
        }),
    };
});
jest.mock('../../../../src/api/helpers/sequelize', () => {
    const mSequelizeQueryGenerator: SequelizeQueryGenerator<any> = {
        selectQuery: () => 'SELECT 1+1 AS result',
    };
    return { getQueryGenerator: jest.fn(() => mSequelizeQueryGenerator) };
});

describe('PerformanceReviewsRepositoryAdapter', () => {
    let performanceReviewsRepositoryAdapter: PerformanceReviewsRepositoryAdapter;
    let mockSequelize: any;

    beforeEach(() => {
        mockSequelize = { query: jest.fn(), transaction: jest.fn() } as any;
        performanceReviewsRepositoryAdapter = new PerformanceReviewsRepositoryAdapter(10, mockSequelize);
    });

    afterEach(() => {
        jest.clearAllMocks();
    });

    describe('newCycle', () => {
        let performanceReviewCycle: PerformanceReviewCycleForCreation;
        let fakeId: number;
        let fakeCreatedAt: Date;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
        });

        it(`should create a review cycle`, async () => {
            performanceReviewCycle = generateRandomPerformanceReviewCycleForCreation();
            jest.spyOn(PerformanceReviewCycleDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewCycleDAO(creationAttributes as CreationAttributes<PerformanceReviewCycleDAO>, buildOptions);
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewCycleCreationFields;
                    model.id = fakeId;
                    model.createdAt = fakeCreatedAt;
                    model.updatedAt = fakeCreatedAt;
                    model.endDate = attributes.endDate;
                    model.instanceId = attributes.instanceId;
                    model.name = attributes.name;
                    model.reviewedUsersMode = attributes.reviewedUsersMode;
                    model.startDate = attributes.startDate;
                    model.status = attributes.status;
                    model.withSummary = attributes.withSummary;
                    model.reviewsVisibleAfterFinishedReviewCycle = attributes.reviewsVisibleAfterFinishedReviewCycle;
                    model.peerReviewReviewerSelectionVisibleForBosses = attributes.peerReviewReviewerSelectionVisibleForBosses;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.newCycle(performanceReviewCycle);

            expect(result).toBeInstanceOf(PerformanceReviewCycle);
            expect(result.id).toBe(fakeId);
            expect(result.createdAt).toBe(fakeCreatedAt);
            expect(result.endDate).toBe(performanceReviewCycle.endDate);
            expect(result.instanceId).toBe(performanceReviewCycle.instanceId);
            expect(result.name).toBe(performanceReviewCycle.name);
            expect(result.reviewedUsersMode).toBe(performanceReviewCycle.reviewedUsersMode);
            expect(result.startDate).toBe(performanceReviewCycle.startDate);
            expect(result.status).toBe(performanceReviewCycle.status);
            expect(result.withSummary).toBe(performanceReviewCycle.withSummary);
            expect(result.reviewsVisibleAfterFinishedReviewCycle).toBe(performanceReviewCycle.reviewsVisibleAfterFinishedReviewCycle);
            expect(result.peerReviewReviewerSelectionVisibleForBosses).toBe(performanceReviewCycle.peerReviewReviewerSelectionVisibleForBosses);
        });

        it('should throw an error if the review cycle could not be saved', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewCycleDAO(creationAttributes as CreationAttributes<PerformanceReviewCycleDAO>, buildOptions);
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.newCycle(performanceReviewCycle)).rejects.toThrow(DatabaseError);
        });
    });

    describe('saveCycle', () => {
        let performanceReviewCycle: PerformanceReviewCycle;

        it(`should update a review cycle`, async () => {
            performanceReviewCycle = generateRandomPerformanceReviewCycle();
            jest.spyOn(PerformanceReviewCycleDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewCycleDAO(creationAttributes as CreationAttributes<PerformanceReviewCycleDAO>, buildOptions);
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewCycleCreationFields;
                    model.id = performanceReviewCycle.id;
                    model.createdAt = performanceReviewCycle.createdAt;
                    model.updatedAt = performanceReviewCycle.updatedAt;
                    model.endDate = attributes.endDate;
                    model.instanceId = attributes.instanceId;
                    model.name = attributes.name;
                    model.reviewedUsersMode = attributes.reviewedUsersMode;
                    model.startDate = attributes.startDate;
                    model.status = attributes.status;
                    model.withSummary = attributes.withSummary;
                    model.reviewsVisibleAfterFinishedReviewCycle = attributes.reviewsVisibleAfterFinishedReviewCycle;
                    model.peerReviewReviewerSelectionVisibleForBosses = attributes.peerReviewReviewerSelectionVisibleForBosses;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.saveCycle(performanceReviewCycle);

            expect(result).toBeInstanceOf(PerformanceReviewCycle);
            expect(result.id).toBe(performanceReviewCycle.id);
            expect(result.createdAt).toBe(performanceReviewCycle.createdAt);
            expect(result.endDate).toBe(performanceReviewCycle.endDate);
            expect(result.instanceId).toBe(performanceReviewCycle.instanceId);
            expect(result.name).toBe(performanceReviewCycle.name);
            expect(result.reviewedUsersMode).toBe(performanceReviewCycle.reviewedUsersMode);
            expect(result.startDate).toBe(performanceReviewCycle.startDate);
            expect(result.status).toBe(performanceReviewCycle.status);
            expect(result.withSummary).toBe(performanceReviewCycle.withSummary);
            expect(result.reviewsVisibleAfterFinishedReviewCycle).toBe(performanceReviewCycle.reviewsVisibleAfterFinishedReviewCycle);
            expect(result.peerReviewReviewerSelectionVisibleForBosses).toBe(performanceReviewCycle.peerReviewReviewerSelectionVisibleForBosses);
        });

        it('should throw an error if the review cycle could not be saved', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewCycleDAO(creationAttributes as CreationAttributes<PerformanceReviewCycleDAO>, buildOptions);
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.saveCycle(performanceReviewCycle)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getCycleById', () => {
        let performanceReviewCycle: PerformanceReviewCycle;
        let performanceReviewFormTemplates: PerformanceReviewFormTemplate[];
        let fakeId: number;
        let fakeFlag: boolean;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewCycleDAO, 'scope').mockImplementationOnce(() => PerformanceReviewCycleDAO);
            performanceReviewCycle = generateRandomPerformanceReviewCycle();
            fakeId = fakePositiveInteger();
            fakeFlag = faker.datatype.boolean();
        });

        it('should get a review cycle by id', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createPerformanceReviewCycleDAO(performanceReviewCycle);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getCycleById(fakeId, performanceReviewCycle.instanceId, fakeFlag);
            const castedResult = result as PerformanceReviewCycle;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewCycle);
            expect(castedResult.id).toBe(performanceReviewCycle.id);
            expect(castedResult.createdAt).toBe(performanceReviewCycle.createdAt);
            expect(castedResult.updatedAt).toBe(performanceReviewCycle.updatedAt);
            expect(castedResult.instanceId).toBe(performanceReviewCycle.instanceId);
            expect(castedResult.name).toBe(performanceReviewCycle.name);
            expect(castedResult.startDate).toBe(performanceReviewCycle.startDate);
            expect(castedResult.endDate).toBe(performanceReviewCycle.endDate);
            expect(castedResult.status).toBe(performanceReviewCycle.status);
            expect(castedResult.reviewedUsersMode).toBe(performanceReviewCycle.reviewedUsersMode);
        });

        it('should get a review cycle by id with form templates', async () => {
            const formTemplatesCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormTemplates = Array(formTemplatesCount)
                .fill(1)
                .map(() => {
                    const formTemplate = generateRandomPerformanceReviewFormTemplate(performanceReviewCycle.id);
                    formTemplate.template = null;
                    return formTemplate;
                });
            jest.spyOn(PerformanceReviewCycleDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createPerformanceReviewCycleDAO(performanceReviewCycle, performanceReviewFormTemplates);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getCycleById(fakeId, performanceReviewCycle.instanceId, true);
            const castedResult = result as PerformanceReviewCycle;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewCycle);
            expect(castedResult.id).toBe(performanceReviewCycle.id);
            expect(castedResult.createdAt).toBe(performanceReviewCycle.createdAt);
            expect(castedResult.updatedAt).toBe(performanceReviewCycle.updatedAt);
            expect(castedResult.instanceId).toBe(performanceReviewCycle.instanceId);
            expect(castedResult.name).toBe(performanceReviewCycle.name);
            expect(castedResult.startDate).toBe(performanceReviewCycle.startDate);
            expect(castedResult.endDate).toBe(performanceReviewCycle.endDate);
            expect(castedResult.status).toBe(performanceReviewCycle.status);
            expect(castedResult.reviewedUsersMode).toBe(performanceReviewCycle.reviewedUsersMode);
            expect(castedResult.formTemplates).toStrictEqual(performanceReviewFormTemplates);
        });

        it('should not get a review cycle', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'findByPk').mockResolvedValueOnce(null);

            await expect(performanceReviewsRepositoryAdapter.getCycleById(fakeId, performanceReviewCycle.instanceId, fakeFlag)).resolves.toBeNull();
        });

        it('should throw an error if the review cycle could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'findByPk').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getCycleById(fakeId, performanceReviewCycle.instanceId, fakeFlag)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('getAllCycles', () => {
        let offset: number;
        let limit: number;
        let dao: PerformanceReviewCycleDAO;

        beforeEach(() => {
            offset = fakePositiveInteger();
            limit = fakePositiveInteger();
            const performanceReviewCycle = generateRandomPerformanceReviewCycle();
            dao = createPerformanceReviewCycleDAO(performanceReviewCycle);
        });

        it('should return empty if no cycles exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([]);
            const { reviewCycles, totalCount } = await performanceReviewsRepositoryAdapter.getAllCycles(dao.instanceId, {});

            expect(totalCount).toBe(0);
            expect(reviewCycles).toHaveLength(0);
        });

        it('should list review cycles if one exists', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 2);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockImplementationOnce(() => {
                dao.amountOfCycles = 1;
                return Promise.resolve([dao]);
            });
            jest.spyOn(dao, 'getDataValue').mockImplementationOnce((key: keyof PerformanceReviewCycleDAO) => {
                return dao[key] as unknown;
            });
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { reviewCycles, totalCount } = await performanceReviewsRepositoryAdapter.getAllCycles(dao.instanceId, {});

            expect(totalCount).toBe(1);
            expect(reviewCycles).toHaveLength(1);
            expect(reviewCycles[0].id).toBe(dao.id);
        });

        it('should list review cycles with list options', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 2);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockImplementationOnce(() => {
                dao.amountOfCycles = 1;
                return Promise.resolve([dao]);
            });
            jest.spyOn(dao, 'getDataValue').mockImplementationOnce((key: keyof PerformanceReviewCycleDAO) => {
                return dao[key] as unknown;
            });
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { reviewCycles, totalCount } = await performanceReviewsRepositoryAdapter.getAllCycles(dao.instanceId, {
                ids: [dao.id],
                pagination: {
                    limit,
                    offset,
                },
                q: dao.name,
                sortingOptions: [['name', 'desc']],
                statuses: [dao.status],
            });

            expect(totalCount).toBe(1);
            expect(reviewCycles).toHaveLength(1);
            expect(reviewCycles[0].id).toBe(dao.id);
        });

        it('should fail if the review cycles could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getAllCycles(dao.instanceId, {})).rejects.toThrow(DatabaseError);
        });
    });

    describe('getUserCycleIds', () => {
        let userId: number;
        let cycleIds: number[];
        let instanceId: number;

        beforeEach(() => {
            userId = fakePositiveInteger();
            instanceId = fakePositiveInteger();
            cycleIds = faker.helpers.multiple(faker.number.int);
        });

        it('should return empty object if no cycles exist for user', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(mockSequelize, 'query').mockResolvedValueOnce([]);
            jest.spyOn(PerformanceReviewCycleDAO, 'scope').mockImplementationOnce(() => PerformanceReviewCycleDAO);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getUserCycleIds(userId, instanceId);

            expect(result).toStrictEqual([]);
        });

        it('should return object correctly if user can see cycles', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(mockSequelize, 'query').mockImplementationOnce(() => {
                const daos = cycleIds.map((cycleId) => {
                    const dao = new PerformanceReviewFormReviewDAO();
                    dao.cycleId = cycleId;
                    return dao;
                });
                return Promise.resolve(daos);
            });
            jest.spyOn(PerformanceReviewCycleDAO, 'scope').mockImplementationOnce(() => PerformanceReviewCycleDAO);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getUserCycleIds(userId, instanceId);

            expect(result).toHaveLength(cycleIds.length);

            result.forEach((cycleId) => {
                expect(cycleIds.includes(cycleId)).toBeTrue();
            });
        });

        it('should fail if the review cycles could not be listed', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'scope').mockImplementationOnce(() => PerformanceReviewCycleDAO);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([]);
            jest.spyOn(mockSequelize, 'query').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getUserCycleIds(userId, instanceId)).rejects.toThrow(DatabaseError);
        });

        //Add test for responsible User
    });

    describe('deleteCycle', () => {
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
        });

        it('should delete a cycle', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteCycle(fakeId)).resolves.not.toThrow();
        });

        it('should throw an error if the cycle could not be deleted', async () => {
            jest.spyOn(PerformanceReviewCycleDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteCycle(fakeId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('duplicateFormTemplates', () => {
        let performanceReviewFormTemplates: DuplicatePerformanceReviewFormTemplateInterface[];
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const formTemplatesCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormTemplates = Array(formTemplatesCount)
                .fill(1)
                .map(() => generateFormTemplateDuplication());
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should duplicate form templates`, async () => {
            performanceReviewFormTemplates.forEach(() =>
                jest.spyOn(PerformanceReviewFormTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormTemplateCreationFields;
                    const model = new PerformanceReviewFormTemplateDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewFormTemplateDAO>,
                        buildOptions,
                    );
                    model.coreFormId = attributes.coreFormId;
                    model.coreFormTag = attributes.coreFormTag;
                    model.cycleId = attributes.cycleId;
                    model.evaluationType = attributes.evaluationType;
                    model.templateId = attributes.templateId;
                    model.scoreConfig = attributes.scoreConfig!;
                    model.formReviewConfig = attributes.formReviewConfig!;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewFormTemplateCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewFormTemplateDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.coreFormId = attributes.coreFormId;
                    dao.coreFormTag = attributes.coreFormTag;
                    dao.cycleId = attributes.cycleId;
                    dao.evaluationType = attributes.evaluationType;
                    dao.templateId = attributes.templateId;
                    dao.scoreConfig = attributes.scoreConfig!;
                    dao.formReviewConfig = attributes.formReviewConfig!;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.duplicateFormTemplates(performanceReviewFormTemplates);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormTemplate);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.coreFormId).toBe(performanceReviewFormTemplates[index].coreFormId);
                expect(result.coreFormTag).toBe(performanceReviewFormTemplates[index].coreFormTag);
                expect(result.cycleId).toBe(performanceReviewFormTemplates[index].cycleId);
                expect(result.evaluationType).toBe(performanceReviewFormTemplates[index].evaluationType);
                expect(result.templateId).toBe(performanceReviewFormTemplates[index].templateId);
                expect(result.formReviewConfig).toEqual(performanceReviewFormTemplates[index].formReviewConfig);
                expect(result.scoreConfig).toEqual(performanceReviewFormTemplates[index].scoreConfig);
            });
        });

        it('should throw an error if the form templates could not be saved', async () => {
            performanceReviewFormTemplates.forEach(() =>
                jest.spyOn(PerformanceReviewFormTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewFormTemplateDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewFormTemplateDAO>,
                        buildOptions,
                    );
                }),
            );
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.duplicateFormTemplates(performanceReviewFormTemplates)).rejects.toThrow(DatabaseError);
        });
    });

    describe('createFormTemplates', () => {
        let performanceReviewFormTemplates: PerformanceReviewFormTemplateForCreation[];
        let fakeCycleId: number;
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            fakeCycleId = fakePositiveInteger();
            const formTemplatesCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormTemplates = Array(formTemplatesCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewFormTemplateForCreation(fakeCycleId));
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create form templates`, async () => {
            performanceReviewFormTemplates.forEach(() =>
                jest.spyOn(PerformanceReviewFormTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormTemplateCreationFields;
                    const model = new PerformanceReviewFormTemplateDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewFormTemplateDAO>,
                        buildOptions,
                    );
                    model.coreFormId = attributes.coreFormId;
                    model.coreFormTag = attributes.coreFormTag;
                    model.cycleId = attributes.cycleId;
                    model.evaluationType = attributes.evaluationType;
                    model.templateId = attributes.templateId;
                    model.dataValues = model;
                    model.scoreConfig = attributes.scoreConfig;
                    model.formReviewConfig = attributes.formReviewConfig;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewFormTemplateCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewFormTemplateDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.coreFormId = attributes.coreFormId;
                    dao.coreFormTag = attributes.coreFormTag;
                    dao.cycleId = attributes.cycleId;
                    dao.evaluationType = attributes.evaluationType;
                    dao.templateId = attributes.templateId;
                    dao.scoreConfig = attributes.scoreConfig;
                    dao.formReviewConfig = attributes.formReviewConfig;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.createFormTemplates(performanceReviewFormTemplates);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormTemplate);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.coreFormId).toBe(performanceReviewFormTemplates[index].coreFormId);
                expect(result.coreFormTag).toBe(performanceReviewFormTemplates[index].coreFormTag);
                expect(result.cycleId).toBe(performanceReviewFormTemplates[index].cycleId);
                expect(result.evaluationType).toBe(performanceReviewFormTemplates[index].evaluationType);
                expect(result.templateId).toBe(performanceReviewFormTemplates[index].templateId);
                expect(result.scoreConfig?.withScore).toBe(false);
                expect(result.scoreConfig?.formTemplateQuestionWeights).toStrictEqual({});
                expect(result.scoreConfig?.coreFormQuestionWeights).toStrictEqual({});
                expect(result.scoreConfig?.withGoalScore).toBe(false);
                expect(result.scoreConfig?.withCompetenciesScore).toBe(false);
                expect(result.scoreConfig?.withHiddenScoreForReviewed).toBe(false);
                expect(result.scoreConfig?.scaleStart).toBe(0);
                expect(result.scoreConfig?.scaleEnd).toBe(10);
                expect(result.scoreConfig?.formWeight).toBe(0);
                expect(result.scoreConfig?.goalsWeight).toBe(0);
                expect(result.scoreConfig?.competenciesWeight).toBe(0);
            });
        });

        it('should throw an error if the form templates could not be saved', async () => {
            performanceReviewFormTemplates.forEach(() =>
                jest.spyOn(PerformanceReviewFormTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewFormTemplateDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewFormTemplateDAO>,
                        buildOptions,
                    );
                }),
            );
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.createFormTemplates(performanceReviewFormTemplates)).rejects.toThrow(DatabaseError);
        });
    });

    describe('saveFormTemplate', () => {
        let performanceReviewFormTemplate: PerformanceReviewFormTemplate;
        let cycleId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
        });

        it(`should update a form template`, async () => {
            performanceReviewFormTemplate = generateRandomPerformanceReviewFormTemplate(cycleId);
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormTemplateDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormTemplateDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormTemplateCreationFields;
                    model.id = performanceReviewFormTemplate.id;
                    model.createdAt = performanceReviewFormTemplate.createdAt;
                    model.updatedAt = performanceReviewFormTemplate.updatedAt;
                    model.coreFormId = attributes.coreFormId;
                    model.coreFormTag = attributes.coreFormTag;
                    model.cycleId = attributes.cycleId;
                    model.evaluationType = attributes.evaluationType;
                    model.templateId = attributes.templateId;
                    model.scoreConfig = performanceReviewFormTemplate.scoreConfig;
                    model.formReviewConfig = performanceReviewFormTemplate.formReviewConfig;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.saveFormTemplate(performanceReviewFormTemplate);

            expect(result).toBeInstanceOf(PerformanceReviewFormTemplate);
            expect(result.id).toBe(performanceReviewFormTemplate.id);
            expect(result.createdAt).toBe(performanceReviewFormTemplate.createdAt);
            expect(result.coreFormId).toBe(performanceReviewFormTemplate.coreFormId);
            expect(result.coreFormTag).toBe(performanceReviewFormTemplate.coreFormTag);
            expect(result.cycleId).toBe(performanceReviewFormTemplate.cycleId);
            expect(result.evaluationType).toBe(performanceReviewFormTemplate.evaluationType);
            expect(result.templateId).toBe(performanceReviewFormTemplate.templateId);
            expect(result.formReviewConfig).toBe(performanceReviewFormTemplate.formReviewConfig);
            expect(result.scoreConfig).toEqual(performanceReviewFormTemplate.scoreConfig);
            expect(result.formReviewConfig).toEqual(performanceReviewFormTemplate.formReviewConfig);
        });

        it('should throw an error if the review form template could not be saved', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormTemplateDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormTemplateDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error());
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.saveFormTemplate(performanceReviewFormTemplate)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormTemplateByEvaluationType', () => {
        let reviewTemplate: PerformanceReviewFormTemplate;

        beforeEach(() => {
            const cycleId = fakePositiveInteger();
            reviewTemplate = generateRandomPerformanceReviewFormTemplate(cycleId);
        });

        it('should get a form template by evaluation type', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'findOne').mockImplementationOnce((_options) => {
                const dao = createPerformanceReviewFormTemplateDAO(reviewTemplate);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getFormTemplateByEvaluationType(
                reviewTemplate.cycleId,
                reviewTemplate.evaluationType,
            );
            const castedResult = result as PerformanceReviewFormTemplate;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewFormTemplate);
            expect(castedResult.id).toBe(reviewTemplate.id);
            expect(castedResult.createdAt).toBe(reviewTemplate.createdAt);
            expect(castedResult.coreFormId).toBe(reviewTemplate.coreFormId);
            expect(castedResult.coreFormTag).toBe(reviewTemplate.coreFormTag);
            expect(castedResult.cycleId).toBe(reviewTemplate.cycleId);
            expect(castedResult.evaluationType).toBe(reviewTemplate.evaluationType);
            expect(castedResult.templateId).toBe(reviewTemplate.templateId);
            expect(castedResult.scoreConfig).toEqual(reviewTemplate.scoreConfig);
            expect(castedResult.formReviewConfig).toEqual(reviewTemplate.formReviewConfig);
        });

        it('should not get a form template', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'findOne').mockResolvedValueOnce(null);

            await expect(
                performanceReviewsRepositoryAdapter.getFormTemplateByEvaluationType(reviewTemplate.cycleId, reviewTemplate.evaluationType),
            ).resolves.toBeNull();
        });

        it('should throw an error if the form template could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'findOne').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.getFormTemplateByEvaluationType(reviewTemplate.cycleId, reviewTemplate.evaluationType),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteFormTemplates', () => {
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
        });

        it('should delete all cycle form templates', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteFormTemplates(fakeId)).resolves.not.toThrow();
        });

        it('should throw an error if the form templates could not be deleted', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteFormTemplates(fakeId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('countTemplateUsages', () => {
        let ids: number[];
        const countPerId: { [templateId: number]: number } = {};

        beforeEach(() => {
            const possibleIds = Array.from(new Set(faker.helpers.multiple(faker.number.int, { count: 10 })));
            ids = faker.helpers.arrayElements(possibleIds);
            ids.forEach((id) => (countPerId[id] = fakePositiveInteger()));
        });

        it('should count usages per form templates with ids that exist', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                ids.map((id) => ({ count: countPerId[id], templateId: id })),
            );

            const result = await performanceReviewsRepositoryAdapter.countTemplateUsages(ids);

            expect(Object.keys(result)).toHaveLength(ids.length);

            Object.keys(result).forEach((id) => {
                const numberId = parseInt(id);
                const count = result[numberId];

                expect(count).toBe(countPerId[numberId]);
            });
        });

        it('should return empty object if no ids exist', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'count').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.countTemplateUsages([]);

            expect(result).toBeDefined();
            expect(Object.keys(result)).toHaveLength(0);
        });

        it('should throw an error if the form templates could not be counted', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.countTemplateUsages([])).rejects.toThrow(DatabaseError);
        });
    });

    describe('countTemplatesWithIds', () => {
        it('should count templates with ids', async () => {
            const count = fakePositiveInteger();
            jest.spyOn(PerformanceReviewTemplateDAO, 'count').mockResolvedValueOnce(count);

            const result = await performanceReviewsRepositoryAdapter.countTemplatesWithIds(fakePositiveInteger(), {
                formTemplateIds: [fakePositiveInteger()],
                templateIds: [fakePositiveInteger()],
            });

            expect(result).toBe(count);
        });

        it('should throw an error if the form templates could not be counted', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.countTemplatesWithIds(fakePositiveInteger(), {
                    formTemplateIds: [fakePositiveInteger()],
                    templateIds: [fakePositiveInteger()],
                }),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('stopUsingTemplateIdsInFormTemplates', () => {
        let formTemplateIds: number[];

        beforeEach(() => {
            formTemplateIds = faker.helpers.multiple(faker.number.int);
        });

        it('should stop using form template ids', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'update').mockResolvedValueOnce([formTemplateIds.length]);

            await expect(performanceReviewsRepositoryAdapter.stopUsingTemplateIdsInFormTemplates(formTemplateIds)).resolves.not.toThrow();
        });

        it('should throw an error if the review users could not be marked as deleted', async () => {
            jest.spyOn(PerformanceReviewFormTemplateDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.stopUsingTemplateIdsInFormTemplates(formTemplateIds)).rejects.toThrow(DatabaseError);
        });
    });

    describe('stopUsingQuestionTemplateIds', () => {
        let questionTemplateIds: number[];

        beforeEach(() => {
            questionTemplateIds = faker.helpers.multiple(faker.number.int);
        });

        it('should stop using question template ids', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'update').mockResolvedValueOnce([questionTemplateIds.length]);

            await expect(performanceReviewsRepositoryAdapter.stopUsingQuestionTemplateIds(questionTemplateIds)).resolves.not.toThrow();
        });

        it('should throw an error if there is a database error', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.stopUsingQuestionTemplateIds(questionTemplateIds)).rejects.toThrow(DatabaseError);
        });
    });

    describe('stopUsingTemplates', () => {
        let formTemplateIds: number[];

        beforeEach(() => {
            formTemplateIds = faker.helpers.multiple(faker.number.int);
        });

        it('should stop using templates template ids', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'update').mockResolvedValueOnce([formTemplateIds.length]);

            await expect(performanceReviewsRepositoryAdapter.stopUsingTemplates(formTemplateIds)).resolves.not.toThrow();
        });

        it('should throw an error if there is a database error', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.stopUsingTemplates(formTemplateIds)).rejects.toThrow(DatabaseError);
        });
    });

    describe('countFormReviewsOfCycleByTypeAndStatus', () => {
        let cycleId: number;
        let types: string[];
        let statuses: string[];

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            types = faker.helpers.arrayElements(Object.values(PerformanceReviewEvaluationTypes), {
                max: Object.values(PerformanceReviewEvaluationTypes).length,
                min: 1,
            });
            statuses = faker.helpers.arrayElements(Object.values(PerformanceReviewFormStatus), {
                max: Object.values(PerformanceReviewFormStatus).length,
                min: 1,
            });
        });

        it('should count form reviews of cycle by type and usage if one type exists', async () => {
            types = types.slice(0, 1);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                types.flatMap((type) => statuses.map((status) => ({ count: fakePositiveInteger(), status, type }))),
            );

            const result = await performanceReviewsRepositoryAdapter.countFormReviewsOfCycleByTypeAndStatus(cycleId);

            expect(Object.keys(result)).toHaveLength(types.length);

            (Object.keys(result) as PerformanceReviewEvaluationTypesValues[]).forEach((type) => {
                expect(Object.keys(result[type])).toHaveLength(statuses.length);

                Object.keys(result[type]).forEach((status) => {
                    expect(result[type][status]).toBeNumber();
                });
            });
        });

        it('should count form reviews of cycle by type and usage if many types exist', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                types.flatMap((type) => statuses.map((status) => ({ count: fakePositiveInteger(), status, type }))),
            );

            const result = await performanceReviewsRepositoryAdapter.countFormReviewsOfCycleByTypeAndStatus(cycleId);

            expect(Object.keys(result)).toHaveLength(types.length);

            (Object.keys(result) as PerformanceReviewEvaluationTypesValues[]).forEach((type) => {
                expect(Object.keys(result[type])).toHaveLength(statuses.length);

                Object.keys(result[type]).forEach((status) => {
                    expect(result[type][status]).toBeNumber();
                });
            });
        });

        it('should count form reviews of cycle filtering by responsibleId', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                types.flatMap((type) => statuses.map((status) => ({ count: fakePositiveInteger(), status, type }))),
            );

            const result = await performanceReviewsRepositoryAdapter.countFormReviewsOfCycleByTypeAndStatus(cycleId, fakePositiveInteger());

            expect(Object.keys(result)).toHaveLength(types.length);

            (Object.keys(result) as PerformanceReviewEvaluationTypesValues[]).forEach((type) => {
                expect(Object.keys(result[type])).toHaveLength(statuses.length);

                Object.keys(result[type]).forEach((status) => {
                    expect(result[type][status]).toBeNumber();
                });
            });
        });

        it('should return empty object if no types exist', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.countFormReviewsOfCycleByTypeAndStatus(cycleId);

            expect(result).toBeDefined();
            expect(Object.keys(result)).toHaveLength(0);
        });

        it('should throw an error if the form reviews could not be counted', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.countFormReviewsOfCycleByTypeAndStatus(cycleId)).rejects.toThrow(DatabaseError);
        });

        it('should exclude CALIBRATION_REVIEW from the where clause', async () => {
            const countSpy = jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                [],
            );

            await performanceReviewsRepositoryAdapter.countFormReviewsOfCycleByTypeAndStatus(cycleId);

            const callArgs = countSpy.mock.calls[0][0];
            expect(callArgs.where).toMatchObject({
                type: { [Op.ne]: PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW },
            });
        });
    });

    describe('countFormReviewsByShareStatus', () => {
        let cycleId: number;
        let shareStatuses: string[];

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            shareStatuses = faker.helpers.arrayElements(Object.values(PerformanceReviewFormShareStatus), {
                max: Object.values(PerformanceReviewFormShareStatus).length,
                min: 1,
            });
        });

        it('should count form reviews of cycle by share status', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                shareStatuses.map((shareStatus) => ({ count: fakePositiveInteger(), shareStatus })),
            );

            const result = await performanceReviewsRepositoryAdapter.countFormReviewsByShareStatus(cycleId);

            expect(Object.keys(result)).toHaveLength(shareStatuses.length);

            Object.keys(result).forEach((shareStatus) => {
                expect(result[shareStatus]).toBeNumber();
            });
        });

        it('should return empty object if no reviews exist', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.countFormReviewsByShareStatus(cycleId);

            expect(result).toBeDefined();
            expect(Object.keys(result)).toHaveLength(0);
        });

        it('should throw an error if the form reviews could not be counted', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.countFormReviewsByShareStatus(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('countTargetUsersByShareStatus', () => {
        let cycleId: number;
        let shareStatuses: string[];

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            shareStatuses = faker.helpers.arrayElements(Object.values(PerformanceReviewFormShareStatus), {
                max: Object.values(PerformanceReviewFormShareStatus).length,
                min: 1,
            });
        });

        it('should count form reviews of cycle by share status', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockResolvedValueOnce(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                shareStatuses.map((shareStatus) => ({ count: fakePositiveInteger(), shareStatus })),
            );

            const result = await performanceReviewsRepositoryAdapter.countTargetUsersByShareStatus(cycleId);

            expect(Object.keys(result)).toHaveLength(shareStatuses.length);

            Object.keys(result).forEach((shareStatus) => {
                expect(result[shareStatus]).toBeNumber();
            });
        });

        it('should return empty object if no reviews exist', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.countTargetUsersByShareStatus(cycleId);

            expect(result).toBeDefined();
            expect(Object.keys(result)).toHaveLength(0);
        });

        it('should throw an error if the form reviews could not be counted', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.countTargetUsersByShareStatus(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('upsertTargetUsers', () => {
        let performanceReviewTargetUsers: PerformanceReviewTargetUserForCreation[];
        let fakeCycleId: number;
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            fakeCycleId = fakePositiveInteger();
            const targetUsersCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewTargetUsers = Array(targetUsersCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewTargetUserForCreation(fakeCycleId));
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should upsert target users`, async () => {
            performanceReviewTargetUsers.forEach(() =>
                jest.spyOn(PerformanceReviewTargetUserDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewTargetUserCreationFields;
                    const model = new PerformanceReviewTargetUserDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewTargetUserDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.userId = attributes.userId;
                    model.responsibleId = attributes.responsibleId;
                    model.shareStatus = attributes.shareStatus;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewTargetUserDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewTargetUserCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewTargetUserDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.userId = attributes.userId;
                    dao.responsibleId = attributes.responsibleId;
                    dao.shareStatus = attributes.shareStatus;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.upsertTargetUsers(performanceReviewTargetUsers);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewTargetUser);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(performanceReviewTargetUsers[index].cycleId);
                expect(result.userId).toBe(performanceReviewTargetUsers[index].userId);
                expect(result.shareStatus).toBe(performanceReviewTargetUsers[index].shareStatus);
            });
        });

        it('should throw an error if the target users could not be saved', async () => {
            performanceReviewTargetUsers.forEach(() =>
                jest.spyOn(PerformanceReviewTargetUserDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewTargetUserDAO(creationAttributes as CreationAttributes<PerformanceReviewTargetUserDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewTargetUserDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.upsertTargetUsers(performanceReviewTargetUsers)).rejects.toThrow(DatabaseError);
        });
    });

    describe('saveTargetUser', () => {
        let performanceReviewTargetUser: PerformanceReviewTargetUser;

        beforeEach(() => {
            const cycleId = fakePositiveInteger();
            performanceReviewTargetUser = generateRandomPerformanceReviewTargetUser(cycleId);
        });

        it(`should update a target user`, async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewTargetUserDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewTargetUserDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewTargetUserCreationFields;
                    model.id = performanceReviewTargetUser.id;
                    model.createdAt = performanceReviewTargetUser.createdAt;
                    model.updatedAt = performanceReviewTargetUser.updatedAt;
                    model.deletedAt = performanceReviewTargetUser.deletedAt;
                    model.cycleId = attributes.cycleId;
                    model.userId = attributes.userId;
                    model.responsibleId = attributes.responsibleId;
                    model.shareStatus = attributes.shareStatus;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.saveTargetUser(performanceReviewTargetUser);

            expect(result).toBeInstanceOf(PerformanceReviewTargetUser);
            expect(result.id).toBe(performanceReviewTargetUser.id);
            expect(result.createdAt).toBe(performanceReviewTargetUser.createdAt);
            expect(result.updatedAt).toBe(performanceReviewTargetUser.updatedAt);
            expect(result.deletedAt).toBe(performanceReviewTargetUser.deletedAt);
            expect(result.cycleId).toBe(performanceReviewTargetUser.cycleId);
            expect(result.userId).toBe(performanceReviewTargetUser.userId);
            expect(result.responsibleId).toBe(performanceReviewTargetUser.responsibleId);
            expect(result.shareStatus).toBe(performanceReviewTargetUser.shareStatus);
        });

        it('should throw an error if the review cycle could not be saved', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewTargetUserDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewTargetUserDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.saveTargetUser(performanceReviewTargetUser)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getTargetUsers', () => {
        let cycleId: number;
        let dao: PerformanceReviewTargetUserDAO;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const targetUser = generateRandomPerformanceReviewTargetUser(cycleId);
            dao = createPerformanceReviewTargetUserDAO(targetUser);
        });

        it('should return empty if no target users exist', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockResolvedValueOnce([]);
            const targetUsers = await performanceReviewsRepositoryAdapter.getTargetUsers(cycleId);

            expect(targetUsers).toHaveLength(0);
        });

        it('should list target users if one exists', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockResolvedValueOnce([dao]);
            const targetUsers = await performanceReviewsRepositoryAdapter.getTargetUsers(cycleId);

            expect(targetUsers).toHaveLength(1);
            expect(targetUsers[0].id).toBe(dao.id);
        });

        it('should fail if the target users could not be listed', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getTargetUsers(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getTargetUserIds', () => {
        let cycleId: number;
        let dao: PerformanceReviewTargetUserDAO;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const targetUser = generateRandomPerformanceReviewTargetUser(cycleId);
            dao = createPerformanceReviewTargetUserDAO(targetUser);
        });

        it('should return empty set if no target users exist', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockResolvedValueOnce([]);
            const ids = await performanceReviewsRepositoryAdapter.getTargetUserIds(cycleId);

            expect(ids.size).toBe(0);
        });

        it('should return user ids for the cycle', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockResolvedValueOnce([dao]);
            const ids = await performanceReviewsRepositoryAdapter.getTargetUserIds(cycleId);

            expect(ids.size).toBe(1);
            expect(ids.has(dao.userId)).toBe(true);
        });

        it('should fail if target user ids could not be loaded', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getTargetUserIds(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getTargetUser', () => {
        let cycleId: number;
        let dao: PerformanceReviewTargetUserDAO;
        let fakeFlag: boolean;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'scope').mockImplementationOnce(() => PerformanceReviewTargetUserDAO);
            cycleId = fakePositiveInteger();
            const targetUser = generateRandomPerformanceReviewTargetUser(cycleId, fakePositiveInteger(), fakePositiveInteger());
            dao = createPerformanceReviewTargetUserDAO(targetUser);
            fakeFlag = faker.datatype.boolean();
        });

        it('should return null if target user does not exist', async () => {
            const userId = faker.number.int({ min: dao.userId + 1 });
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockResolvedValueOnce(null);
            const targetUser = await performanceReviewsRepositoryAdapter.getTargetUser(cycleId, userId, fakeFlag);

            expect(targetUser).toBeNull();
        });

        it('should get a target user by userId', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockResolvedValueOnce(dao);
            const result = await performanceReviewsRepositoryAdapter.getTargetUser(cycleId, dao.userId, false);
            const castedResult = result as PerformanceReviewTargetUser;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewTargetUser);
            expect(castedResult.id).toBe(dao.id);
            expect(castedResult.createdAt).toBe(dao.createdAt);
            expect(castedResult.updatedAt).toBe(dao.updatedAt);
            expect(castedResult.cycleId).toBe(dao.cycleId);
            expect(castedResult.userId).toBe(dao.userId);
            expect(castedResult.responsibleId).toBe(dao.responsibleId);
            expect(castedResult.shareStatus).toBe(dao.shareStatus);
        });

        it('should get a target user by userId with user details', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockImplementationOnce(() => {
                dao.user = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.userId));
                dao.responsible = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.responsibleId as number));
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getTargetUser(cycleId, dao.userId, true);
            const castedResult = result as PerformanceReviewTargetUser;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewTargetUser);
            expect(castedResult.id).toBe(dao.id);
            expect(castedResult.createdAt).toBe(dao.createdAt);
            expect(castedResult.updatedAt).toBe(dao.updatedAt);
            expect(castedResult.cycleId).toBe(dao.cycleId);
            expect(castedResult.userId).toBe(dao.userId);
            expect(castedResult.responsibleId).toBe(dao.responsibleId);
            expect(castedResult.shareStatus).toBe(dao.shareStatus);
            expect(castedResult.user).not.toBeNull();
            expect(castedResult.user?.userId).toBe(dao.userId);
            expect(castedResult.responsible).not.toBeNull();
            expect(castedResult.responsible?.userId).toBe(dao.responsibleId);
        });

        it('should fail if the target users could not be listed', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getTargetUser(cycleId, dao.userId, fakeFlag)).rejects.toThrow(DatabaseError);
        });
    });

    describe('isUserResponsibleOfCycle', () => {
        let cycleId: number;
        let userId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            userId = fakePositiveInteger();
        });

        it('should return true if user id is responsible of at least 1 user', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockResolvedValueOnce(faker.number.int({ min: 1 }));

            const result = await performanceReviewsRepositoryAdapter.isUserResponsibleOfCycle(cycleId, userId);

            expect(result).toBe(true);
        });

        it('should return false if user id is not responsible of any users', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockResolvedValueOnce(0);

            const result = await performanceReviewsRepositoryAdapter.isUserResponsibleOfCycle(cycleId, userId);

            expect(result).toBe(false);
        });

        it('should throw an error if the target users could not be deleted', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.isUserResponsibleOfCycle(cycleId, userId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteTargetUsers', () => {
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
        });

        it('should delete all cycle target users', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteTargetUsers(fakeId)).resolves.not.toThrow();
        });

        it('should throw an error if the target users could not be deleted', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteTargetUsers(fakeId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteTargetUsersOfCycleByIds', () => {
        let ids: number[];
        let cycleId: number;

        beforeEach(() => {
            ids = faker.helpers.multiple(faker.number.int);
            cycleId = fakePositiveInteger();
        });

        it('should delete all target users with ids in cycle', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'destroy').mockResolvedValueOnce(ids.length);

            await expect(performanceReviewsRepositoryAdapter.deleteTargetUsersOfCycleByIds(cycleId, ids)).resolves.not.toThrow();
        });

        it('should throw an error if the target users could not be deleted', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteTargetUsersOfCycleByIds(cycleId, ids)).rejects.toThrow(DatabaseError);
        });
    });

    describe('duplicateFormReviews', () => {
        let performanceReviewFormReviews: DuplicatePerformanceReviewFormReviewInterface[];
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const formReviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generateRandomReviewDuplication());
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should duplicate form reviews`, async () => {
            performanceReviewFormReviews.forEach(() =>
                jest.spyOn(PerformanceReviewFormReviewDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormReviewCreationFields;
                    const model = new PerformanceReviewFormReviewDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewFormReviewDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.type = attributes.type;
                    model.status = attributes.status;
                    model.shareStatus = attributes.shareStatus;
                    model.responsibleId = attributes.responsibleId;
                    model.externalReviewerId = attributes.externalReviewerId;
                    model.reviewerId = attributes.reviewerId;
                    model.reviewedId = attributes.reviewedId;
                    model.coreFormId = attributes.coreFormId;
                    model.coreFormTag = attributes.coreFormTag;
                    model.coreFormAnswerId = attributes.coreFormAnswerId;
                    model.formScore = attributes.formScore!;
                    model.goalsScore = attributes.goalsScore!;
                    model.competenciesScore = attributes.competenciesScore!;
                    model.score = attributes.score!;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewFormReviewDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewFormReviewCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewFormReviewDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.type = attributes.type;
                    dao.status = attributes.status;
                    dao.shareStatus = attributes.shareStatus;
                    dao.responsibleId = attributes.responsibleId;
                    dao.externalReviewerId = attributes.externalReviewerId;
                    dao.reviewerId = attributes.reviewerId;
                    dao.reviewedId = attributes.reviewedId;
                    dao.coreFormId = attributes.coreFormId;
                    dao.coreFormTag = attributes.coreFormTag;
                    dao.coreFormAnswerId = attributes.coreFormAnswerId;
                    dao.score = attributes.score!;
                    dao.goalsScore = attributes.goalsScore!;
                    dao.competenciesScore = attributes.competenciesScore!;
                    dao.formScore = attributes.formScore!;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.duplicateFormReviews(performanceReviewFormReviews);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormReview);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(performanceReviewFormReviews[index].cycleId);
                expect(result.type).toBe(performanceReviewFormReviews[index].type);
                expect(result.status).toBe(performanceReviewFormReviews[index].status);
                expect(result.shareStatus).toBe(performanceReviewFormReviews[index].shareStatus);
                expect(result.responsibleId).toBe(performanceReviewFormReviews[index].responsibleId);
                expect(result.reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
                expect(result.externalReviewerId).toBe(performanceReviewFormReviews[index].externalReviewerId);
                expect(result.reviewedId).toBe(performanceReviewFormReviews[index].reviewedId);
                expect(result.coreFormId).toBe(performanceReviewFormReviews[index].coreFormId);
                expect(result.coreFormTag).toBe(performanceReviewFormReviews[index].coreFormTag);
                expect(result.coreFormAnswerId).toBe(performanceReviewFormReviews[index].coreFormAnswerId);
                expect(result.score).toBe(performanceReviewFormReviews[index].score);
                expect(result.goalsScore).toBe(performanceReviewFormReviews[index].goalsScore);
                expect(result.competenciesScore).toBe(performanceReviewFormReviews[index].competenciesScore);
                expect(result.formScore).toBe(performanceReviewFormReviews[index].formScore);
            });
        });

        it('should throw an error if the target users could not be saved', async () => {
            performanceReviewFormReviews.forEach(() =>
                jest.spyOn(PerformanceReviewFormReviewDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewFormReviewDAO(creationAttributes as CreationAttributes<PerformanceReviewFormReviewDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewFormReviewDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.duplicateFormReviews(performanceReviewFormReviews)).rejects.toThrow(DatabaseError);
        });
    });

    describe('createFormReviews', () => {
        let performanceReviewFormReviews: PerformanceReviewFormReviewForCreation[];
        let fakeCycleId: number;
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            fakeCycleId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewFormReviewForCreation(fakeCycleId));
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create form reviews`, async () => {
            performanceReviewFormReviews.forEach(() =>
                jest.spyOn(PerformanceReviewFormReviewDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormReviewCreationFields;
                    const model = new PerformanceReviewFormReviewDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewFormReviewDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.type = attributes.type;
                    model.status = attributes.status;
                    model.shareStatus = attributes.shareStatus;
                    model.responsibleId = attributes.responsibleId;
                    model.externalReviewerId = attributes.externalReviewerId;
                    model.reviewerId = attributes.reviewerId;
                    model.reviewedId = attributes.reviewedId;
                    model.coreFormId = attributes.coreFormId;
                    model.coreFormTag = attributes.coreFormTag;
                    model.coreFormAnswerId = attributes.coreFormAnswerId;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewFormReviewDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewFormReviewCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewFormReviewDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.type = attributes.type;
                    dao.status = attributes.status;
                    dao.shareStatus = attributes.shareStatus;
                    dao.responsibleId = attributes.responsibleId;
                    dao.externalReviewerId = attributes.externalReviewerId;
                    dao.reviewerId = attributes.reviewerId;
                    dao.reviewedId = attributes.reviewedId;
                    dao.coreFormId = attributes.coreFormId;
                    dao.coreFormTag = attributes.coreFormTag;
                    dao.coreFormAnswerId = attributes.coreFormAnswerId;
                    dao.score = null;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.createFormReviews(performanceReviewFormReviews);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormReview);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(performanceReviewFormReviews[index].cycleId);
                expect(result.type).toBe(performanceReviewFormReviews[index].type);
                expect(result.status).toBe(performanceReviewFormReviews[index].status);
                expect(result.shareStatus).toBe(performanceReviewFormReviews[index].shareStatus);
                expect(result.responsibleId).toBe(performanceReviewFormReviews[index].responsibleId);
                expect(result.reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
                expect(result.externalReviewerId).toBe(performanceReviewFormReviews[index].externalReviewerId);
                expect(result.reviewedId).toBe(performanceReviewFormReviews[index].reviewedId);
                expect(result.coreFormId).toBe(performanceReviewFormReviews[index].coreFormId);
                expect(result.coreFormTag).toBe(performanceReviewFormReviews[index].coreFormTag);
                expect(result.coreFormAnswerId).toBe(performanceReviewFormReviews[index].coreFormAnswerId);
                expect(result.score).toBeNull();
            });
        });

        it('should throw an error if the target users could not be saved', async () => {
            performanceReviewFormReviews.forEach(() =>
                jest.spyOn(PerformanceReviewFormReviewDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewFormReviewDAO(creationAttributes as CreationAttributes<PerformanceReviewFormReviewDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewFormReviewDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.createFormReviews(performanceReviewFormReviews)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormReviewById', () => {
        let reviewForm: PerformanceReviewFormReview;
        let fakeId: number;
        let fakeFlag: boolean;
        let cycleId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            cycleId = fakePositiveInteger();
            fakeId = fakePositiveInteger();
            fakeFlag = faker.datatype.boolean();
            reviewForm = generatePerformanceReviewFormReviewWithValues(fakeId, cycleId, { reviewerId: fakePositiveInteger(),externalReviewerId: fakePositiveInteger() });
        });

        it('should get a form review by id', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createPerformanceReviewFormReviewDAO(reviewForm);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getFormReviewById(fakeId, cycleId, fakeFlag);
            const castedResult = result as PerformanceReviewFormReview;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewFormReview);
            expect(castedResult.cycleId).toBe(reviewForm.cycleId);
            expect(castedResult.type).toBe(reviewForm.type);
            expect(castedResult.status).toBe(reviewForm.status);
            expect(castedResult.shareStatus).toBe(reviewForm.shareStatus);
            expect(castedResult.responsibleId).toBe(reviewForm.responsibleId);
            expect(castedResult.externalReviewerId).toBe(reviewForm.externalReviewerId);
            expect(castedResult.reviewerId).toBe(reviewForm.reviewerId);
            expect(castedResult.reviewedId).toBe(reviewForm.reviewedId);
            expect(castedResult.coreFormId).toBe(reviewForm.coreFormId);
            expect(castedResult.coreFormTag).toBe(reviewForm.coreFormTag);
            expect(castedResult.coreFormAnswerId).toBe(reviewForm.coreFormAnswerId);
            expect(castedResult.id).toBe(reviewForm.id);
            expect(castedResult.createdAt).toBe(reviewForm.createdAt);
            expect(castedResult.updatedAt).toBe(reviewForm.updatedAt);
            expect(castedResult.deletedAt).toBe(reviewForm.deletedAt);
            expect(castedResult.score).toBe(reviewForm.score);
        });

        it('should get a form review with review users', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createPerformanceReviewFormReviewDAO(reviewForm);
                dao.reviewer = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.reviewerId!));
                dao.reviewed = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.reviewedId));
                dao.externalReviewer = createExternalReviewerDAO(generateRandomExternalReviewer({ id: dao.externalReviewerId! }));
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getFormReviewById(fakeId, cycleId, fakeFlag);
            const castedResult = result as PerformanceReviewFormReview;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewFormReview);
            expect(castedResult.cycleId).toBe(reviewForm.cycleId);
            expect(castedResult.type).toBe(reviewForm.type);
            expect(castedResult.status).toBe(reviewForm.status);
            expect(castedResult.shareStatus).toBe(reviewForm.shareStatus);
            expect(castedResult.responsibleId).toBe(reviewForm.responsibleId);
            expect(castedResult.externalReviewerId).toBe(reviewForm.externalReviewerId);
            expect(castedResult.reviewerId).toBe(reviewForm.reviewerId);
            expect(castedResult.reviewedId).toBe(reviewForm.reviewedId);
            expect(castedResult.coreFormId).toBe(reviewForm.coreFormId);
            expect(castedResult.coreFormTag).toBe(reviewForm.coreFormTag);
            expect(castedResult.coreFormAnswerId).toBe(reviewForm.coreFormAnswerId);
            expect(castedResult.id).toBe(reviewForm.id);
            expect(castedResult.createdAt).toBe(reviewForm.createdAt);
            expect(castedResult.updatedAt).toBe(reviewForm.updatedAt);
            expect(castedResult.deletedAt).toBe(reviewForm.deletedAt);
            expect(castedResult.score).toBe(reviewForm.score);

            expect(castedResult.reviewed).not.toBeNull();
            expect(castedResult.reviewed).toBeInstanceOf(PerformanceReviewUser);
            expect((castedResult.reviewed as PerformanceReviewUser).userId).toBe(castedResult.reviewedId);
            expect(castedResult.reviewer).not.toBeNull();
            expect(castedResult.reviewer).toBeInstanceOf(PerformanceReviewUser);
            expect((castedResult.reviewer as PerformanceReviewUser).userId).toBe(castedResult.reviewerId);
            expect(castedResult.externalReviewer).not.toBeNull();
            expect(castedResult.externalReviewer).toBeInstanceOf(PerformanceReviewExternalReviewer);
            expect((castedResult.externalReviewer as PerformanceReviewExternalReviewer).id).toBe(castedResult.externalReviewerId);
        });

        it('should not get a form review', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findByPk').mockResolvedValueOnce(null);

            await expect(performanceReviewsRepositoryAdapter.getFormReviewById(fakeId, cycleId, fakeFlag)).resolves.toBeNull();
        });

        it('should throw an error if the form review could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findByPk').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewById(fakeId, cycleId, fakeFlag)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getEditRequestById', () => {
        let editRequest: PerformanceReviewFormReviewEditRequest;
        let fakeId: number;
        let cycleId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewEditRequestDAO);
            cycleId = fakePositiveInteger();
            fakeId = fakePositiveInteger();
            editRequest = generateRandomEditRequest({ cycleId, id: fakeId });
        });

        it('should get a edit request by id', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createEditRequestDAO(editRequest);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getEditRequestById(fakeId, cycleId);
            const castedResult = result as PerformanceReviewFormReviewEditRequest;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewFormReviewEditRequest);
            expect(castedResult.cycleId).toBe(editRequest.cycleId);
            expect(castedResult.status).toBe(editRequest.status);
            expect(castedResult.reviewType).toBe(editRequest.reviewType);
            expect(castedResult.reviewedId).toBe(editRequest.reviewedId);
            expect(castedResult.reviewerId).toBe(editRequest.reviewerId);
            expect(castedResult.reviewFormId).toBe(editRequest.reviewFormId);
            expect(castedResult.requestDescription).toBe(editRequest.requestDescription);
            expect(castedResult.resolutionDescription).toBe(editRequest.resolutionDescription);
            expect(castedResult.id).toBe(editRequest.id);
            expect(castedResult.createdAt).toBe(editRequest.createdAt);
            expect(castedResult.updatedAt).toBe(editRequest.updatedAt);
            expect(castedResult.deletedAt).toBe(editRequest.deletedAt);
        });

        it('should not get an edit request', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findByPk').mockResolvedValueOnce(null);

            await expect(performanceReviewsRepositoryAdapter.getEditRequestById(fakeId, cycleId)).resolves.toBeNull();
        });

        it('should throw an error if the edit request could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findByPk').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getEditRequestById(fakeId, cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormReviewsOfCycleForReviewerId', () => {
        let performanceReviewFormReviews: PerformanceReviewFormReview[];
        let cycleId: number;
        let reviewerId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            cycleId = fakePositiveInteger();
            reviewerId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, { reviewerId }));
        });

        it(`should list form reviews of user if none exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForReviewerId(cycleId, reviewerId);

            expect(result).toHaveLength(0);
        });

        it(`should list form reviews of user`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviews.map((formReview) => {
                    const dao = createPerformanceReviewFormReviewDAO(formReview);
                    const reviewedUser = generateRandomPerformanceReviewUser(dao.reviewedId);
                    dao.reviewed = createPerformanceReviewUserDAO(reviewedUser);
                    return dao;
                });
                return Promise.resolve(daos);
            });

            const result = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForReviewerId(cycleId, reviewerId);

            expect(result).toHaveLength(performanceReviewFormReviews.length);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormReview);
                expect(result.id).toBe(performanceReviewFormReviews[index].id);
                expect(result.externalReviewerId).toBe(performanceReviewFormReviews[index].externalReviewerId);
                expect(result.reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
                expect(result.reviewedId).toBe(performanceReviewFormReviews[index].reviewedId);
                expect(result.reviewed).not.toBeNull();
                expect((result.reviewed as PerformanceReviewUser).userId).toBe(result.reviewedId);
            });
        });

        it('should throw an error if the form reviews could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForReviewerId(cycleId, reviewerId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormReviewsOfCycleForExternalReviewerId', () => {
        let performanceReviewFormReviews: PerformanceReviewFormReview[];
        let cycleId: number;
        let externalReviewerId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            cycleId = fakePositiveInteger();
            externalReviewerId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, { externalReviewerId }));
        });

        it(`should list form reviews of  external reviewer if none exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForExternalReviewerId(cycleId, externalReviewerId);

            expect(result).toHaveLength(0);
        });

        it(`should list form reviews of external reviewer`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviews.map((formReview) => {
                    const dao = createPerformanceReviewFormReviewDAO(formReview);
                    const reviewedUser = generateRandomPerformanceReviewUser(dao.reviewedId);
                    dao.reviewed = createPerformanceReviewUserDAO(reviewedUser);
                    return dao;
                });
                return Promise.resolve(daos);
            });

            const result = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForExternalReviewerId(cycleId, externalReviewerId);

            expect(result).toHaveLength(performanceReviewFormReviews.length);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormReview);
                expect(result.id).toBe(performanceReviewFormReviews[index].id);
                expect(result.externalReviewerId).toBe(performanceReviewFormReviews[index].externalReviewerId);
                expect(result.reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
                expect(result.reviewedId).toBe(performanceReviewFormReviews[index].reviewedId);
                expect(result.reviewed).not.toBeNull();
                expect((result.reviewed as PerformanceReviewUser).userId).toBe(result.reviewedId);
            });
        });

        it('should throw an error if the form reviews could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForExternalReviewerId(cycleId, externalReviewerId)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('getFormReviewsOfCycleForReviewedId', () => {
        let performanceReviewFormReviews: PerformanceReviewFormReview[];
        let cycleId: number;
        let reviewedId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            cycleId = fakePositiveInteger();
            reviewedId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 5, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, { reviewedId, externalReviewerId: fakePositiveInteger() }));
        });

        it(`should list form reviews of user if none exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);

            const result = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForReviewedId(cycleId, reviewedId);

            expect(result).toHaveLength(0);
        });

        it(`should list form reviews of user`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviews.map((formReview) => {
                    const dao = createPerformanceReviewFormReviewDAO(formReview);
                    const reviewerUser = generateRandomPerformanceReviewUser(dao.reviewerId!);
                    dao.reviewer = createPerformanceReviewUserDAO(reviewerUser);
                    dao.externalReviewer = createExternalReviewerDAO(generateRandomExternalReviewer({ id: dao.externalReviewerId! }));
                    return dao;
                });
                return Promise.resolve(daos);
            });

            const result = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForReviewedId(cycleId, reviewedId);

            expect(result).toHaveLength(performanceReviewFormReviews.length);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewFormReview);
                expect(result.id).toBe(performanceReviewFormReviews[index].id);
                expect(result.reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
                expect(result.externalReviewerId).toBe(performanceReviewFormReviews[index].externalReviewerId);
                expect(result.reviewedId).toBe(performanceReviewFormReviews[index].reviewedId);
                expect(result.reviewer).not.toBeNull();
                expect((result.reviewer as PerformanceReviewUser).userId).toBe(result.reviewerId);
                expect(result.externalReviewer).not.toBeNull();
                expect((result.externalReviewer as PerformanceReviewExternalReviewer).id).toBe(result.externalReviewerId);
            });
        });

        it('should throw an error if the form reviews could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForReviewedId(cycleId, reviewedId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormReviewsOfCycleByFilters', () => {
        let dao: PerformanceReviewFormReviewDAO;
        let options: ListPerformanceReviewFormReviewsOptions;

        beforeEach(() => {
            const cycleId = fakePositiveInteger();
            const formReview = generateRandomPerformanceReviewFormReview(cycleId);
            dao = createPerformanceReviewFormReviewDAO(formReview);
            options = {
                pagination: generateRandomCursorPagination(),
                withFormAnswers: false,
                withUserDetails: false,
            };
        });

        it('should return empty if no reviews exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewFormReviewDAO, 'scope'), () => PerformanceReviewFormReviewDAO, 1);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);
            const { nextCursorId, reviewForms } = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleByFilters(dao.cycleId, options);

            expect(nextCursorId).toBeNull();
            expect(reviewForms).toHaveLength(0);
        });

        it('should list form reviews if one exists', async () => {
            options.pagination.limit = 1;
            mockImplementationTimes(jest.spyOn(PerformanceReviewFormReviewDAO, 'scope'), () => PerformanceReviewFormReviewDAO, 2);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { nextCursorId, reviewForms } = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleByFilters(dao.cycleId, options);

            expect(nextCursorId).toBeNull();
            expect(reviewForms).toHaveLength(1);
            expect(reviewForms[0].id).toBe(dao.id);
        });

        it('should list form reviews with non-null next cursor', async () => {
            options.pagination.limit = 1;
            const otherDao = createPerformanceReviewFormReviewDAO(generateRandomPerformanceReviewFormReview(dao.cycleId));
            mockImplementationTimes(jest.spyOn(PerformanceReviewFormReviewDAO, 'scope'), () => PerformanceReviewFormReviewDAO, 2);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao, otherDao]);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { nextCursorId, reviewForms } = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleByFilters(dao.cycleId, options);

            expect(nextCursorId).toBe(otherDao.id);
            expect(reviewForms).toHaveLength(1);
            expect(reviewForms[0].id).toBe(dao.id);
        });

        it('should list form reviews with list options', async () => {
            options.pagination.limit = 1;
            mockImplementationTimes(jest.spyOn(PerformanceReviewFormReviewDAO, 'scope'), () => PerformanceReviewFormReviewDAO, 2);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { nextCursorId, reviewForms } = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleByFilters(dao.cycleId, {
                evaluationType: dao.type,
                formAnswerConversionOptions: {
                    convertedAnswer: true,
                    humanReadableAnswer: true,
                },
                ids: [dao.id],
                pagination: options.pagination,
                sortingOptions: [['id', 'desc']],
                withFormAnswers: true,
                withUserDetails: true,
            });

            expect(nextCursorId).toBeNull();
            expect(reviewForms).toHaveLength(1);
            expect(reviewForms[0].id).toBe(dao.id);
        });

        it('should fail if the form reviews could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewFormReviewDAO, 'scope'), () => PerformanceReviewFormReviewDAO, 1);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycleByFilters(dao.cycleId, options)).rejects.toThrow(DatabaseError);
        });
    });

    describe('updateStatusOfPendingEditRequests', () => {
        let cycleId: number;
        let editRequestStatus: string;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            editRequestStatus = faker.helpers.arrayElement(Object.values(PerformanceReviewFormEditRequestStatus));
        });

        it('should update the status of pending edit requests of cycle', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'update').mockResolvedValueOnce([fakePositiveInteger()]);

            await expect(performanceReviewsRepositoryAdapter.updateStatusOfPendingEditRequests(cycleId, editRequestStatus)).resolves.not.toThrow();
        });

        it('should throw an error if the form reviews could not be updated', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.updateStatusOfPendingEditRequests(cycleId, editRequestStatus)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('updateStatusOfFormReviews', () => {
        let cycleId: number;
        let sourceStatuses: string[];
        let targetStatus: string;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            sourceStatuses = [faker.helpers.arrayElement(Object.values(PerformanceReviewFormStatus))];
            targetStatus = faker.helpers.arrayElement(Object.values(PerformanceReviewFormStatus));
        });

        it('should update the status of form reviews of the cycle with the given status', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'update').mockResolvedValueOnce([fakePositiveInteger()]);

            await expect(performanceReviewsRepositoryAdapter.updateStatusOfFormReviews(cycleId, sourceStatuses, targetStatus)).resolves.not.toThrow();
        });

        it('should throw an error if the form reviews could not be updated', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.updateStatusOfFormReviews(cycleId, sourceStatuses, targetStatus)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('updateShareStatusOfFinishedFormReviewsByShareStatus', () => {
        let cycleId: number;
        let currentShareStatus: string;
        let newShareStatus: string;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const shareStatuses = Object.values(PerformanceReviewFormShareStatus);
            currentShareStatus = faker.helpers.arrayElement(shareStatuses);
            newShareStatus = faker.helpers.arrayElement(shareStatuses.filter((status) => status !== currentShareStatus));
        });

        it('should update the share status of finished form reviews of cycle', async () => {
            const formReviewsCount = faker.number.int({ max: 5, min: 1 });
            const affectedFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() =>
                    generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, {
                        shareStatus: currentShareStatus,
                        status: PerformanceReviewFormStatus.FINISHED,
                    }),
                );
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewDAO, 'update').mockImplementationOnce(() => {
                affectedFormReviews.map((formReview) => {
                    formReview.shareStatus = newShareStatus;
                    return formReview;
                });
                return Promise.resolve([affectedFormReviews.length, affectedFormReviews]);
            });

            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfFinishedFormReviewsByShareStatus(
                cycleId,
                currentShareStatus,
                newShareStatus,
            );

            expect(result).toHaveLength(affectedFormReviews.length);

            affectedFormReviews.forEach((formReview) => {
                expect(formReview.shareStatus).toBe(newShareStatus);
            });
        });

        it('should update the share status of finished form reviews of cycle filtered by reviewedId', async () => {
            const reviewedId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 5, min: 1 });
            const affectedFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() =>
                    generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, {
                        reviewedId,
                        shareStatus: currentShareStatus,
                        status: PerformanceReviewFormStatus.FINISHED,
                    }),
                );
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewDAO, 'update').mockImplementationOnce(() => {
                affectedFormReviews.map((formReview) => {
                    formReview.shareStatus = newShareStatus;
                    return formReview;
                });
                return Promise.resolve([affectedFormReviews.length, affectedFormReviews]);
            });

            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfFinishedFormReviewsByShareStatus(
                cycleId,
                currentShareStatus,
                newShareStatus,
                reviewedId,
            );

            expect(result).toHaveLength(affectedFormReviews.length);

            affectedFormReviews.forEach((formReview) => {
                expect(formReview.shareStatus).toBe(newShareStatus);
                expect(formReview.reviewedId).toBe(reviewedId);
            });
        });

        it('should do nothing if no reviews are affected', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewDAO, 'update').mockResolvedValueOnce([0, []]);

            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfFinishedFormReviewsByShareStatus(
                cycleId,
                currentShareStatus,
                newShareStatus,
            );

            expect(result).toHaveLength(0);
        });

        it('should do nothing if current share status equals new share status', async () => {
            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfFinishedFormReviewsByShareStatus(
                cycleId,
                currentShareStatus,
                currentShareStatus,
            );

            expect(result).toHaveLength(0);
        });

        it('should throw an error if the form reviews could not be updated', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.updateShareStatusOfFinishedFormReviewsByShareStatus(cycleId, currentShareStatus, newShareStatus),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('updateShareStatusOfTargetUsersByShareStatus', () => {
        let cycleId: number;
        let currentShareStatus: string;
        let newShareStatus: string;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const shareStatuses = Object.values(PerformanceReviewFormShareStatus);
            currentShareStatus = faker.helpers.arrayElement(shareStatuses);
            newShareStatus = faker.helpers.arrayElement(shareStatuses.filter((status) => status !== currentShareStatus));
        });

        it('should update the share status of all target users of cycle', async () => {
            const targetUsersCount = faker.number.int({ max: 5, min: 1 });
            const affectedTargetUsers = Array(targetUsersCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewTargetUser(cycleId, fakePositiveInteger(), fakePositiveInteger(), currentShareStatus));
            jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockResolvedValueOnce([
                affectedTargetUsers.length,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                affectedTargetUsers.map((targetUser) => {
                    targetUser.shareStatus = newShareStatus;
                    return targetUser;
                }),
            ]);

            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfTargetUsersByShareStatus(
                cycleId,
                currentShareStatus,
                newShareStatus,
            );

            expect(result).toHaveLength(affectedTargetUsers.length);

            result.forEach((targetUser, index) => {
                expect(targetUser).toBeInstanceOf(PerformanceReviewTargetUser);
                expect(targetUser.id).toBe(affectedTargetUsers[index].id);
                expect(targetUser.cycleId).toBe(cycleId);
                expect(targetUser.shareStatus).toBe(newShareStatus);
            });
        });

        it('should update the share status of target users of cycle filtered by userId', async () => {
            const userIds = faker.helpers.multiple(faker.number.int);
            const targetUsersCount = faker.number.int({ max: 5, min: 1 });
            const affectedTargetUsers = Array(targetUsersCount)
                .fill(1)
                .map(() =>
                    generateRandomPerformanceReviewTargetUser(
                        cycleId,
                        faker.helpers.arrayElement(userIds),
                        fakePositiveInteger(),
                        currentShareStatus,
                    ),
                );
            jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockResolvedValueOnce([
                affectedTargetUsers.length,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                affectedTargetUsers.map((targetUser) => {
                    targetUser.shareStatus = newShareStatus;
                    return targetUser;
                }),
            ]);

            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfTargetUsersByShareStatus(
                cycleId,
                currentShareStatus,
                newShareStatus,
                userIds,
            );

            expect(result).toHaveLength(affectedTargetUsers.length);

            const userIdsSet = new Set(userIds);
            result.forEach((targetUser, index) => {
                expect(targetUser).toBeInstanceOf(PerformanceReviewTargetUser);
                expect(targetUser.id).toBe(affectedTargetUsers[index].id);
                expect(targetUser.cycleId).toBe(cycleId);
                expect(targetUser.shareStatus).toBe(newShareStatus);
                expect(userIdsSet.has(targetUser.userId)).toBeTrue();
            });
        });

        it('should do nothing if no reviews are affected', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockResolvedValueOnce([0, []]);

            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfTargetUsersByShareStatus(
                cycleId,
                currentShareStatus,
                newShareStatus,
            );

            expect(result).toHaveLength(0);
        });

        it('should do nothing if current share status equals new share status', async () => {
            const result = await performanceReviewsRepositoryAdapter.updateShareStatusOfTargetUsersByShareStatus(
                cycleId,
                currentShareStatus,
                currentShareStatus,
            );

            expect(result).toHaveLength(0);
        });

        it('should throw an error if the form reviews could not be updated', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.updateShareStatusOfTargetUsersByShareStatus(cycleId, currentShareStatus, newShareStatus),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteFormReviews', () => {
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
        });

        it('should delete all cycle form reviews', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteFormReviews(fakeId)).resolves.not.toThrow();
        });

        it('should throw an error if the form reviews could not be deleted', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteFormReviews(fakeId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteFormReviewsOfCycleForReviewedId', () => {
        let cycleId: number;
        let reviewedId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            reviewedId = fakePositiveInteger();
        });

        it('should delete all form reviews of reviewed user', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteFormReviewsOfCycleForReviewedId(cycleId, reviewedId)).resolves.not.toThrow();
        });

        it('should throw an error if the form reviews could not be deleted', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteFormReviewsOfCycleForReviewedId(cycleId, reviewedId)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('deleteCompetencyQuestionsWithAnswerForReviewedId', () => {
        let cycleId: number;
        let userId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            userId = fakePositiveInteger();
        });

        it('should delete all competency questions with answer for reviewed user', async () => {
            jest.spyOn(PerformanceReviewCompetencyQuestionWithAnswerDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteCompetencyQuestionsWithAnswerForReviewedId(cycleId, userId)).resolves.not.toThrow();
        });

        it('should throw an error if the competency questions could not be deleted', async () => {
            jest.spyOn(PerformanceReviewCompetencyQuestionWithAnswerDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteCompetencyQuestionsWithAnswerForReviewedId(cycleId, userId)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('deleteGoalQuestionsWithAnswerOfCycleForReviewedId', () => {
        let cycleId: number;
        let reviewedId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            reviewedId = fakePositiveInteger();
        });

        it('should delete all goal questions with answer for reviewed user', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteGoalQuestionsWithAnswerOfCycleForReviewedId(cycleId, reviewedId)).resolves.not.toThrow();
        });

        it('should throw an error if the goal questions could not be deleted', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteGoalQuestionsWithAnswerOfCycleForReviewedId(cycleId, reviewedId)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe.each([
        [
            'getReviewerUsersOfCycle',
            (cycleId: number, options: ListPerformanceReviewUsersOptions) =>
                performanceReviewsRepositoryAdapter.getReviewerUsersOfCycle(cycleId, options),
        ],
        [
            'getReviewedUsersOfCycle',
            (cycleId: number, options: ListPerformanceReviewUsersOptions) =>
                performanceReviewsRepositoryAdapter.getReviewedUsersOfCycle(cycleId, options),
        ],
        [
            'getExternalReviewerUsersOfCycle',
            (cycleId: number, options: ListPerformanceReviewUsersOptions) =>
                performanceReviewsRepositoryAdapter.getExternalReviewerUsersOfCycle(cycleId, options),
        ],
    ])(
        '%s',
        (
            _: string,
            invokeFunction: (
                cycleId: number,
                options: ListPerformanceReviewUsersOptions,
            ) => Promise<{ totalCount: number; reviewUsers: PerformanceReviewUserWithStats[] }>,
        ) => {
            let cycleId: number;
            let offset: number;
            let limit: number;
            let performanceReviewUsers: PerformanceReviewUserDAO[];
            let withStatsByType: boolean;

            beforeEach(() => {
                cycleId = fakePositiveInteger();
                offset = fakePositiveInteger();
                limit = fakePositiveInteger();
                withStatsByType = faker.datatype.boolean();
                const reviewUserCount = faker.number.int({ max: 5, min: 1 });
                performanceReviewUsers = Array(reviewUserCount)
                    .fill(1)
                    .map(() => createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(fakePositiveInteger())));
            });

            it('should return empty if no reviewer users exist for cycle', async () => {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                jest.spyOn(mockSequelize, 'query').mockResolvedValueOnce([]);
                const { reviewUsers, totalCount } = await invokeFunction(cycleId, {});

                expect(totalCount).toBe(0);
                expect(reviewUsers).toHaveLength(0);
            });

            it('should list reviewer users if some exist', async () => {
                jest.spyOn(mockSequelize, 'query').mockResolvedValueOnce(
                    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                    // @ts-ignore:
                    performanceReviewUsers.map((reviewUser) => addStatsToPerformanceReviewUserDAO(reviewUser, performanceReviewUsers.length)),
                );
                jest.spyOn(performanceReviewsRepositoryAdapter as any, 'getUsersStatsByEvaluationType').mockResolvedValueOnce(
                    performanceReviewUsers.reduce((acc, reviewUser) => {
                        acc[reviewUser.userId] = generateRandomReviewUserStatsByType(
                            faker.helpers.arrayElements(Object.values(PerformanceReviewEvaluationTypes)),
                        );
                        return acc;
                    }, {} as { [userId: number]: UserStatsByType }),
                );
                const { reviewUsers, totalCount } = await invokeFunction(cycleId, { withStatsByType });

                expect(totalCount).toBe(performanceReviewUsers.length);
                expect(reviewUsers).toHaveLength(performanceReviewUsers.length);

                reviewUsers.forEach((reviewUser, index) => {
                    expect(reviewUser.userId).toBe(performanceReviewUsers[index].userId);
                    expect(reviewUser.firstName).toBe(performanceReviewUsers[index].firstName);
                    expect(reviewUser.lastName).toBe(performanceReviewUsers[index].lastName);
                    expect(reviewUser.profilePicture).toBe(performanceReviewUsers[index].profilePicture);
                    expect(reviewUser.deleted).toBe(performanceReviewUsers[index].deleted);
                    expect(reviewUser.employeeInternalId).toBe(performanceReviewUsers[index].employeeInternalId);
                    expect(reviewUser.filledReviews).toBeNumber();
                    expect(reviewUser.totalReviews).toBeNumber();
                    expect(reviewUser.pendingReviews).toBeNumber();
                    expect(reviewUser.status).toBeOneOf(Object.values(PerformanceReviewUserStatus));
                    expect(reviewUser.score).toBeDefined(); // This could be null or a random number (depending on filledReviews)
                    expect(reviewUser.statsPerEvaluationType).toBeDefined(); // This could be null or a random (depending on withStatsByType)
                });
            });

            it('should list reviewer users with list options', async () => {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore:
                jest.spyOn(mockSequelize, 'query').mockResolvedValueOnce([addStatsToPerformanceReviewUserDAO(performanceReviewUsers[0], 1)]);
                const { reviewUsers, totalCount } = await invokeFunction(cycleId, {
                    pagination: {
                        limit,
                        offset,
                    },
                    q: performanceReviewUsers[0].firstName,
                    responsibleId: fakePositiveInteger(),
                    sortingOptions: [['status', 'desc']],
                });

                expect(totalCount).toBe(1);
                expect(reviewUsers).toHaveLength(1);
                expect(reviewUsers[0].userId).toBe(performanceReviewUsers[0].userId);
            });

            it('should fail if the reviewer users could not be listed', async () => {
                jest.spyOn(mockSequelize, 'query').mockRejectedValueOnce(new Error());

                await expect(invokeFunction(cycleId, {})).rejects.toThrow(DatabaseError);
            });
        },
    );

    describe('getReviewerIdsWithPendingReviews', () => {
        let performanceReviewFormReviewsForExternal: PerformanceReviewFormReview[];
        let performanceReviewFormReviews: PerformanceReviewFormReview[];
        let cycleId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 5, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, { reviewerId: fakePositiveInteger() }));
            performanceReviewFormReviewsForExternal = Array(formReviewsCount)
                .fill(1)
                .map(() => generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, { externalReviewerId: fakePositiveInteger() }));
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
        });

        it(`should list reviewer ids with pending reviews if none exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);

            const { reviewerIds, externalReviewerIds } = await performanceReviewsRepositoryAdapter.getReviewerIdsWithPendingReviews(cycleId);

            expect(reviewerIds).toHaveLength(0);
            expect(externalReviewerIds).toHaveLength(0);
        });

        it(`should list reviewer ids with pending reviews if some exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviews.map((formReview) => {
                    return createPerformanceReviewFormReviewDAO(formReview);
                });
                return Promise.resolve(daos);
            });

            const { reviewerIds, externalReviewerIds } = await performanceReviewsRepositoryAdapter.getReviewerIdsWithPendingReviews(cycleId);

            expect(reviewerIds).toHaveLength(performanceReviewFormReviews.length);
            expect(externalReviewerIds).toHaveLength(0);

            reviewerIds.forEach((reviewerId, index) => {
                expect(reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
            });
        });

        it(`should list external reviewer ids with pending reviews if some exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviewsForExternal.map((formReview) => {
                    return createPerformanceReviewFormReviewDAO(formReview);
                });
                return Promise.resolve(daos);
            });
            const { reviewerIds, externalReviewerIds } = await performanceReviewsRepositoryAdapter.getReviewerIdsWithPendingReviews(cycleId);

            expect(reviewerIds).toHaveLength(0);
            expect(externalReviewerIds).toHaveLength(performanceReviewFormReviewsForExternal.length);

            externalReviewerIds.forEach((externalReviewerId, index) => {
                expect(externalReviewerId).toBe(performanceReviewFormReviewsForExternal[index].externalReviewerId);
            });
        });

        it(`should list reviewer and external reviewer ids with pending reviews if some exist`, async () => {

            const daos = performanceReviewFormReviews.map((formReview) => {
                return createPerformanceReviewFormReviewDAO(formReview);
            });
            const daosForExternal = performanceReviewFormReviewsForExternal.map((formReview) => {
                return createPerformanceReviewFormReviewDAO(formReview);
            });
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                return Promise.resolve([...daos, ...daosForExternal]);
            });
            const { reviewerIds, externalReviewerIds } = await performanceReviewsRepositoryAdapter.getReviewerIdsWithPendingReviews(cycleId);
 
            expect(reviewerIds).toHaveLength(performanceReviewFormReviews.length);
            expect(externalReviewerIds).toHaveLength(performanceReviewFormReviewsForExternal.length);

            reviewerIds.forEach((reviewerId, index) => {
                expect(reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
            });
            externalReviewerIds.forEach((externalReviewerId, index) => {
                expect(externalReviewerId).toBe(performanceReviewFormReviewsForExternal[index].externalReviewerId);
            });
        });

        it(`should list reviewer ids with pending reviews filtering by user ids`, async () => {
            const userIds = performanceReviewFormReviews.map((formReview) => formReview.reviewerId!) as number[];
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviews.map((formReview) => {
                    return createPerformanceReviewFormReviewDAO(formReview);
                });
                return Promise.resolve(daos);
            });

            const { reviewerIds, externalReviewerIds } = await performanceReviewsRepositoryAdapter.getReviewerIdsWithPendingReviews(cycleId, { userIds });

            expect(reviewerIds).toHaveLength(performanceReviewFormReviews.length);
            expect(externalReviewerIds).toHaveLength(0);

            reviewerIds.forEach((reviewerId, index) => {
                expect(reviewerId).toBe(performanceReviewFormReviews[index].reviewerId);
            });
        });

        it('should throw an error if the reviewer ids could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getReviewerIdsWithPendingReviews(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getCyclesWithPendingReviewsForReviewer', () => {
        let performanceReviewFormReviews: PerformanceReviewFormReview[];
        let cycleId: number;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 5, min: 1 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() => generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId));
        });

        it(`should get cycles with pending reviews for reviewer if none exist`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);

            const cycleIds = performanceReviewFormReviews.map((formReview) => formReview.cycleId) as number[];
            const reviewerId = performanceReviewFormReviews[0].reviewerId;
            const result = await performanceReviewsRepositoryAdapter.getCyclesWithPendingReviewsForReviewer(cycleIds, reviewerId!);

            expect(result).toHaveLength(0);
        });

        it(`should get cycles with pending reviews for reviewer`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockImplementationOnce(() => {
                const daos = performanceReviewFormReviews.map((formReview) => {
                    return createPerformanceReviewFormReviewDAO(formReview);
                });
                return Promise.resolve(daos);
            });

            const cycleIds = performanceReviewFormReviews.map((formReview) => formReview.cycleId) as number[];
            const reviewerId = performanceReviewFormReviews[0].reviewerId;
            const result = await performanceReviewsRepositoryAdapter.getCyclesWithPendingReviewsForReviewer(cycleIds, reviewerId!);

            expect(result).toHaveLength(performanceReviewFormReviews.length);

            result.forEach((cycleId, index) => {
                expect(cycleId).toBe(performanceReviewFormReviews[index].cycleId);
            });
        });

        it('should throw an error if the cycles ids could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            const cycleIds = performanceReviewFormReviews.map((formReview) => formReview.cycleId) as number[];
            const reviewerId = performanceReviewFormReviews[0].reviewerId;

            await expect(performanceReviewsRepositoryAdapter.getCyclesWithPendingReviewsForReviewer(cycleIds, reviewerId!)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('getUsersStatsByEvaluationType', () => {
        let performanceReviewFormReviews: PerformanceReviewFormReview[];
        let cycleId: number;
        let userId: number;
        let participantType: PerformanceReviewCycleParticipantType;

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            userId = fakePositiveInteger();
            const formReviewsCount = faker.number.int({ max: 5, min: 2 });
            performanceReviewFormReviews = Array(formReviewsCount)
                .fill(1)
                .map(() =>
                    generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, {
                        coreFormAnswerId: fakePositiveInteger(),
                        reviewedId: userId,
                        type: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                    }),
                );
            participantType = faker.helpers.arrayElement(Object.values(PerformanceReviewCycleParticipantType));
        });

        it(`should get user stats by evaluation type with empty array`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValue([]);

            const result = await performanceReviewsRepositoryAdapter.getUsersStatsByEvaluationType(cycleId, [], participantType);

            expect(Object.keys(result)).toHaveLength(0);
        });
    });

    describe('getTargetUsersByFilters', () => {
        let cycleId: number;
        let targetUsers: PerformanceReviewTargetUser[];

        beforeEach(() => {
            cycleId = fakePositiveInteger();
            const targetUserCount = faker.number.int({ max: 5, min: 1 });
            targetUsers = Array(targetUserCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewTargetUser(cycleId, fakePositiveInteger(), fakePositiveInteger()));
        });

        it('should return empty if no target users exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTargetUserDAO, 'scope'), () => PerformanceReviewTargetUserDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 0, rows: [] });
            const { targetUsers, totalCount } = await performanceReviewsRepositoryAdapter.getTargetUsersByFilters(cycleId, {});

            expect(totalCount).toBe(0);
            expect(targetUsers).toHaveLength(0);
        });

        it('should list target users if target users have no responsible', async () => {
            targetUsers = Array(targetUsers.length)
                .fill(1)
                .map(() => generateRandomPerformanceReviewTargetUser(cycleId, fakePositiveInteger(), null));
            const daos = targetUsers.map((targetUser) => createPerformanceReviewTargetUserDAO(targetUser));

            mockImplementationTimes(jest.spyOn(PerformanceReviewTargetUserDAO, 'scope'), () => PerformanceReviewTargetUserDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAndCountAll').mockImplementationOnce(() => {
                const daosWithUsers = daos.map((dao) => {
                    dao.user = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.userId));
                    dao.responsible = dao.responsibleId
                        ? createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.responsibleId))
                        : undefined;
                    return dao;
                });
                return Promise.resolve({ count: daosWithUsers.length, rows: daosWithUsers });
            });

            const response = await performanceReviewsRepositoryAdapter.getTargetUsersByFilters(cycleId, {
                withResponsibleDetails: true,
            });

            expect(response.totalCount).toBe(daos.length);
            expect(response.targetUsers).toHaveLength(daos.length);

            response.targetUsers.forEach((targetUser, index) => {
                expect(targetUser.id).toBe(daos[index].id);
                expect(targetUser.responsible).toBeNull();
                expect(targetUser.user).not.toBeNull();
                expect(targetUser.user?.userId).toBe(targetUser.userId);
            });
        });

        it('should list target users if target users have responsible', async () => {
            const daos = targetUsers.map((targetUser) => createPerformanceReviewTargetUserDAO(targetUser));

            mockImplementationTimes(jest.spyOn(PerformanceReviewTargetUserDAO, 'scope'), () => PerformanceReviewTargetUserDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAndCountAll').mockImplementationOnce(() => {
                const daosWithUsers = daos.map((dao) => {
                    dao.user = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.userId));
                    dao.responsible = dao.responsibleId
                        ? createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.responsibleId))
                        : undefined;
                    return dao;
                });
                return Promise.resolve({ count: daosWithUsers.length, rows: daosWithUsers });
            });

            const response = await performanceReviewsRepositoryAdapter.getTargetUsersByFilters(cycleId, {
                withResponsibleDetails: true,
            });

            expect(response.totalCount).toBe(daos.length);
            expect(response.targetUsers).toHaveLength(daos.length);

            response.targetUsers.forEach((targetUser, index) => {
                expect(targetUser.id).toBe(daos[index].id);
                expect(targetUser.responsible).not.toBeNull();
                expect(targetUser.responsible?.userId).toBe(targetUser.responsibleId);
                expect(targetUser.user).not.toBeNull();
                expect(targetUser.user?.userId).toBe(targetUser.userId);
            });
        });

        it('should list target users with list options', async () => {
            const responsibleId = fakePositiveInteger();
            targetUsers.forEach((targetUser) => (targetUser.responsibleId = responsibleId));
            const daos = targetUsers.map((targetUser) => createPerformanceReviewTargetUserDAO(targetUser));

            mockImplementationTimes(jest.spyOn(PerformanceReviewTargetUserDAO, 'scope'), () => PerformanceReviewTargetUserDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAndCountAll').mockImplementationOnce(() => {
                const daosWithUsers = daos.map((dao) => {
                    dao.user = createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.userId));
                    dao.responsible = dao.responsibleId
                        ? createPerformanceReviewUserDAO(generateRandomPerformanceReviewUser(dao.responsibleId))
                        : undefined;
                    return dao;
                });
                return Promise.resolve({ count: daosWithUsers.length, rows: daosWithUsers });
            });

            const response = await performanceReviewsRepositoryAdapter.getTargetUsersByFilters(cycleId, {
                pagination: {
                    limit: daos.length,
                    offset: 0,
                },
                q: faker.word.words(),
                responsibleId,
                sortingOptions: [[literal(`concat("user"."firstName", ' ', "user"."lastName")`), 'desc']],
                withResponsibleDetails: faker.datatype.boolean(),
            });

            response.targetUsers.forEach((targetUser, index) => {
                expect(targetUser.id).toBe(daos[index].id);
                expect(targetUser.responsible).not.toBeNull();
                expect(targetUser.responsible?.userId).toBe(targetUser.responsibleId);
                expect(targetUser.user).not.toBeNull();
                expect(targetUser.user?.userId).toBe(targetUser.userId);
            });
        });

        it('should fail if the target users could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTargetUserDAO, 'scope'), () => PerformanceReviewTargetUserDAO, 1);
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAndCountAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getTargetUsersByFilters(cycleId, {})).rejects.toThrow(DatabaseError);
        });
    });

    describe('saveFormReview', () => {
        let performanceReviewForm: PerformanceReviewFormReview;

        beforeEach(() => {
            const cycleId = fakePositiveInteger();
            performanceReviewForm = generateRandomPerformanceReviewFormReview(cycleId);
        });

        it(`should update a review form`, async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormReviewDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormReviewDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormReviewCreationFields;
                    model.id = performanceReviewForm.id;
                    model.createdAt = performanceReviewForm.createdAt;
                    model.updatedAt = performanceReviewForm.updatedAt;
                    model.deletedAt = performanceReviewForm.deletedAt;
                    model.cycleId = attributes.cycleId;
                    model.type = attributes.type;
                    model.status = attributes.status;
                    model.shareStatus = attributes.shareStatus;
                    model.responsibleId = attributes.responsibleId;
                    model.externalReviewerId = attributes.externalReviewerId;
                    model.reviewerId = attributes.reviewerId;
                    model.reviewedId = attributes.reviewedId;
                    model.coreFormId = attributes.coreFormId;
                    model.coreFormTag = attributes.coreFormTag;
                    model.coreFormAnswerId = attributes.coreFormAnswerId;
                    model.score = performanceReviewForm.score;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.saveFormReview(performanceReviewForm);

            expect(result).toBeInstanceOf(PerformanceReviewFormReview);
            expect(result.id).toBe(performanceReviewForm.id);
            expect(result.createdAt).toBe(performanceReviewForm.createdAt);
            expect(result.updatedAt).toBe(performanceReviewForm.updatedAt);
            expect(result.deletedAt).toBe(performanceReviewForm.deletedAt);
            expect(result.cycleId).toBe(performanceReviewForm.cycleId);
            expect(result.type).toBe(performanceReviewForm.type);
            expect(result.status).toBe(performanceReviewForm.status);
            expect(result.shareStatus).toBe(performanceReviewForm.shareStatus);
            expect(result.externalReviewerId).toBe(performanceReviewForm.externalReviewerId);
            expect(result.reviewerId).toBe(performanceReviewForm.reviewerId);
            expect(result.reviewedId).toBe(performanceReviewForm.reviewedId);
            expect(result.responsibleId).toBe(performanceReviewForm.responsibleId);
            expect(result.coreFormId).toBe(performanceReviewForm.coreFormId);
            expect(result.coreFormTag).toBe(performanceReviewForm.coreFormTag);
            expect(result.coreFormAnswerId).toBe(performanceReviewForm.coreFormAnswerId);
            expect(result.score).toBe(performanceReviewForm.score);
        });

        it('should throw an error if the review cycle could not be saved', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormReviewDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormReviewDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.saveFormReview(performanceReviewForm)).rejects.toThrow(DatabaseError);
        });
    });

    describe('saveEditRequest', () => {
        let editRequest: PerformanceReviewFormReviewEditRequest;

        beforeEach(() => {
            const cycleId = fakePositiveInteger();
            editRequest = generateRandomEditRequest({ cycleId });
        });

        it(`should update an edit request`, async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormReviewEditRequestDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormReviewEditRequestDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormReviewEditRequestCreationFields;
                    model.id = editRequest.id;
                    model.createdAt = editRequest.createdAt;
                    model.updatedAt = editRequest.updatedAt;
                    model.deletedAt = editRequest.deletedAt;
                    model.cycleId = attributes.cycleId;
                    model.requestDescription = attributes.requestDescription;
                    model.reviewFormId = attributes.reviewFormId;
                    model.status = attributes.status;
                    model.reviewType = attributes.reviewType;
                    model.reviewedId = attributes.reviewerId;
                    model.reviewerId = attributes.reviewerId;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.saveEditRequest(editRequest);

            expect(result).toBeInstanceOf(PerformanceReviewFormReviewEditRequest);
            expect(result.id).toBe(editRequest.id);
            expect(result.createdAt).toBe(editRequest.createdAt);
            expect(result.updatedAt).toBe(editRequest.updatedAt);
            expect(result.deletedAt).toBe(editRequest.deletedAt);
            expect(result.requestDescription).toBe(editRequest.requestDescription);
            expect(result.resolutionDescription).toBe(editRequest.resolutionDescription);
            expect(result.cycleId).toBe(editRequest.cycleId);
            expect(result.reviewFormId).toBe(editRequest.reviewFormId);
            expect(result.status).toBe(editRequest.status);
            expect(result.reviewerId).toBe(editRequest.reviewerId);
        });

        it('should throw an error if the edit request could not be saved', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormReviewEditRequestDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormReviewEditRequestDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.saveEditRequest(editRequest)).rejects.toThrow(DatabaseError);
        });
    });

    describe('upsertReviewUsers', () => {
        let performanceReviewUsers: PerformanceReviewUser[];
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;

        beforeEach(() => {
            const reviewUserCount = faker.number.int({ max: 5, min: 1 });
            performanceReviewUsers = Array(reviewUserCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewUser(fakePositiveInteger()));
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
        });

        it(`should create review users`, async () => {
            performanceReviewUsers.forEach(() =>
                jest.spyOn(PerformanceReviewUserDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewUserCreationFields;
                    const model = new PerformanceReviewUserDAO(creationAttributes as CreationAttributes<PerformanceReviewUserDAO>, buildOptions);
                    model.deleted = attributes.deleted;
                    model.firstName = attributes.firstName;
                    model.lastName = attributes.lastName;
                    model.profilePicture = attributes.profilePicture;
                    model.userId = attributes.userId;
                    model.employeeInternalId = attributes.employeeInternalId;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewUserDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewUserCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewUserDAO();
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deleted = attributes.deleted;
                    dao.firstName = attributes.firstName;
                    dao.lastName = attributes.lastName;
                    dao.profilePicture = attributes.profilePicture;
                    dao.userId = attributes.userId;
                    dao.employeeInternalId = attributes.employeeInternalId;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.upsertReviewUsers(performanceReviewUsers);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewUser);
                expect(result.deleted).toBe(performanceReviewUsers[index].deleted);
                expect(result.employeeInternalId).toBe(performanceReviewUsers[index].employeeInternalId);
                expect(result.firstName).toBe(performanceReviewUsers[index].firstName);
                expect(result.lastName).toBe(performanceReviewUsers[index].lastName);
                expect(result.profilePicture).toBe(performanceReviewUsers[index].profilePicture);
                expect(result.userId).toBe(performanceReviewUsers[index].userId);
            });
        });

        it('should throw an error if the review users could not be saved', async () => {
            performanceReviewUsers.forEach(() =>
                jest.spyOn(PerformanceReviewUserDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewUserDAO(creationAttributes as CreationAttributes<PerformanceReviewUserDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewUserDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.upsertReviewUsers(performanceReviewUsers)).rejects.toThrow(DatabaseError);
        });
    });

    describe('markReviewUsersAsDeleted', () => {
        let userIds: number[];

        beforeEach(() => {
            userIds = faker.helpers.multiple(faker.number.int);
        });

        it('should mark review users as deleted', async () => {
            jest.spyOn(PerformanceReviewUserDAO, 'update').mockResolvedValueOnce([userIds.length]);

            await expect(performanceReviewsRepositoryAdapter.markReviewUsersAsDeleted(userIds)).resolves.not.toThrow();
        });

        it('should throw an error if the review users could not be marked as deleted', async () => {
            jest.spyOn(PerformanceReviewUserDAO, 'update').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.markReviewUsersAsDeleted(userIds)).rejects.toThrow(DatabaseError);
        });
    });

    describe('deletePeerReviewersOfTargetUser', () => {
        let userId: number;
        let cycleId: number;

        beforeEach(() => {
            userId = fakePositiveInteger();
            cycleId = fakePositiveInteger();

        });

        it('should delete peer reviewers of target user', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'destroy').mockResolvedValueOnce(userId);

            await expect(performanceReviewsRepositoryAdapter.deletePeerReviewersOfTargetUser(cycleId, userId)).resolves.not.toThrow();
        });

        it('should throw an error if peer reviewers of target user could not be marked as deleted', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deletePeerReviewersOfTargetUser(cycleId, userId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('listCyclesOfInstance', () => {
        let instanceId: number;
        let dao: PerformanceReviewCycleDAO;
        let pagination: CursorIdPagination;

        beforeEach(() => {
            instanceId = fakePositiveInteger();
            const performanceReviewCycle = generateRandomPerformanceReviewCycle();
            dao = createPerformanceReviewCycleDAO(performanceReviewCycle);
            pagination = generateRandomCursorPagination();
        });

        it('should return empty if no cycles exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([]);
            const { reviewCycles, nextCursorId } = await performanceReviewsRepositoryAdapter.listCyclesOfInstance(instanceId, pagination);

            expect(nextCursorId).toBeNull();
            expect(reviewCycles).toHaveLength(0);
        });

        it('should list cycles if one exists', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { reviewCycles, nextCursorId } = await performanceReviewsRepositoryAdapter.listCyclesOfInstance(instanceId, pagination);

            expect(nextCursorId).toBeNull();
            expect(reviewCycles).toHaveLength(1);
            expect(reviewCycles[0].id).toBe(dao.id);
        });

        it('should list cycles if one exists and status match', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { reviewCycles, nextCursorId } = await performanceReviewsRepositoryAdapter.listCyclesOfInstance(instanceId, pagination, [
                dao.status,
            ]);

            expect(nextCursorId).toBeNull();
            expect(reviewCycles).toHaveLength(1);
            expect(reviewCycles[0].id).toBe(dao.id);
        });

        it('should list form cycles with non-null next cursor', async () => {
            pagination.limit = 1;
            const otherPerformanceReviewCycle = generateRandomPerformanceReviewCycle();
            const otherDao = createPerformanceReviewCycleDAO(otherPerformanceReviewCycle);
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockResolvedValueOnce([dao, otherDao]);
            const { reviewCycles, nextCursorId } = await performanceReviewsRepositoryAdapter.listCyclesOfInstance(instanceId, pagination);

            expect(nextCursorId).toBe(otherDao.id);
            expect(reviewCycles).toHaveLength(1);
            expect(reviewCycles[0].id).toBe(dao.id);
        });

        it('should fail if the cycles could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewCycleDAO, 'scope'), () => PerformanceReviewCycleDAO, 1);
            jest.spyOn(PerformanceReviewCycleDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.listCyclesOfInstance(instanceId, pagination)).rejects.toThrow(DatabaseError);
        });
    });

    describe('listTemplatesOfInstance', () => {
        let instanceId: number;
        let dao: PerformanceReviewTemplateDAO;
        let pagination: CursorIdPagination;

        beforeEach(() => {
            instanceId = fakePositiveInteger();
            const template = generateRandomTemplate();
            dao = createPerformanceReviewTemplateDAO(template);
            pagination = generateRandomCursorPagination();
        });

        it('should return empty if no templates exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([]);
            const { templates, nextCursorId } = await performanceReviewsRepositoryAdapter.listTemplatesOfInstance(instanceId, pagination);

            expect(nextCursorId).toBeNull();
            expect(templates).toHaveLength(0);
        });

        it('should list templates if one exists', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { templates, nextCursorId } = await performanceReviewsRepositoryAdapter.listTemplatesOfInstance(instanceId, pagination);

            expect(nextCursorId).toBeNull();
            expect(templates).toHaveLength(1);
            expect(templates[0].id).toBe(dao.id);
        });

        it('should list templates with non-null next cursor', async () => {
            pagination.limit = 1;
            const otherTemplate = generateRandomTemplate();
            const otherDao = createPerformanceReviewTemplateDAO(otherTemplate);
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([dao, otherDao]);
            const { templates, nextCursorId } = await performanceReviewsRepositoryAdapter.listTemplatesOfInstance(instanceId, pagination);

            expect(nextCursorId).toBe(otherDao.id);
            expect(templates).toHaveLength(1);
            expect(templates[0].id).toBe(dao.id);
        });

        it('should fail if the templates could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.listTemplatesOfInstance(instanceId, pagination)).rejects.toThrow(DatabaseError);
        });
    });

    describe('createCycles', () => {
        let performanceReviews: PerformanceReviewCycleForCreation[];
        let fakeIds: number[];
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const reviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviews = Array(reviewsCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewCycleForCreation());
            fakeIds = faker.helpers.multiple(fakePositiveInteger, { count: reviewsCount });
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create review cycles`, async () => {
            performanceReviews.forEach(() =>
                jest.spyOn(PerformanceReviewCycleDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewCycleCreationFields;
                    const model = new PerformanceReviewCycleDAO(creationAttributes as CreationAttributes<PerformanceReviewCycleDAO>, buildOptions);
                    model.instanceId = attributes.instanceId;
                    model.name = attributes.name;
                    model.startDate = attributes.startDate;
                    model.endDate = attributes.endDate;
                    model.status = attributes.status;
                    model.reviewedUsersMode = attributes.reviewedUsersMode;
                    model.withSummary = attributes.withSummary;
                    model.reviewsVisibleAfterFinishedReviewCycle = attributes.reviewsVisibleAfterFinishedReviewCycle;
                    model.peerReviewReviewerSelectionVisibleForBosses = attributes.peerReviewReviewerSelectionVisibleForBosses;
                    model.duplicationId = attributes.duplicationId;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewCycleDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewCycleCreationFields[];
                const models = attributeArray.map((attributes, index) => {
                    const dao = new PerformanceReviewCycleDAO();
                    dao.id = fakeIds[index];
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.instanceId = attributes.instanceId;
                    dao.name = attributes.name;
                    dao.startDate = attributes.startDate;
                    dao.endDate = attributes.endDate;
                    dao.status = attributes.status;
                    dao.reviewedUsersMode = attributes.reviewedUsersMode;
                    dao.withSummary = attributes.withSummary;
                    dao.reviewsVisibleAfterFinishedReviewCycle = attributes.reviewsVisibleAfterFinishedReviewCycle;
                    dao.peerReviewReviewerSelectionVisibleForBosses = attributes.peerReviewReviewerSelectionVisibleForBosses;
                    dao.duplicationId = attributes.duplicationId;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.createCycles(performanceReviews);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewCycle);
                expect(result.id).toBe(fakeIds[index]);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.instanceId).toBe(performanceReviews[index].instanceId);
                expect(result.name).toBe(performanceReviews[index].name);
                expect(result.startDate).toBe(performanceReviews[index].startDate);
                expect(result.endDate).toBe(performanceReviews[index].endDate);
                expect(result.status).toBe(performanceReviews[index].status);
                expect(result.reviewedUsersMode).toBe(performanceReviews[index].reviewedUsersMode);
                expect(result.withSummary).toBe(performanceReviews[index].withSummary);
                expect(result.reviewsVisibleAfterFinishedReviewCycle).toBe(performanceReviews[index].reviewsVisibleAfterFinishedReviewCycle);
                expect(result.peerReviewReviewerSelectionVisibleForBosses).toBe(
                    performanceReviews[index].peerReviewReviewerSelectionVisibleForBosses,
                );
                expect(result.duplicationId).toBe(performanceReviews[index].duplicationId);
            });
        });

        it('should throw an error if the review cycle could not be saved', async () => {
            performanceReviews.forEach(() =>
                jest.spyOn(PerformanceReviewCycleDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewCycleDAO(creationAttributes as CreationAttributes<PerformanceReviewCycleDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewCycleDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.createCycles(performanceReviews)).rejects.toThrow(DatabaseError);
        });
    });

    describe('createExternalReviewers', () => {
        let externalReviewers: PerformanceReviewExternalReviewerForCreation[];
        let fakeIds: number[];
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const externalReviewerCount = faker.number.int({ max: 3, min: 1 });
            externalReviewers = Array(externalReviewerCount)
                .fill(1)
                .map(() => generateRandomExternalReviewerForCreation());
            fakeIds = faker.helpers.multiple(fakePositiveInteger, { count: externalReviewerCount });
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create external reviewers`, async () => {
            externalReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewExternalReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewExternalReviewerCreationFields;
                    const model = new PerformanceReviewExternalReviewerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewExternalReviewerDAO>,
                        buildOptions,
                    );
                    model.instanceId = attributes.instanceId;
                    model.firstName = attributes.firstName;
                    model.lastName = attributes.lastName;
                    model.email = attributes.email;
                    model.duplicationId = attributes.duplicationId;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewExternalReviewerCreationFields[];
                const models = attributeArray.map((attributes, index) => {
                    const dao = new PerformanceReviewExternalReviewerDAO();
                    dao.id = fakeIds[index];
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.instanceId = attributes.instanceId;
                    dao.firstName = attributes.firstName;
                    dao.lastName = attributes.lastName;
                    dao.email = attributes.email;
                    dao.duplicationId = attributes.duplicationId;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.createExternalReviewers(externalReviewers);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewExternalReviewer);
                expect(result.id).toBe(fakeIds[index]);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.instanceId).toBe(externalReviewers[index].instanceId);
                expect(result.firstName).toBe(externalReviewers[index].firstName);
                expect(result.lastName).toBe(externalReviewers[index].lastName);
                expect(result.email).toBe(externalReviewers[index].email);
                expect(result.duplicationId).toBe(externalReviewers[index].duplicationId);
            });
        });

        it('should throw an error if the external reviewers could not be saved', async () => {
            externalReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewExternalReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewExternalReviewerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewExternalReviewerDAO>,
                        buildOptions,
                    );
                }),
            );
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.createExternalReviewers(externalReviewers)).rejects.toThrow(DatabaseError);
        });
    });

    describe('createEditRequest', () => {
        let editRequest: PerformanceReviewFormReviewEditRequestForCreation;
        let fakeId: number;
        let fakeCreatedAt: Date;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
        });

        it(`should create a form edit request`, async () => {
            editRequest = generateRandomEditRequestForCreation();
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormReviewEditRequestDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormReviewEditRequestDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewFormReviewEditRequestCreationFields;
                    model.id = fakeId;
                    model.createdAt = fakeCreatedAt;
                    model.updatedAt = fakeCreatedAt;
                    model.status = attributes.status;
                    model.reviewType = attributes.reviewType;
                    model.reviewFormId = attributes.reviewFormId;
                    model.reviewedId = attributes.reviewedId;
                    model.reviewerId = attributes.reviewerId;
                    model.cycleId = attributes.cycleId;
                    model.requestDescription = attributes.requestDescription;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.createEditRequest(editRequest);

            expect(result).toBeInstanceOf(PerformanceReviewFormReviewEditRequest);
            expect(result.id).toBe(fakeId);
            expect(result.createdAt).toBe(fakeCreatedAt);
            expect(result.updatedAt).toBe(fakeCreatedAt);
            expect(result.reviewFormId).toBe(editRequest.reviewFormId);
            expect(result.reviewedId).toBe(editRequest.reviewedId);
            expect(result.reviewType).toBe(editRequest.reviewType);
            expect(result.reviewerId).toBe(editRequest.reviewerId);
            expect(result.cycleId).toBe(editRequest.cycleId);
            expect(result.requestDescription).toBe(editRequest.requestDescription);
            expect(result.status).toBe(editRequest.status);
        });

        it('should throw an error if the edit request could not be saved', async () => {
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewFormReviewEditRequestDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewFormReviewEditRequestDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.createEditRequest(editRequest)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getAllEditRequests', () => {
        let offset: number;
        let limit: number;
        let dao: PerformanceReviewFormReviewEditRequestDAO;

        beforeEach(() => {
            offset = fakePositiveInteger();
            limit = fakePositiveInteger();
            const editRequest = generateRandomEditRequest();
            dao = createEditRequestDAO(editRequest);
        });

        it('should return empty if no edit requests exist', async () => {
            mockImplementationTimes(
                jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'scope'),
                () => PerformanceReviewFormReviewEditRequestDAO,
                1,
            );
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 0, rows: [] });
            const { editRequests, totalCount } = await performanceReviewsRepositoryAdapter.getAllEditRequests(dao.cycleId, {});

            expect(totalCount).toBe(0);
            expect(editRequests).toHaveLength(0);
        });

        it('should list edit requests if one exists', async () => {
            mockImplementationTimes(
                jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'scope'),
                () => PerformanceReviewFormReviewEditRequestDAO,
                1,
            );
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 1, rows: [dao] });
            const { editRequests, totalCount } = await performanceReviewsRepositoryAdapter.getAllEditRequests(dao.cycleId, {});

            expect(totalCount).toBe(1);
            expect(editRequests).toHaveLength(1);
            expect(editRequests[0].id).toBe(dao.id);
        });

        it('should list edit requests with list options', async () => {
            mockImplementationTimes(
                jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'scope'),
                () => PerformanceReviewFormReviewEditRequestDAO,
                1,
            );

            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 1, rows: [dao] });
            const { editRequests, totalCount } = await performanceReviewsRepositoryAdapter.getAllEditRequests(dao.cycleId, {
                pagination: {
                    limit,
                    offset,
                },
                sortingOptions: [['id', 'desc']],
                statuses: [dao.status],
            });

            expect(totalCount).toBe(1);
            expect(editRequests).toHaveLength(1);
            expect(editRequests[0].id).toBe(dao.id);
        });

        it('should fail if the edit requests could not be listed', async () => {
            mockImplementationTimes(
                jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'scope'),
                () => PerformanceReviewFormReviewEditRequestDAO,
                1,
            );
            jest.spyOn(PerformanceReviewFormReviewEditRequestDAO, 'findAndCountAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getAllEditRequests(dao.cycleId, {})).rejects.toThrow(DatabaseError);
        });
    });

    describe('createTemplate', () => {
        let performanceReviewTemplate: PerformanceReviewTemplateForCreation;
        let fakeId: number;
        let fakeCreatedAt: Date;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
        });

        it(`should create a performance review template`, async () => {
            performanceReviewTemplate = generateRandomTemplateForCreation();
            jest.spyOn(PerformanceReviewTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewTemplateDAO(creationAttributes as CreationAttributes<PerformanceReviewTemplateDAO>, buildOptions);
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewTemplateCreationFields;
                    model.id = fakeId;
                    model.createdAt = fakeCreatedAt;
                    model.creatorId = attributes.creatorId;
                    model.updatedAt = fakeCreatedAt;
                    model.instanceId = attributes.instanceId;
                    model.questionTemplateId = attributes.questionTemplateId;
                    model.questionsCount = attributes.questionsCount;
                    model.status = attributes.status;
                    model.title = attributes.title;
                    model.formTemplateId = attributes.formTemplateId;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.createTemplate(performanceReviewTemplate);

            expect(result).toBeInstanceOf(PerformanceReviewTemplate);
            expect(result.id).toBe(fakeId);
            expect(result.createdAt).toBe(fakeCreatedAt);
            expect(result.creatorId).toBe(performanceReviewTemplate.creatorId);
            expect(result.updatedAt).toBe(fakeCreatedAt);
            expect(result.instanceId).toBe(performanceReviewTemplate.instanceId);
            expect(result.questionTemplateId).toBe(performanceReviewTemplate.questionTemplateId);
            expect(result.questionsCount).toBe(performanceReviewTemplate.questionsCount);
            expect(result.status).toBe(performanceReviewTemplate.status);
            expect(result.title).toBe(performanceReviewTemplate.title);
            expect(result.formTemplateId).toBe(performanceReviewTemplate.formTemplateId);
        });

        it('should throw an error if the performance review template could not be saved', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewTemplateDAO(creationAttributes as CreationAttributes<PerformanceReviewTemplateDAO>, buildOptions);
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.createTemplate(performanceReviewTemplate)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getTemplateById', () => {
        let performanceReviewTemplate: PerformanceReviewTemplate;
        let goalsQuestionTemplate: QuestionTemplate;
        let competenciesQuestionTemplate: QuestionTemplate;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'scope').mockImplementationOnce(() => PerformanceReviewTemplateDAO);
            performanceReviewTemplate = generateRandomTemplate();
        });

        it('should get a review template by id with form template and question template', async () => {
            goalsQuestionTemplate = generateRandomQuestionTemplate();
            competenciesQuestionTemplate = generateRandomQuestionTemplate();
            jest.spyOn(PerformanceReviewTemplateDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createPerformanceReviewTemplateDAO(performanceReviewTemplate, goalsQuestionTemplate, competenciesQuestionTemplate);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getTemplateById(
                performanceReviewTemplate.id,
                performanceReviewTemplate.instanceId,
            );
            const castedResult = result as PerformanceReviewTemplate;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewTemplate);
            expect(castedResult.id).toBe(performanceReviewTemplate.id);
            expect(castedResult.createdAt).toBe(performanceReviewTemplate.createdAt);
            expect(castedResult.updatedAt).toBe(performanceReviewTemplate.updatedAt);
            expect(castedResult.instanceId).toBe(performanceReviewTemplate.instanceId);
            expect(castedResult.formTemplateId).toBe(performanceReviewTemplate.formTemplateId);
            expect(castedResult.goalsQuestionTemplateId).toBe(performanceReviewTemplate.goalsQuestionTemplateId);
            expect(castedResult.goalsQuestionTemplate!.id).toBe(goalsQuestionTemplate.id);
            expect(castedResult.goalsQuestionTemplate!.instanceId).toBe(goalsQuestionTemplate.instanceId);
            expect(castedResult.formTemplate!.id).toBe(performanceReviewTemplate.formTemplateId);
            expect(castedResult.formTemplate!.instanceId).toBe(performanceReviewTemplate.instanceId);
            expect(castedResult.status).toBe(performanceReviewTemplate.status);
            expect(castedResult.title).toBe(performanceReviewTemplate.title);
            expect(castedResult.questionsCount).toBe(performanceReviewTemplate.questionsCount);
        });

        it('should not get a review template', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findByPk').mockResolvedValueOnce(null);

            await expect(
                performanceReviewsRepositoryAdapter.getTemplateById(performanceReviewTemplate.id, performanceReviewTemplate.instanceId),
            ).resolves.toBeNull();
        });

        it('should throw an error if the review cycle could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findByPk').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.getTemplateById(performanceReviewTemplate.id, performanceReviewTemplate.instanceId),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteTemplates', () => {
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
        });

        it('should delete a performance review template', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteTemplates([fakeId])).resolves.not.toThrow();
        });

        it('should throw an error if the performance review template could not be deleted', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteTemplates([fakeId])).rejects.toThrow(DatabaseError);
        });
    });

    describe('saveTemplate', () => {
        let template: PerformanceReviewTemplate;

        beforeEach(() => {
            template = generateRandomTemplate();
        });

        it(`should update a template`, async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewTemplateDAO(creationAttributes as CreationAttributes<PerformanceReviewTemplateDAO>, buildOptions);
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewTemplateCreationFields;
                    model.id = template.id;
                    model.createdAt = template.createdAt;
                    model.creatorId = template.creatorId;
                    model.questionTemplateId = template.questionTemplateId;
                    model.formTemplateId = template.formTemplateId;
                    model.updatedAt = template.updatedAt;
                    model.instanceId = attributes.instanceId;
                    model.status = attributes.status;
                    model.title = attributes.title;
                    model.questionsCount = attributes.questionsCount;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.saveTemplate(template);

            expect(result).toBeInstanceOf(PerformanceReviewTemplate);
            expect(result.id).toBe(template.id);
            expect(result.questionTemplateId).toBe(template.questionTemplateId);
            expect(result.formTemplateId).toBe(template.formTemplateId);
            expect(result.createdAt).toBe(template.createdAt);
            expect(result.creatorId).toBe(template.creatorId);
            expect(result.instanceId).toBe(template.instanceId);
            expect(result.title).toBe(template.title);
            expect(result.status).toBe(template.status);
            expect(result.questionsCount).toBe(template.questionsCount);
            expect(result.instanceId).toBe(template.instanceId);
        });

        it('should throw an error if the template could not be saved', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewTemplateDAO(creationAttributes as CreationAttributes<PerformanceReviewTemplateDAO>, buildOptions);
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.saveTemplate(template)).rejects.toThrow(DatabaseError);
        });
    });

    describe('listTemplates', () => {
        let offset: number;
        let limit: number;
        let dao: PerformanceReviewTemplateDAO;

        beforeEach(() => {
            offset = fakePositiveInteger();
            limit = fakePositiveInteger();
            const template = generateRandomTemplate();
            dao = createPerformanceReviewTemplateDAO(template, generateRandomQuestionTemplate());
        });

        it('should return empty if no templates exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 0, rows: [] });
            const { templates, totalCount } = await performanceReviewsRepositoryAdapter.listTemplates(dao.instanceId, {
                pagination: {
                    limit,
                    offset,
                },
                sortingOptions: [['id', 'desc']],
            });

            expect(totalCount).toBe(0);
            expect(templates).toHaveLength(0);
        });

        it('should list templates if one exists', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 1, rows: [dao] });
            const { templates, totalCount } = await performanceReviewsRepositoryAdapter.listTemplates(dao.instanceId, {
                pagination: {
                    limit,
                    offset,
                },
                sortingOptions: [['id', 'desc']],
            });

            expect(totalCount).toBe(1);
            expect(templates).toHaveLength(1);
            expect(templates[0].id).toBe(dao.id);
        });

        it('should fail if the templates could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewTemplateDAO, 'scope'), () => PerformanceReviewTemplateDAO, 1);
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAndCountAll').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.listTemplates(dao.instanceId, {
                    pagination: {
                        limit,
                        offset,
                    },
                    sortingOptions: [['id', 'desc']],
                }),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormReviewsOfCycle', () => {
        let cycleId: number;
        let dao: PerformanceReviewFormReviewDAO;
        let reviewerIds: number[];

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            cycleId = fakePositiveInteger();
            const formReview = generateRandomPerformanceReviewFormReview(cycleId);
            reviewerIds = [formReview.reviewerId!];
            dao = createPerformanceReviewFormReviewDAO(formReview);
        });

        it('should return empty if no form reviews exist', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);
            const formReviews = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycle(cycleId);

            expect(formReviews).toHaveLength(0);
        });

        it('should list form reviews if one exists', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            const formReviews = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycle(cycleId, reviewerIds);

            expect(formReviews).toHaveLength(1);
            expect(formReviews[0].id).toBe(dao.id);
        });

        it('should list form reviews if one exists, without reviewerIds filter', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            const formReviews = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycle(cycleId);

            expect(formReviews).toHaveLength(1);
            expect(formReviews[0].id).toBe(dao.id);
        });

        it('should fail if the form reviews could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycle(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getFormReviewsOfCycleForNineBox', () => {
        let cycleId: number;
        let dao: PerformanceReviewFormReviewDAO;
        let user: PerformanceReviewUser;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'scope').mockImplementationOnce(() => PerformanceReviewFormReviewDAO);
            cycleId = fakePositiveInteger();
            const formReview = generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycleId, {
                coreFormAnswerId: fakePositiveInteger(),
                goalsScore: fakePositiveInteger(),
                score: fakePositiveInteger(),
            });
            user = generateRandomPerformanceReviewUser(formReview.reviewedId);
            const userDao = createPerformanceReviewUserDAO(user);
            dao = createPerformanceReviewFormReviewDAO(formReview);
            dao.reviewed = userDao;
        });

        it('should return empty if no form reviews exist', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([]);
            const formReviews = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForNineBox(cycleId);

            expect(formReviews).toHaveLength(0);
        });

        it('should list form reviews if one exists', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);
            const nineBoxPoints = await performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForNineBox(cycleId);

            expect(nineBoxPoints).toHaveLength(1);
            expect(nineBoxPoints[0].formScore).toBe(dao.formScore);
            expect(nineBoxPoints[0].goalsScore).toBe(dao.goalsScore);
            expect(nineBoxPoints[0].user.userId).toBe(user.userId);
        });

        it('should fail if reviewed is undefined', async () => {
            dao.reviewed = undefined;
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockResolvedValueOnce([dao]);

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForNineBox(cycleId)).rejects.toThrow(IllegalStateError);
        });

        it('should fail if the form reviews could not be listed', async () => {
            jest.spyOn(PerformanceReviewFormReviewDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getFormReviewsOfCycleForNineBox(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getPerformanceReviewTemplateByFormTemplateId', () => {
        let dao: PerformanceReviewTemplateDAO;
        let reviewTemplate: PerformanceReviewTemplate;

        beforeEach(() => {
            reviewTemplate = generateRandomTemplate();
            dao = createPerformanceReviewTemplateDAO(reviewTemplate);
        });

        it('should get a performance review template from template id', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getPerformanceReviewTemplateByFormTemplateId(reviewTemplate.formTemplateId!);
            const castedResult = result as PerformanceReviewTemplate;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewTemplate);
            expect(castedResult.id).toBe(reviewTemplate.id);
            expect(castedResult.createdAt).toBe(reviewTemplate.createdAt);
            expect(castedResult.questionTemplateId).toBe(reviewTemplate.questionTemplateId);
            expect(castedResult.status).toBe(reviewTemplate.status);
            expect(castedResult.title).toBe(reviewTemplate.title);
            expect(castedResult.questionsCount).toBe(reviewTemplate.questionsCount);
            expect(castedResult.formTemplateId).toBe(reviewTemplate.formTemplateId);
        });

        it('should not get a performance review template', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([]);

            await expect(
                performanceReviewsRepositoryAdapter.getPerformanceReviewTemplateByFormTemplateId(reviewTemplate.formTemplateId!),
            ).resolves.toBeNull();
        });

        it('should throw an error if the performance review template could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.getPerformanceReviewTemplateByFormTemplateId(reviewTemplate.formTemplateId!),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('getPerformanceReviewTemplatesByFormTemplateIds', () => {
        let dao: PerformanceReviewTemplateDAO;
        let formTemplateIds: number[];

        beforeEach(() => {
            const reviewTemplate = generateRandomTemplate();
            dao = createPerformanceReviewTemplateDAO(reviewTemplate);
            formTemplateIds = [dao.formTemplateId!];
        });

        it('should return empty if no performance review template exist', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([]);
            const targetUsers = await performanceReviewsRepositoryAdapter.getPerformanceReviewTemplatesByFormTemplateIds(formTemplateIds);

            expect(targetUsers).toHaveLength(0);
        });

        it('should list performance review templates if one exists', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([dao]);
            const targetUsers = await performanceReviewsRepositoryAdapter.getPerformanceReviewTemplatesByFormTemplateIds(formTemplateIds);

            expect(targetUsers).toHaveLength(1);
            expect(targetUsers[0].id).toBe(dao.id);
        });

        it('should fail if the performance review templates could not be listed', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getPerformanceReviewTemplatesByFormTemplateIds(formTemplateIds)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('countReviewedsAssigned', () => {
        let cycleId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
            cycleId = fakePositiveInteger();
        });

        it('should count revieweds assigned', async () => {
            const reviewedAsginedCount = fakePositiveInteger();
            jest.spyOn(PerformanceReviewReviewerDAO, 'count').mockResolvedValueOnce(reviewedAsginedCount);
            const result = await performanceReviewsRepositoryAdapter.countReviewedsAssigned(cycleId);

            expect(result).toBe(reviewedAsginedCount);
        });

        it('should fail if the revieweds assigned could not be counted', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.countReviewedsAssigned(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('countTargetUsers', () => {
        let cycleId: number;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'scope').mockImplementationOnce(() => PerformanceReviewTargetUserDAO);
            cycleId = fakePositiveInteger();
        });

        it('should count total target users', async () => {
            const targetUserCount = fakePositiveInteger();
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockResolvedValueOnce(targetUserCount);
            const result = await performanceReviewsRepositoryAdapter.countTargetUsers(cycleId);

            expect(result).toBe(targetUserCount);
        });

        it('should fail if the target users could not be counted', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'count').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.countTargetUsers(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getRevieweds', () => {
        let cycleId: number;
        let reviewerIds: number[];
        let dao: PerformanceReviewReviewerDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
            cycleId = fakePositiveInteger();
        });

        it('should return empty if no reviewers exist', async () => {
            reviewerIds = [1, 2, 3];
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getRevieweds(cycleId, reviewerIds);

            expect(result).toHaveLength(0);
        });

        it('should list reviewers if one exists', async () => {
            const reviewReviewer = generatePerformanceReviewReviewer({ cycleId });
            dao = generatePerformanceReviewReviewerDAO(reviewReviewer);
            reviewerIds = [dao.reviewerId!];
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getRevieweds(cycleId, reviewerIds);

            expect(result).toHaveLength(1);
            expect(result[0].cycleId).toBe(dao.cycleId);
            expect(result[0].reviewedId).toBe(dao.reviewedId);
            expect(result[0].reviewerCount).toBe(dao.reviewerCount);
            expect(result[0].externalReviewerCount).toBe(dao.externalReviewerCount);
        });

        it('should fail if the reviewers could not be listed', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getRevieweds(cycleId, reviewerIds)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getAllReviewers', () => {
        let cycleId: number;
        let dao: PerformanceReviewReviewerDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
            cycleId = fakePositiveInteger();
        });

        it('should return empty if no reviewers exist', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getAllReviewers(cycleId);

            expect(result).toHaveLength(0);
        });

        it('should list reviewers if one exists', async () => {
            const model = generatePerformanceReviewReviewer({ cycleId });
            dao = generatePerformanceReviewReviewerDAO(model);
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getAllReviewers(cycleId);

            expect(result).toHaveLength(1);
            expect(result[0].cycleId).toBe(dao.cycleId);
            expect(result[0].reviewedId).toBe(dao.reviewedId);
            expect(result[0].externalReviewerId).toBe(dao.externalReviewerId);
            expect(result[0].reviewerId).toBe(dao.reviewerId);
        });

        it('should fail if the reviewers could not be listed', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getAllReviewers(cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getReviewers', () => {
        let cycleId: number;
        let options: ListPerformanceReviewReviewerUsersOptions;
        let dao: PerformanceReviewReviewerDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
            cycleId = fakePositiveInteger();
            options = {
                pagination: {
                    limit: 10,
                    offset: 0,
                },
            };
        });

        it('should return empty if no reviewers exist', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce({ count: 0, rows: [] });
            const result = await performanceReviewsRepositoryAdapter.getReviewers(cycleId, options);

            expect(result.reviewers).toHaveLength(0);
            expect(result.totalCount).toBe(0);
        });

        it('should list reviewers if one exists', async () => {
            const reviewReviewer = generatePerformanceReviewReviewer({ cycleId });
            dao = generatePerformanceReviewReviewerDAO(reviewReviewer);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([{ ...dao, amountOfreviewers: 1 }]);
            const result = await performanceReviewsRepositoryAdapter.getReviewers(cycleId, options);

            expect(result.reviewers).toHaveLength(1);
            expect(result.totalCount).toBe(1);

            expect(result.reviewers[0].cycleId).toBe(dao.cycleId);
            expect(result.reviewers[0].reviewedId).toBe(dao.reviewedId);
            expect(result.reviewers[0].reviewedCount).toBe(dao.reviewedCount);
        });

        it('should fail if the reviewers could not be listed', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getReviewers(cycleId, options)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getReviewersOfReviewed', () => {
        let cycleId: number;
        let reviewedId: number;
        let dao: PerformanceReviewReviewerDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
            cycleId = fakePositiveInteger();
            reviewedId = fakePositiveInteger();
        });

        it('should return empty if no reviewers exist', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getReviewersOfReviewed(cycleId, reviewedId);

            expect(result).toHaveLength(0);
        });

        it('should list reviewers if one exists', async () => {
            const reviewReviewer = generatePerformanceReviewReviewer({ cycleId, reviewedId });
            dao = generatePerformanceReviewReviewerDAO(reviewReviewer);
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getReviewersOfReviewed(cycleId, reviewedId);

            expect(result).toHaveLength(1);
            expect(result[0].cycleId).toBe(dao.cycleId);
            expect(result[0].reviewedId).toBe(dao.reviewedId);
            expect(result[0].reviewerId).toBe(dao.reviewerId);
            expect(result[0].externalReviewerId).toBe(dao.externalReviewerId);
        });

        it('should list reviewers using external flag', async () => {
            const reviewReviewer = generatePerformanceReviewReviewer({ cycleId, reviewedId });
            dao = generatePerformanceReviewReviewerDAO(reviewReviewer);
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getReviewersOfReviewed(cycleId, reviewedId);

            expect(result).toHaveLength(1);
            expect(result[0].cycleId).toBe(dao.cycleId);
            expect(result[0].reviewedId).toBe(dao.reviewedId);
            expect(result[0].reviewerId).toBe(dao.reviewerId);
            expect(result[0].externalReviewerId).toBe(dao.externalReviewerId);
        });

        it('should fail if the reviewers could not be listed', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getReviewersOfReviewed(cycleId, reviewedId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('upsertInternalReviewers', () => {
        let performanceReviewReviewers: PerformanceReviewReviewerForCreation[];
        let fakeIds: number[];
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const reviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewReviewers = Array(reviewsCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewReviewerForCreation());
            fakeIds = faker.helpers.multiple(fakePositiveInteger, { count: reviewsCount });
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create reviewers`, async () => {
            performanceReviewReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewReviewerCreationFields;
                    const model = new PerformanceReviewReviewerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewReviewerDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.reviewedId = attributes.reviewedId;
                    model.reviewerId = attributes.reviewerId;
                    model.externalReviewerId = attributes.externalReviewerId;
                    model.dataValues = model;
                    return model;
                }),
            );

            jest.spyOn(PerformanceReviewReviewerDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewReviewerCreationFields[];
                const models = attributeArray.map((attributes, index) => {
                    const dao = new PerformanceReviewReviewerDAO();
                    dao.id = fakeIds[index];
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.reviewedId = attributes.reviewedId;
                    dao.externalReviewerId = attributes.externalReviewerId;
                    dao.reviewerId = attributes.reviewerId;
                    return dao;
                });
                return Promise.resolve(models);
            });

            const result = await performanceReviewsRepositoryAdapter.upsertInternalReviewers(performanceReviewReviewers);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewReviewer);
                expect(result.id).toBe(fakeIds[index]);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(performanceReviewReviewers[index].cycleId);
                expect(result.reviewerId).toBe(performanceReviewReviewers[index].reviewerId);
                expect(result.externalReviewerId).toBe(performanceReviewReviewers[index].externalReviewerId);
                expect(result.reviewedId).toBe(performanceReviewReviewers[index].reviewedId);
            });
        });

        it('should throw an error if the reviewers could not be saved', async () => {
            performanceReviewReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewReviewerDAO(creationAttributes as CreationAttributes<PerformanceReviewReviewerDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewReviewerDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.upsertInternalReviewers(performanceReviewReviewers)).rejects.toThrow(DatabaseError);
        });
    });

    describe('upsertExternalReviewers', () => {
        let performanceReviewReviewers: PerformanceReviewReviewerForCreation[];
        let fakeIds: number[];
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const reviewsCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewReviewers = Array(reviewsCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewReviewerForCreation());
            fakeIds = faker.helpers.multiple(fakePositiveInteger, { count: reviewsCount });
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create reviewers`, async () => {
            performanceReviewReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewReviewerCreationFields;
                    const model = new PerformanceReviewReviewerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewReviewerDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.reviewedId = attributes.reviewedId;
                    model.reviewerId = attributes.reviewerId;
                    model.externalReviewerId = attributes.externalReviewerId;
                    model.dataValues = model;
                    return model;
                }),
            );

            jest.spyOn(PerformanceReviewReviewerDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewReviewerCreationFields[];
                const models = attributeArray.map((attributes, index) => {
                    const dao = new PerformanceReviewReviewerDAO();
                    dao.id = fakeIds[index];
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.reviewedId = attributes.reviewedId;
                    dao.externalReviewerId = attributes.externalReviewerId;
                    dao.reviewerId = attributes.reviewerId;
                    return dao;
                });
                return Promise.resolve(models);
            });

            const result = await performanceReviewsRepositoryAdapter.upsertExternalReviewers(performanceReviewReviewers);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewReviewer);
                expect(result.id).toBe(fakeIds[index]);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(performanceReviewReviewers[index].cycleId);
                expect(result.reviewerId).toBe(performanceReviewReviewers[index].reviewerId);
                expect(result.externalReviewerId).toBe(performanceReviewReviewers[index].externalReviewerId);
                expect(result.reviewedId).toBe(performanceReviewReviewers[index].reviewedId);
            });
        });

        it('should throw an error if the reviewers could not be saved', async () => {
            performanceReviewReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewReviewerDAO(creationAttributes as CreationAttributes<PerformanceReviewReviewerDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewReviewerDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.upsertExternalReviewers(performanceReviewReviewers)).rejects.toThrow(DatabaseError);
        });
    });

    describe('createReviewCyclesReviewers', () => {
        let performanceReviewReviewers: PerformanceReviewReviewerForCreation[];
        let fakeIds: number[];
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const reviewerCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewReviewers = Array(reviewerCount)
                .fill(1)
                .map(() => generateRandomPerformanceReviewReviewerForCreation());
            fakeIds = faker.helpers.multiple(fakePositiveInteger, { count: reviewerCount });
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create reviewers`, async () => {
            performanceReviewReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewReviewerDAO, 'build').mockImplementation((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewReviewerCreationFields;
                    const model = new PerformanceReviewReviewerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewReviewerDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.reviewedId = attributes.reviewedId;
                    model.reviewerId = attributes.reviewerId;
                    model.externalReviewerId = attributes.externalReviewerId;
                    model.dataValues = model;
                    return model;
                }),
            );

            jest.spyOn(PerformanceReviewReviewerDAO, 'bulkCreate').mockImplementation((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewReviewerCreationFields[];
                const models = attributeArray.map((attributes, index) => {
                    const dao = new PerformanceReviewReviewerDAO();
                    dao.id = fakeIds[index];
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.reviewedId = attributes.reviewedId;
                    dao.externalReviewerId = attributes.externalReviewerId;
                    dao.reviewerId = attributes.reviewerId;
                    return dao;
                });
                return Promise.resolve(models);
            });

            const result = await performanceReviewsRepositoryAdapter.createReviewCyclesReviewers(performanceReviewReviewers);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewReviewer);
                expect(result.id).toBe(fakeIds[index]);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(performanceReviewReviewers[index].cycleId);
                expect(result.reviewerId).toBe(performanceReviewReviewers[index].reviewerId);
                expect(result.externalReviewerId).toBe(performanceReviewReviewers[index].externalReviewerId);
                expect(result.reviewedId).toBe(performanceReviewReviewers[index].reviewedId);
            });
        });

        it('should throw an error if the reviewers could not be saved', async () => {
            performanceReviewReviewers.forEach(() =>
                jest.spyOn(PerformanceReviewReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewReviewerDAO(creationAttributes as CreationAttributes<PerformanceReviewReviewerDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewReviewerDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.upsertInternalReviewers(performanceReviewReviewers)).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteReviewers', () => {
        let reviewerIds: number[];
        let reviewedId: number;
        let isExternal: boolean;
        let evaluationType: PerformanceReviewPeerEvaluationTypesValues;

        beforeEach(() => {
            reviewerIds = [fakePositiveInteger()];
            reviewedId = fakePositiveInteger();
            isExternal = faker.datatype.boolean();
            evaluationType = faker.helpers.arrayElement(Object.values(PerformanceReviewPeerEvaluationTypes));
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
        });

        it('should delete a performance review reviewers', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteReviewers(reviewerIds, isExternal, reviewedId, evaluationType)).resolves.not.toThrow();
        });

        it('should throw an error if the performance review reviewers could not be deleted', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteReviewers(reviewerIds, isExternal, reviewedId, evaluationType)).rejects.toThrow(DatabaseError);
        });
    });

    describe('deleteReviewedsNotIn', () => {
        let reviewedsIds: number[];
        let cycleId: number;

        beforeEach(() => {
            reviewedsIds = [fakePositiveInteger()];
            cycleId = fakePositiveInteger();
            jest.spyOn(PerformanceReviewReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewReviewerDAO);
        });

        it('should delete revieweds', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteReviewedsNotIn(reviewedsIds, cycleId)).resolves.not.toThrow();
        });

        it('should throw an error if the revieweds could not be deleted', async () => {
            jest.spyOn(PerformanceReviewReviewerDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteReviewedsNotIn(reviewedsIds, cycleId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('newExternalReviewer', () => {
        let externalReviewer: PerformanceReviewExternalReviewerForCreation;
        let fakeId: number;
        let fakeCreatedAt: Date;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
        });

        it(`should create an external reviewer`, async () => {
            externalReviewer = generateRandomExternalReviewerForCreation();
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewExternalReviewerDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewExternalReviewerDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockImplementationOnce(async () => {
                    const attributes = creationAttributes as unknown as PerformanceReviewExternalReviewerCreationFields;
                    model.id = fakeId;
                    model.createdAt = fakeCreatedAt;
                    model.updatedAt = fakeCreatedAt;
                    model.instanceId = attributes.instanceId;
                    model.firstName = attributes.firstName;
                    model.lastName = attributes.lastName;
                    model.email = attributes.email;
                    return Promise.resolve(model);
                });
                return model;
            });
            const result = await performanceReviewsRepositoryAdapter.newExternalReviewer(externalReviewer);

            expect(result).toBeInstanceOf(PerformanceReviewExternalReviewer);
            expect(result.id).toBe(fakeId);
            expect(result.createdAt).toBe(fakeCreatedAt);
            expect(result.instanceId).toBe(externalReviewer.instanceId);
            expect(result.firstName).toBe(externalReviewer.firstName);
            expect(result.email).toBe(externalReviewer.email);
        });

        it('should throw an error if the external reviewer could not be saved', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                const model = new PerformanceReviewExternalReviewerDAO(
                    creationAttributes as CreationAttributes<PerformanceReviewExternalReviewerDAO>,
                    buildOptions,
                );
                jest.spyOn(model, 'save').mockRejectedValueOnce(new Error('Error'));
                return model;
            });

            await expect(performanceReviewsRepositoryAdapter.newExternalReviewer(externalReviewer)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getExternalReviewerByEmail', () => {
        let externalReviewer: PerformanceReviewExternalReviewer;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewExternalReviewerDAO);
            externalReviewer = generateRandomExternalReviewer();
        });

        it('should get an external reviewer by email', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findOne').mockImplementationOnce((_options) => {
                const dao = createExternalReviewerDAO(externalReviewer);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getExternalReviewerByEmail(externalReviewer.instanceId, externalReviewer.email);
            const castedResult = result as PerformanceReviewExternalReviewer;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewExternalReviewer);
            expect(castedResult.instanceId).toBe(externalReviewer.instanceId);
            expect(castedResult.email).toBe(externalReviewer.email);
            expect(castedResult.firstName).toBe(externalReviewer.firstName);
            expect(castedResult.id).toBe(externalReviewer.id);
            expect(castedResult.createdAt).toBe(externalReviewer.createdAt);
            expect(castedResult.updatedAt).toBe(externalReviewer.updatedAt);
            expect(castedResult.deletedAt).toBe(externalReviewer.deletedAt);
        });

        it('should not get an external reviewer', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findOne').mockResolvedValueOnce(null);

            await expect(
                performanceReviewsRepositoryAdapter.getExternalReviewerByEmail(externalReviewer.instanceId, externalReviewer.email),
            ).resolves.toBeNull();
        });

        it('should throw an error if the external reviewer could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findOne').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.getExternalReviewerByEmail(externalReviewer.instanceId, externalReviewer.email),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('listExternalReviewers', () => {
        let offset: number;
        let limit: number;
        let dao: PerformanceReviewExternalReviewerDAO;

        beforeEach(() => {
            offset = fakePositiveInteger();
            limit = fakePositiveInteger();
            const externalReviewer = generateRandomExternalReviewer();
            dao = createExternalReviewerDAO(externalReviewer);
        });

        it('should return empty if no external reviewers exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 0, rows: [] });
            const { externalReviewers, totalCount } = await performanceReviewsRepositoryAdapter.listExternalReviewers(dao.instanceId, {
                pagination: {
                    limit,
                    offset,
                },
                sortingOptions: [['id', 'desc']],
            });

            expect(totalCount).toBe(0);
            expect(externalReviewers).toHaveLength(0);
        });

        it('should list external reviewers if one exists', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 1, rows: [dao] });
            const { externalReviewers, totalCount } = await performanceReviewsRepositoryAdapter.listExternalReviewers(dao.instanceId, {
                pagination: {
                    limit,
                    offset,
                },
                sortingOptions: [['id', 'desc']],
            });

            expect(totalCount).toBe(1);
            expect(externalReviewers).toHaveLength(1);
            expect(externalReviewers[0].id).toBe(dao.id);
        });

        it('should list external reviewers if one exists with q', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAndCountAll').mockResolvedValueOnce({ count: 1, rows: [dao] });
            const { externalReviewers, totalCount } = await performanceReviewsRepositoryAdapter.listExternalReviewers(dao.instanceId, {
                pagination: {
                    limit,
                    offset,
                },
                q: dao.firstName,
                sortingOptions: [['id', 'desc']],
            });

            expect(totalCount).toBe(1);
            expect(externalReviewers).toHaveLength(1);
            expect(externalReviewers[0].id).toBe(dao.id);
        });

        it('should fail if the external reviewers could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAndCountAll').mockRejectedValueOnce(new Error());

            await expect(
                performanceReviewsRepositoryAdapter.listExternalReviewers(dao.instanceId, {
                    pagination: {
                        limit,
                        offset,
                    },
                    sortingOptions: [['id', 'desc']],
                }),
            ).rejects.toThrow(DatabaseError);
        });
    });

    describe('getExternalReviewers', () => {
        let instanceId: number;
        let ids: number[];
        let dao: PerformanceReviewExternalReviewerDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewExternalReviewerDAO);
            instanceId = fakePositiveInteger();
        });

        it('should return empty if no external reviewers exist', async () => {
            ids = [1, 2, 3];
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getExternalReviewers(instanceId, ids);

            expect(result).toHaveLength(0);
        });

        it('should list external reviewers if one exists', async () => {
            const externalReviewer = generateRandomExternalReviewer({ instanceId });
            dao = createExternalReviewerDAO(externalReviewer);
            ids = [dao.id];
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getExternalReviewers(instanceId, ids);

            expect(result).toHaveLength(1);
            expect(result[0].email).toBe(externalReviewer.email);
            expect(result[0].firstName).toBe(externalReviewer.firstName);
            expect(result[0].lastName).toBe(externalReviewer.lastName);
        });

        it('should fail if the external reviewers could not be listed', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getExternalReviewers(instanceId, ids)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getExternalReviewerById', () => {
        let externalReviewer: PerformanceReviewExternalReviewer;
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
            externalReviewer = generateRandomExternalReviewer({ id: fakeId });
        });

        it('should get a external reviewer by id', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findByPk').mockImplementationOnce((_options) => {
                const dao = createExternalReviewerDAO(externalReviewer);
                return Promise.resolve(dao);
            });
            const result = await performanceReviewsRepositoryAdapter.getExternalReviewerById(fakeId);
            const castedResult = result as PerformanceReviewExternalReviewer;

            expect(result).not.toBeNull();
            expect(castedResult).toBeInstanceOf(PerformanceReviewExternalReviewer);
            expect(castedResult.email).toBe(externalReviewer.email);
            expect(castedResult.firstName).toBe(externalReviewer.firstName);
            expect(castedResult.lastName).toBe(externalReviewer.lastName);
            expect(castedResult.id).toBe(externalReviewer.id);
            expect(castedResult.createdAt).toBe(externalReviewer.createdAt);
            expect(castedResult.updatedAt).toBe(externalReviewer.updatedAt);
            expect(castedResult.deletedAt).toBe(externalReviewer.deletedAt);
        });

        it('should not get an external reviewer', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findByPk').mockResolvedValueOnce(null);

            await expect(performanceReviewsRepositoryAdapter.getExternalReviewerById(fakeId)).resolves.toBeNull();
        });

        it('should throw an error if the edit request could not be retrieved', async () => {
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findByPk').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getExternalReviewerById(fakeId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getTemplatesByIds', () => {
        let dao: PerformanceReviewTemplateDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'scope').mockImplementationOnce(() => PerformanceReviewTemplateDAO);
        });

        it('should return empty if no templates exist', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.getTemplatesByIds([fakePositiveInteger()]);

            expect(result).toHaveLength(0);
        });

        it('should list templates if one exists', async () => {
            const template = generateRandomTemplate();
            dao = createPerformanceReviewTemplateDAO(template);
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockResolvedValueOnce([dao]);
            const result = await performanceReviewsRepositoryAdapter.getTemplatesByIds([dao.id]);

            expect(result).toHaveLength(1);
            expect(result[0].id).toBe(dao.id);
        });

        it('should fail if the templates could not be listed', async () => {
            jest.spyOn(PerformanceReviewTemplateDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getTemplatesByIds([fakePositiveInteger()])).rejects.toThrow(DatabaseError);
        });
    });

    describe('upsertGoalQuestionsWithAnswer', () => {
        let goalQuestionsWithAnswer: PerformanceReviewGoalQuestionWithAnswerForCreation[];
        let fakeCycleId: number;
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            fakeCycleId = fakePositiveInteger();
            const questionsCount = faker.number.int({ max: 3, min: 1 });
            goalQuestionsWithAnswer = Array(questionsCount)
                .fill(1)
                .map(() => generateRandomGoalQuestionWithAnswerForCreation(fakeCycleId));
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should upsert questions with answer`, async () => {
            goalQuestionsWithAnswer.forEach(() =>
                jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewGoalQuestionWithAnswerDAO;
                    const model = new PerformanceReviewGoalQuestionWithAnswerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewGoalQuestionWithAnswerDAO>,
                        buildOptions,
                    );
                    model.cycleId = attributes.cycleId;
                    model.userId = attributes.userId;
                    model.questionWithAnswer = attributes.questionWithAnswer;
                    model.goalId = attributes.goalId;
                    model.dataValues = model;
                    model.formReviewId = attributes.formReviewId;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewGoalQuestionWithAnswerCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewGoalQuestionWithAnswerDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.cycleId = attributes.cycleId;
                    dao.userId = attributes.userId;
                    dao.goalId = attributes.goalId;
                    dao.questionWithAnswer = attributes.questionWithAnswer;
                    dao.formReviewId = attributes.formReviewId;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.upsertGoalQuestionsWithAnswer(goalQuestionsWithAnswer);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewGoalQuestionWithAnswer);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.cycleId).toBe(goalQuestionsWithAnswer[index].cycleId);
                expect(result.userId).toBe(goalQuestionsWithAnswer[index].userId);
                expect(result.goalId).toBe(goalQuestionsWithAnswer[index].goalId);
                expect(result.questionWithAnswer).toBe(goalQuestionsWithAnswer[index].questionWithAnswer);
                expect(result.formReviewId).toBe(goalQuestionsWithAnswer[index].formReviewId);
            });
        });

        it('should throw an error if the questions with answer could not be saved', async () => {
            goalQuestionsWithAnswer.forEach(() =>
                jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewGoalQuestionWithAnswerDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewGoalQuestionWithAnswerDAO>,
                        buildOptions,
                    );
                }),
            );
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.upsertGoalQuestionsWithAnswer(goalQuestionsWithAnswer)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getGoalQuestionsWithAnswer', () => {
        let cycleId: number;
        let userId: number;
        let formReviewId: number;
        let dao: PerformanceReviewGoalQuestionWithAnswerDAO;

        beforeEach(() => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'scope').mockImplementationOnce(() => PerformanceReviewGoalQuestionWithAnswerDAO);
            cycleId = fakePositiveInteger();
            userId = fakePositiveInteger();
            const goalQuestion = generateGoalQuestionWithAnswer();
            dao = generateGoalQuestionWithAnswerDAO(goalQuestion);
            formReviewId = dao.formReviewId;
        });

        it('should return empty if no goal questions exist', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'findAll').mockResolvedValueOnce([]);
            const goalQuestions = await performanceReviewsRepositoryAdapter.getGoalQuestionsWithAnswer(cycleId, userId, formReviewId);

            expect(goalQuestions).toHaveLength(0);
        });

        it('should list goal questions if one exists', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const formReviews = await performanceReviewsRepositoryAdapter.getGoalQuestionsWithAnswer(cycleId, userId, formReviewId);

            expect(formReviews).toHaveLength(1);
            expect(formReviews[0].id).toBe(dao.id);
        });

        it('should list form reviews if one exists, with goal filter', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const formReviews = await performanceReviewsRepositoryAdapter.getGoalQuestionsWithAnswer(cycleId, userId, formReviewId, true);

            expect(formReviews).toHaveLength(1);
            expect(formReviews[0].id).toBe(dao.id);
        });

        it('should fail if the form reviews could not be listed', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.getGoalQuestionsWithAnswer(cycleId, userId, formReviewId)).rejects.toThrow(
                DatabaseError,
            );
        });
    });

    describe('deleteGoalQuestionsWithAnswerByIds', () => {
        let fakeId: number;

        beforeEach(() => {
            fakeId = fakePositiveInteger();
        });

        it('should delete all goal question from ids', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'destroy').mockResolvedValueOnce(fakePositiveInteger());

            await expect(performanceReviewsRepositoryAdapter.deleteGoalQuestionsWithAnswerByIds([fakeId])).resolves.not.toThrow();
        });

        it('should throw an error if the goal questions could not be deleted', async () => {
            jest.spyOn(PerformanceReviewGoalQuestionWithAnswerDAO, 'destroy').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.deleteGoalQuestionsWithAnswerByIds([fakeId])).rejects.toThrow(DatabaseError);
        });
    });

    describe('createTemplates', () => {
        let performanceReviewTemplates: PerformanceReviewTemplateForCreation[];
        let fakeId: number;
        let fakeCreatedAt: Date;
        let fakeUpdatedAt: Date;
        let fakeDeletedAt: Date | null;

        beforeEach(() => {
            const formTemplatesCount = faker.number.int({ max: 3, min: 1 });
            performanceReviewTemplates = Array(formTemplatesCount)
                .fill(1)
                .map(() => generateRandomTemplateForCreation());
            fakeId = fakePositiveInteger();
            fakeCreatedAt = faker.date.recent();
            fakeUpdatedAt = faker.date.recent();
            fakeDeletedAt = null;
        });

        it(`should create templates`, async () => {
            performanceReviewTemplates.forEach(() =>
                jest.spyOn(PerformanceReviewTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    const attributes = creationAttributes as unknown as PerformanceReviewTemplateCreationFields;
                    const model = new PerformanceReviewTemplateDAO(
                        creationAttributes as CreationAttributes<PerformanceReviewTemplateDAO>,
                        buildOptions,
                    );
                    model.creatorId = attributes.creatorId;
                    model.title = attributes.title;
                    model.status = attributes.status;
                    model.formTemplateId = attributes.formTemplateId;
                    model.questionTemplateId = attributes.questionTemplateId;
                    model.questionsCount = attributes.questionsCount;
                    model.instanceId = attributes.instanceId;
                    model.duplicationId = attributes.duplicationId;
                    model.dataValues = model;
                    return model;
                }),
            );
            jest.spyOn(PerformanceReviewTemplateDAO, 'bulkCreate').mockImplementationOnce((records, _options) => {
                const attributeArray = records as unknown as PerformanceReviewTemplateCreationFields[];
                const models = attributeArray.map((attributes) => {
                    const dao = new PerformanceReviewTemplateDAO();
                    dao.id = fakeId;
                    dao.createdAt = fakeCreatedAt;
                    dao.updatedAt = fakeUpdatedAt;
                    dao.deletedAt = fakeDeletedAt;
                    dao.creatorId = attributes.creatorId;
                    dao.title = attributes.title;
                    dao.status = attributes.status;
                    dao.formTemplateId = attributes.formTemplateId;
                    dao.questionTemplateId = attributes.questionTemplateId;
                    dao.questionsCount = attributes.questionsCount;
                    dao.instanceId = attributes.instanceId;
                    dao.duplicationId = attributes.duplicationId;
                    return dao;
                });
                return Promise.resolve(models);
            });
            const result = await performanceReviewsRepositoryAdapter.createTemplates(performanceReviewTemplates);

            result.forEach((result, index) => {
                expect(result).toBeInstanceOf(PerformanceReviewTemplate);
                expect(result.id).toBe(fakeId);
                expect(result.createdAt).toBe(fakeCreatedAt);
                expect(result.updatedAt).toBe(fakeUpdatedAt);
                expect(result.deletedAt).toBe(fakeDeletedAt);
                expect(result.title).toBe(performanceReviewTemplates[index].title);
                expect(result.status).toBe(performanceReviewTemplates[index].status);
                expect(result.formTemplateId).toBe(performanceReviewTemplates[index].formTemplateId);
                expect(result.questionTemplateId).toBe(performanceReviewTemplates[index].questionTemplateId);
                expect(result.duplicationId).toBe(performanceReviewTemplates[index].duplicationId);
                expect(result.questionsCount).toBe(performanceReviewTemplates[index].questionsCount);
            });
        });

        it('should throw an error if the templates could not be saved', async () => {
            performanceReviewTemplates.forEach(() =>
                jest.spyOn(PerformanceReviewTemplateDAO, 'build').mockImplementationOnce((creationAttributes, buildOptions) => {
                    return new PerformanceReviewTemplateDAO(creationAttributes as CreationAttributes<PerformanceReviewTemplateDAO>, buildOptions);
                }),
            );
            jest.spyOn(PerformanceReviewTemplateDAO, 'bulkCreate').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.createTemplates(performanceReviewTemplates)).rejects.toThrow(DatabaseError);
        });
    });

    describe('listExternalReviewersOfInstance', () => {
        let instanceId: number;
        let dao: PerformanceReviewExternalReviewerDAO;
        let pagination: CursorIdPagination;

        beforeEach(() => {
            instanceId = fakePositiveInteger();
            const performanceReviewCycle = generateRandomExternalReviewer();
            dao = createExternalReviewerDAO(performanceReviewCycle);
            pagination = generateRandomCursorPagination();
        });

        it('should return empty if no external reviewers exist', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockResolvedValueOnce([]);
            const { externalReviewers, nextCursorId } = await performanceReviewsRepositoryAdapter.listExternalReviewersOfInstance(
                instanceId,
                pagination,
            );

            expect(nextCursorId).toBeNull();
            expect(externalReviewers).toHaveLength(0);
        });

        it('should list external reviewers if one exists', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockResolvedValueOnce([dao]);
            const { externalReviewers, nextCursorId } = await performanceReviewsRepositoryAdapter.listExternalReviewersOfInstance(
                instanceId,
                pagination,
            );

            expect(nextCursorId).toBeNull();
            expect(externalReviewers).toHaveLength(1);
            expect(externalReviewers[0].id).toBe(dao.id);
        });

        it('should list external reviewers with non-null next cursor', async () => {
            pagination.limit = 1;
            const otherExternalReviewer = generateRandomExternalReviewer();
            const otherDao = createExternalReviewerDAO(otherExternalReviewer);
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockResolvedValueOnce([dao, otherDao]);
            const { externalReviewers, nextCursorId } = await performanceReviewsRepositoryAdapter.listExternalReviewersOfInstance(
                instanceId,
                pagination,
            );

            expect(nextCursorId).toBe(otherDao.id);
            expect(externalReviewers).toHaveLength(1);
            expect(externalReviewers[0].id).toBe(dao.id);
        });

        it('should fail if the external reviewers could not be listed', async () => {
            mockImplementationTimes(jest.spyOn(PerformanceReviewExternalReviewerDAO, 'scope'), () => PerformanceReviewExternalReviewerDAO, 1);
            jest.spyOn(PerformanceReviewExternalReviewerDAO, 'findAll').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.listExternalReviewersOfInstance(instanceId, pagination)).rejects.toThrow(DatabaseError);
        });
    });

    describe('userHasPendingReviewActions', () => {
        let userId: number;
        let subordinateIds: number[];
        let instanceId: number;

        beforeEach(() => {
            userId = fakePositiveInteger();
            subordinateIds = faker.helpers.multiple(fakePositiveInteger);
            instanceId = fakePositiveInteger();
        });

        it('should have pending review actions', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(mockSequelize, 'query').mockResolvedValueOnce([1]);
            const result = await performanceReviewsRepositoryAdapter.userHasPendingReviewActions(userId, subordinateIds, instanceId);

            expect(result).toBe(1);
        });

        it('should not have pending review actions', async () => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore:
            jest.spyOn(mockSequelize, 'query').mockResolvedValueOnce([]);
            const result = await performanceReviewsRepositoryAdapter.userHasPendingReviewActions(userId, subordinateIds, instanceId);

            expect(result).toBe(0);
        });

        it('should fail if the review actions could not be computed', async () => {
            jest.spyOn(mockSequelize, 'query').mockRejectedValueOnce(new Error());

            await expect(performanceReviewsRepositoryAdapter.userHasPendingReviewActions(userId, subordinateIds, instanceId)).rejects.toThrow(DatabaseError);
        });
    });

    describe('updateTargetUserScore', () => {
        it('updates the score for the (cycleId, userId) row', async () => {
            const cycleId = fakePositiveInteger();
            const userId = fakePositiveInteger();
            const score = 75;
            const updateMock = jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockResolvedValueOnce([1]);

            await performanceReviewsRepositoryAdapter.updateTargetUserScore(cycleId, userId, score);

            expect(updateMock).toHaveBeenCalledWith({ score }, { where: { cycleId, userId } });
        });

        it('persists null', async () => {
            const cycleId = fakePositiveInteger();
            const userId = fakePositiveInteger();
            const updateMock = jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockResolvedValueOnce([1]);

            await performanceReviewsRepositoryAdapter.updateTargetUserScore(cycleId, userId, null);

            expect(updateMock).toHaveBeenCalledWith({ score: null }, { where: { cycleId, userId } });
        });

        it('wraps DB errors in DatabaseError', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'update').mockRejectedValueOnce(new Error('boom'));

            await expect(performanceReviewsRepositoryAdapter.updateTargetUserScore(1, 1, 50)).rejects.toThrow(DatabaseError);
        });
    });

    describe('getTargetUsersScoresMap', () => {
        it('returns an empty object for empty userIds', async () => {
            const result = await performanceReviewsRepositoryAdapter.getTargetUsersScoresMap(1, []);

            expect(result).toEqual({});
        });

        it('returns a userId -> score map for present rows', async () => {
            const cycleId = 10;
            const rows = [
                { userId: 1, score: 80 },
                { userId: 2, score: null },
            ];
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockResolvedValueOnce(rows as never);

            const result = await performanceReviewsRepositoryAdapter.getTargetUsersScoresMap(cycleId, [1, 2, 3]);

            expect(result).toEqual({ 1: 80, 2: null });
        });

        it('wraps DB errors in DatabaseError', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findAll').mockRejectedValueOnce(new Error('boom'));

            await expect(performanceReviewsRepositoryAdapter.getTargetUsersScoresMap(1, [1])).rejects.toThrow(DatabaseError);
        });
    });

    describe('getAverageScoreForTargetUsers', () => {
        it('returns null for empty userIds', async () => {
            const result = await performanceReviewsRepositoryAdapter.getAverageScoreForTargetUsers(1, []);

            expect(result).toBeNull();
        });

        it('returns the average as a number', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockResolvedValueOnce({ avgScore: '78.5' } as never);

            const result = await performanceReviewsRepositoryAdapter.getAverageScoreForTargetUsers(1, [1, 2]);

            expect(result).toBe(78.5);
        });

        it('returns null when the aggregate is null', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockResolvedValueOnce({ avgScore: null } as never);

            const result = await performanceReviewsRepositoryAdapter.getAverageScoreForTargetUsers(1, [1, 2]);

            expect(result).toBeNull();
        });

        it('wraps DB errors in DatabaseError', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockRejectedValueOnce(new Error('boom'));

            await expect(performanceReviewsRepositoryAdapter.getAverageScoreForTargetUsers(1, [1])).rejects.toThrow(DatabaseError);
        });
    });

    describe('lockTargetUserRow', () => {
        it('issues a SELECT FOR UPDATE on the (cycleId, userId) row', async () => {
            const cycleId = fakePositiveInteger();
            const userId = fakePositiveInteger();
            const findOneMock = jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockResolvedValueOnce({ id: 1 } as never);

            await performanceReviewsRepositoryAdapter.lockTargetUserRow(cycleId, userId);

            expect(findOneMock).toHaveBeenCalledTimes(1);
            const callArgs = findOneMock.mock.calls[0][0]!;
            expect(callArgs.where).toEqual({ cycleId, userId });
            expect(callArgs.attributes).toEqual(['id']);
            // The actual lock value is sequelize-typescript's `Transaction.LOCK.UPDATE` enum,
            // which serialises to the string 'UPDATE'. Asserting on its presence (not on the
            // exact enum reference) makes the test resilient to import-shape changes.
            expect(callArgs.lock).toBeDefined();
        });

        it('does not throw when the row does not exist (null result)', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockResolvedValueOnce(null);

            await expect(performanceReviewsRepositoryAdapter.lockTargetUserRow(1, 1)).resolves.toBeUndefined();
        });

        it('wraps DB errors in DatabaseError', async () => {
            jest.spyOn(PerformanceReviewTargetUserDAO, 'findOne').mockRejectedValueOnce(new Error('boom'));

            await expect(performanceReviewsRepositoryAdapter.lockTargetUserRow(1, 1)).rejects.toThrow(DatabaseError);
        });
    });

    describe('bulkUpdateTargetUserScores', () => {
        it('does not call sequelize.query when the map is empty', async () => {
            await performanceReviewsRepositoryAdapter.bulkUpdateTargetUserScores(1, new Map());

            expect(mockSequelize.query).not.toHaveBeenCalled();
        });

        it('issues a single UPDATE FROM VALUES statement for a non-empty map', async () => {
            mockSequelize.query.mockResolvedValueOnce([]);
            const cycleId = 42;
            const scoresByUserId = new Map<number, number | null>([[10, 75]]);

            await performanceReviewsRepositoryAdapter.bulkUpdateTargetUserScores(cycleId, scoresByUserId);

            expect(mockSequelize.query).toHaveBeenCalledTimes(1);
            const [sql, options] = mockSequelize.query.mock.calls[0];
            expect(sql).toContain('UPDATE "PerformanceReviewTargetUsers"');
            expect(sql).toContain('FROM (VALUES ($userId0::int, $score0::int)) AS v("userId", score)');
            expect(sql).toContain('WHERE t."cycleId" = $cycleId AND t."userId" = v."userId"');
            expect(options).toEqual({ bind: { cycleId: 42, userId0: 10, score0: 75 }, type: 'UPDATE' });
        });

        it('persists null scores via $score::int bind (PG casts NULL correctly)', async () => {
            mockSequelize.query.mockResolvedValueOnce([]);
            const scoresByUserId = new Map<number, number | null>([[10, null]]);

            await performanceReviewsRepositoryAdapter.bulkUpdateTargetUserScores(7, scoresByUserId);

            expect(mockSequelize.query).toHaveBeenCalledTimes(1);
            const [, options] = mockSequelize.query.mock.calls[0];
            expect(options.bind).toEqual({ cycleId: 7, userId0: 10, score0: null });
        });

        it('chunks the map according to bulkCreateChunkSize', async () => {
            // Spin up an adapter with chunkSize=2 so 5 entries split into 3 chunks (2+2+1).
            const chunkedAdapter = new PerformanceReviewsRepositoryAdapter(2, mockSequelize);
            mockSequelize.query.mockResolvedValue([]);
            const scoresByUserId = new Map<number, number | null>([
                [1, 50],
                [2, 60],
                [3, 70],
                [4, 80],
                [5, 90],
            ]);

            await chunkedAdapter.bulkUpdateTargetUserScores(99, scoresByUserId);

            expect(mockSequelize.query).toHaveBeenCalledTimes(3);
            const firstChunkBind = mockSequelize.query.mock.calls[0][1].bind;
            expect(firstChunkBind).toEqual({ cycleId: 99, userId0: 1, score0: 50, userId1: 2, score1: 60 });
            const lastChunkBind = mockSequelize.query.mock.calls[2][1].bind;
            expect(lastChunkBind).toEqual({ cycleId: 99, userId0: 5, score0: 90 });
        });

        it('wraps DB errors in DatabaseError', async () => {
            mockSequelize.query.mockRejectedValueOnce(new Error('boom'));

            await expect(performanceReviewsRepositoryAdapter.bulkUpdateTargetUserScores(1, new Map([[1, 50]]))).rejects.toThrow(DatabaseError);
        });
    });
});
