first commit
CI / typecheck (push) Successful in 1m8s
CI / format (push) Failing after 1m6s
CI / lint (push) Failing after 49s
CI / test (push) Failing after 1m8s

This commit is contained in:
SDI
2026-07-15 18:05:12 +09:00
commit 12e4f17b62
4633 changed files with 817125 additions and 0 deletions
+379
View File
@@ -0,0 +1,379 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { faker } from '@faker-js/faker';
import * as bcrypt from 'bcrypt';
import { DateTime } from 'luxon';
import { CodeTypeEnum } from '@/shared/code/code-type.enum';
import type { CodeEntity } from '@/shared/code/code.entity';
import {
EventStatusEnum,
EventTypeEnum,
FieldFormatEnum,
FieldPropertyEnum,
FieldStatusEnum,
isSelectFieldFormat,
IssueStatusEnum,
WebhookStatusEnum,
} from '@/common/enums';
import type { ChannelEntity } from '@/domains/admin/channel/channel/channel.entity';
import type {
CreateFieldDto,
ReplaceFieldDto,
} from '@/domains/admin/channel/field/dtos';
import type { FieldEntity } from '@/domains/admin/channel/field/field.entity';
import type { OptionEntity } from '@/domains/admin/channel/option/option.entity';
import type { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import type { ApiKeyEntity } from '@/domains/admin/project/api-key/api-key.entity';
import type { IssueTrackerEntity } from '@/domains/admin/project/issue-tracker/issue-tracker.entity';
import type { CreateIssueDto } from '@/domains/admin/project/issue/dtos';
import type { IssueEntity } from '@/domains/admin/project/issue/issue.entity';
import type { MemberEntity } from '@/domains/admin/project/member/member.entity';
import type { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { PermissionEnum } from '@/domains/admin/project/role/permission.enum';
import type { RoleEntity } from '@/domains/admin/project/role/role.entity';
import type { EventEntity } from '@/domains/admin/project/webhook/event.entity';
import type { WebhookEntity } from '@/domains/admin/project/webhook/webhook.entity';
import type { TenantEntity } from '@/domains/admin/tenant/tenant.entity';
import {
SignUpMethodEnum,
UserStateEnum,
UserTypeEnum,
} from '@/domains/admin/user/entities/enums';
import type { UserEntity } from '@/domains/admin/user/entities/user.entity';
export const createFieldEntity = (input: Partial<CreateFieldDto>) => {
const format = input.format ?? getRandomEnumValue(FieldFormatEnum);
const property = input.property ?? getRandomEnumValue(FieldPropertyEnum);
const status = input.status ?? getRandomEnumValue(FieldStatusEnum);
return {
name: faker.string.alphanumeric(20),
description: faker.lorem.lines(2),
format,
property,
status,
options:
format === FieldFormatEnum.select ?
getRandomOptionEntities().sort(optionSort)
: undefined,
...input,
};
};
export const createFieldDto = (input: Partial<CreateFieldDto> = {}) => {
const format = input.format ?? getRandomEnumValue(FieldFormatEnum);
const property = input.property ?? getRandomEnumValue(FieldPropertyEnum);
const status = input.status ?? getRandomEnumValue(FieldStatusEnum);
return {
name: `_${faker.string.alphanumeric(20)}`,
key: `_${faker.string.alphanumeric(20)}`,
description: faker.lorem.lines(2),
format,
property,
status,
options:
isSelectFieldFormat(format) ?
getRandomOptionDtos().sort(optionSort)
: undefined,
...input,
};
};
export const updateFieldDto = (input: Partial<ReplaceFieldDto>) => {
return {
id: faker.number.int(),
...createFieldDto(input),
};
};
export const createIssueDto = (input: Partial<CreateIssueDto>) => {
return {
name: faker.string.alphanumeric(20),
...input,
};
};
export const getRandomValue = (
format: FieldFormatEnum,
options?: { id: number; name: string; key: string }[],
): string | number | string[] | number[] => {
switch (format) {
case FieldFormatEnum.text:
case FieldFormatEnum.aiField:
return faker.string.sample();
case FieldFormatEnum.keyword:
return faker.string.sample();
case FieldFormatEnum.number:
return faker.number.int({ min: 1, max: 100 });
case FieldFormatEnum.select:
return !options || options.length === 0 ?
[]
: options[faker.number.int({ min: 0, max: options.length - 1 })].key;
case FieldFormatEnum.multiSelect:
return !options || options.length === 0 ?
[]
: faker.helpers
.shuffle(options)
.slice(0, faker.number.int({ min: 0, max: options.length - 1 }))
.map((option) => option.key);
case FieldFormatEnum.date:
return faker.date.anytime().toISOString();
case FieldFormatEnum.images:
return ['https://example.com/' + faker.string.sample()];
default:
throw new Error('Invalid field type ');
}
};
const getRandomOptionEntities = () => {
const length = faker.number.int({ min: 1, max: 10 });
return Array.from({ length }).map(() => ({
id: faker.number.int(),
name: faker.string.sample(),
}));
};
const getRandomOptionDtos = () => {
const length = faker.number.int({ min: 1, max: 10 });
return Array.from({ length }).map(() => {
const randomValue = faker.string.sample();
return {
id: faker.number.int(),
name: randomValue,
key: randomValue,
};
});
};
export const getRandomEnumValue = <T extends object>(anEnum: T): T[keyof T] => {
const enumValues = Object.keys(anEnum) as (keyof T)[];
const randomIndex = faker.number.int(enumValues.length - 1);
const randomEnumKey = enumValues[randomIndex];
return anEnum[randomEnumKey];
};
export const getRandomEnumValues = <T extends object>(
anEnum: T,
): T[keyof T][] => {
const enumValues = Object.values(anEnum);
return faker.helpers.arrayElements(enumValues) as T[keyof T][];
};
export const optionSort = (
a: { id: number; name: string },
b: { id: number; name: string },
) =>
a.name < b.name ? -1
: a.name > b.name ? 1
: 0;
export const passwordFixture = faker.internet.password();
export const emailFixture = faker.internet.email();
const memberId = faker.number.int();
const roleId = faker.number.int();
const userId = faker.number.int();
export const tenantFixture = {
id: faker.number.int(),
siteName: faker.string.sample(),
description: faker.lorem.lines(2),
useEmail: faker.datatype.boolean(),
allowDomains: [],
useOAuth: faker.datatype.boolean(),
oauthConfig: null,
createdAt: faker.date.past(),
updatedAt: faker.date.past(),
projects: [],
deletedAt: new Date(0),
beforeInsertHook: jest.fn(),
beforeUpdateHook: jest.fn(),
} as TenantEntity;
export const projectFixture = {
id: faker.number.int(),
name: faker.string.sample(),
description: faker.lorem.lines(2),
createdAt: faker.date.past(),
updatedAt: faker.date.past(),
timezone: {
countryCode: 'KR',
name: 'Asia/Seoul',
offset: '+09:00',
},
tenant: tenantFixture,
} as ProjectEntity;
export const userFixture = {
id: userId,
email: emailFixture,
name: faker.string.sample(),
department: faker.string.sample(),
state: getRandomEnumValue(UserStateEnum),
hashPassword: bcrypt.hashSync(passwordFixture, 0),
type: getRandomEnumValue(UserTypeEnum),
signUpMethod: getRandomEnumValue(SignUpMethodEnum),
} as UserEntity;
const roleName = faker.string.sample();
export const roleFixture = {
id: roleId,
name: roleName,
permissions: getRandomEnumValues(PermissionEnum),
project: projectFixture,
members: [
{
id: memberId,
user: {
id: userId,
email: emailFixture,
name: faker.string.sample(),
department: faker.string.sample(),
state: getRandomEnumValue(UserStateEnum),
type: getRandomEnumValue(UserTypeEnum),
signUpMethod: getRandomEnumValue(SignUpMethodEnum),
},
role: {
id: roleId,
name: roleName,
permissions: getRandomEnumValues(PermissionEnum),
project: projectFixture,
},
},
],
} as RoleEntity;
export const memberFixture = {
id: memberId,
user: userFixture,
role: roleFixture,
} as MemberEntity;
export const apiKeyFixture = {
id: faker.number.int(),
value: faker.string.sample(),
project: projectFixture,
} as ApiKeyEntity;
export const issueTrackerFixture = {
id: faker.number.int(),
data: {},
project: projectFixture,
} as IssueTrackerEntity;
export const codeFixture = {
id: faker.number.int(),
type: getRandomEnumValue(CodeTypeEnum),
key: faker.string.sample(),
code: faker.string.sample(6),
data: {},
isVerified: faker.datatype.boolean(),
tryCount: faker.number.int(),
expiredAt: DateTime.utc().plus({ minutes: 5 }).toJSDate(),
} as CodeEntity;
export const fieldsFixture = Object.values(FieldFormatEnum).flatMap((format) =>
Object.values(FieldPropertyEnum).flatMap((property) =>
Object.values(FieldStatusEnum).flatMap((status) => ({
id: faker.number.int(),
...createFieldDto({
format,
property,
status,
}),
})),
),
) as FieldEntity[];
export const channelFixture = {
id: faker.number.int(),
name: faker.string.sample(),
description: faker.lorem.lines(2),
imageConfig: null,
feedbackSearchMaxDays: faker.number.int({ min: 1, max: 365 }),
createdAt: faker.date.past(),
updatedAt: faker.date.past(),
project: projectFixture,
fields: fieldsFixture,
} as ChannelEntity;
export const optionFixture = {
id: faker.number.int(),
key: faker.string.sample(),
name: faker.string.sample(),
field: fieldsFixture.find((field) =>
[FieldFormatEnum.select, FieldFormatEnum.multiSelect].includes(
field.format,
),
),
} as OptionEntity;
export const issueFixture = {
id: faker.number.int(),
name: faker.string.sample(),
description: faker.lorem.lines(2),
status: getRandomEnumValue(IssueStatusEnum),
feedbackCount: faker.number.int(),
externalIssueId: faker.string.sample(),
project: projectFixture,
} as IssueEntity;
export const feedbackDataFixture = fieldsFixture.reduce((prev, curr) => {
if (curr.status === FieldStatusEnum.INACTIVE) return prev;
const value = getRandomValue(curr.format, curr.options);
return {
...prev,
[curr.key]: value,
};
}, {});
export const feedbackFixture = {
id: faker.number.int(),
data: feedbackDataFixture,
createdAt: faker.date.past(),
updatedAt: faker.date.past(),
channel: channelFixture,
issues: [],
deletedAt: new Date(0),
beforeInsertHook: jest.fn(),
beforeUpdateHook: jest.fn(),
} as FeedbackEntity;
export const eventFixture = {
id: faker.number.int(),
status: EventStatusEnum.ACTIVE,
type: getRandomEnumValue(EventTypeEnum),
channels: [channelFixture],
} as EventEntity;
function getAllEvents() {
return Object.values(EventTypeEnum).map((type) => ({
id: faker.number.int(),
status: EventStatusEnum.ACTIVE,
type,
channels: [channelFixture],
}));
}
export const webhookFixture = {
id: faker.number.int(),
name: faker.string.sample(),
url: faker.internet.url(),
token: 'TEST-TOKEN',
status: WebhookStatusEnum.ACTIVE,
project: projectFixture,
events: getAllEvents(),
createdAt: faker.date.past(),
updatedAt: faker.date.past(),
} as WebhookEntity;
@@ -0,0 +1,33 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { ApiKeyEntity } from '../../domains/admin/project/api-key/api-key.entity';
import { ApiKeyService } from '../../domains/admin/project/api-key/api-key.service';
import { ApiKeyRepositoryStub, ProjectRepositoryStub } from '../stubs';
export const ApiKeyServiceProviders = [
ApiKeyService,
{
provide: getRepositoryToken(ApiKeyEntity),
useClass: ApiKeyRepositoryStub,
},
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
];
@@ -0,0 +1,61 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { HttpService } from '@nestjs/axios';
import { JwtService } from '@nestjs/jwt';
import { ClsModule } from 'nestjs-cls';
import { EmailVerificationMailingService } from '@/shared/mailing/email-verification-mailing.service';
import { CodeServiceProviders } from '@/test-utils/providers/code.service.providers';
import { getMockProvider } from '@/test-utils/util-functions';
import { AuthService } from '../../domains/admin/auth/auth.service';
import { ApiKeyServiceProviders } from './api-key.service.providers';
import { CreateUserServiceProviders } from './create-user.service.providers';
import { MemberServiceProviders } from './member.service.providers';
import { RoleServiceProviders } from './role.service.providers';
import { TenantServiceProviders } from './tenant.service.providers';
import { UserServiceProviders } from './user.service.providers';
export const MockJwtService = {
sign: jest.fn(),
};
export const MockEmailVerificationMailingService = {
send: jest.fn(),
};
export const AuthServiceProviders = [
AuthService,
...CreateUserServiceProviders,
...UserServiceProviders,
getMockProvider(JwtService, MockJwtService),
getMockProvider(
EmailVerificationMailingService,
MockEmailVerificationMailingService,
),
...CodeServiceProviders,
...ApiKeyServiceProviders,
...TenantServiceProviders,
...RoleServiceProviders,
...MemberServiceProviders,
ClsModule,
{
provide: HttpService,
useValue: {
get: jest.fn(),
post: jest.fn(),
},
},
];
@@ -0,0 +1,42 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { ConfigService } from '@nestjs/config';
import { getRepositoryToken } from '@nestjs/typeorm';
import { OpensearchRepository } from '@/common/repositories';
import { ProjectServiceProviders } from '@/test-utils/providers/project.service.providers';
import {
getMockProvider,
MockOpensearchRepository,
} from '@/test-utils/util-functions';
import { ChannelEntity } from '../../domains/admin/channel/channel/channel.entity';
import { ChannelMySQLService } from '../../domains/admin/channel/channel/channel.mysql.service';
import { ChannelService } from '../../domains/admin/channel/channel/channel.service';
import { ChannelRepositoryStub } from '../stubs';
import { FieldServiceProviders } from './field.service.providers';
export const ChannelServiceProviders = [
ChannelService,
ChannelMySQLService,
{
provide: getRepositoryToken(ChannelEntity),
useClass: ChannelRepositoryStub,
},
getMockProvider(OpensearchRepository, MockOpensearchRepository),
...ProjectServiceProviders,
...FieldServiceProviders,
ConfigService,
];
@@ -0,0 +1,26 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { CodeEntity } from '../../shared/code/code.entity';
import { CodeService } from '../../shared/code/code.service';
import { CodeRepositoryStub } from '../stubs';
export const CodeServiceProviders = [
CodeService,
{ provide: getRepositoryToken(CodeEntity), useClass: CodeRepositoryStub },
];
@@ -0,0 +1,47 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { ChannelEntity } from '@/domains/admin/channel/channel/channel.entity';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { CreateUserService } from '@/domains/admin/user/create-user.service';
import { UserEntity } from '@/domains/admin/user/entities/user.entity';
import {
ChannelRepositoryStub,
ProjectRepositoryStub,
UserRepositoryStub,
} from '../stubs';
import { FeedbackServiceProviders } from './feedback.service.providers';
import { MemberServiceProviders } from './member.service.providers';
import { TenantServiceProviders } from './tenant.service.providers';
import { UserPasswordServiceProviders } from './user-password.service.providers';
export const CreateUserServiceProviders = [
CreateUserService,
...UserPasswordServiceProviders,
...TenantServiceProviders,
...MemberServiceProviders,
...FeedbackServiceProviders,
{ provide: getRepositoryToken(UserEntity), useClass: UserRepositoryStub },
{
provide: getRepositoryToken(ChannelEntity),
useClass: ChannelRepositoryStub,
},
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
];
@@ -0,0 +1,52 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { SchedulerRegistry } from '@nestjs/schedule';
import { getRepositoryToken } from '@nestjs/typeorm';
import { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import { IssueEntity } from '@/domains/admin/project/issue/issue.entity';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { FeedbackIssueStatisticsEntity } from '@/domains/admin/statistics/feedback-issue/feedback-issue-statistics.entity';
import { FeedbackIssueStatisticsService } from '@/domains/admin/statistics/feedback-issue/feedback-issue-statistics.service';
import { mockRepository } from '@/test-utils/util-functions';
import {
FeedbackRepositoryStub,
IssueRepositoryStub,
ProjectRepositoryStub,
} from '../stubs';
import { SchedulerLockServiceProviders } from './scheduler-lock.service.providers';
export const FeedbackIssueStatisticsServiceProviders = [
FeedbackIssueStatisticsService,
{
provide: getRepositoryToken(FeedbackIssueStatisticsEntity),
useValue: mockRepository(),
},
{
provide: getRepositoryToken(FeedbackEntity),
useClass: FeedbackRepositoryStub,
},
{
provide: getRepositoryToken(IssueEntity),
useClass: IssueRepositoryStub,
},
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
SchedulerRegistry,
...SchedulerLockServiceProviders,
];
@@ -0,0 +1,58 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { SchedulerRegistry } from '@nestjs/schedule';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ChannelEntity } from '@/domains/admin/channel/channel/channel.entity';
import { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import { IssueEntity } from '@/domains/admin/project/issue/issue.entity';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { FeedbackStatisticsEntity } from '@/domains/admin/statistics/feedback/feedback-statistics.entity';
import { FeedbackStatisticsService } from '@/domains/admin/statistics/feedback/feedback-statistics.service';
import { mockRepository } from '@/test-utils/util-functions';
import {
ChannelRepositoryStub,
FeedbackRepositoryStub,
IssueRepositoryStub,
ProjectRepositoryStub,
} from '../stubs';
import { SchedulerLockServiceProviders } from './scheduler-lock.service.providers';
export const FeedbackStatisticsServiceProviders = [
FeedbackStatisticsService,
{
provide: getRepositoryToken(FeedbackStatisticsEntity),
useValue: mockRepository(),
},
{
provide: getRepositoryToken(FeedbackEntity),
useClass: FeedbackRepositoryStub,
},
{
provide: getRepositoryToken(IssueEntity),
useClass: IssueRepositoryStub,
},
{
provide: getRepositoryToken(ChannelEntity),
useClass: ChannelRepositoryStub,
},
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
SchedulerRegistry,
...SchedulerLockServiceProviders,
];
@@ -0,0 +1,54 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { EventEmitter2 } from '@nestjs/event-emitter';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ClsModule } from 'nestjs-cls';
import { OpensearchRepository } from '@/common/repositories';
import { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import { FeedbackMySQLService } from '@/domains/admin/feedback/feedback.mysql.service';
import { FeedbackOSService } from '@/domains/admin/feedback/feedback.os.service';
import { FeedbackService } from '@/domains/admin/feedback/feedback.service';
import {
getMockProvider,
MockOpensearchRepository,
} from '@/test-utils/util-functions';
import { FeedbackRepositoryStub } from '../stubs';
import { ChannelServiceProviders } from './channel.service.providers';
import { FeedbackIssueStatisticsServiceProviders } from './feedback-issue-statistics.service.providers';
import { FeedbackStatisticsServiceProviders } from './feedback-statistics.service.providers';
import { FieldServiceProviders } from './field.service.providers';
import { IssueServiceProviders } from './issue.service.providers';
import { OptionServiceProviders } from './option.service.providers';
export const FeedbackServiceProviders = [
FeedbackService,
FeedbackMySQLService,
{
provide: getRepositoryToken(FeedbackEntity),
useClass: FeedbackRepositoryStub,
},
ClsModule,
...FieldServiceProviders,
...IssueServiceProviders,
...OptionServiceProviders,
...ChannelServiceProviders,
...FeedbackStatisticsServiceProviders,
...FeedbackIssueStatisticsServiceProviders,
getMockProvider(OpensearchRepository, MockOpensearchRepository),
FeedbackOSService,
EventEmitter2,
];
@@ -0,0 +1,38 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { OpensearchRepository } from '@/common/repositories';
import {
getMockProvider,
MockOpensearchRepository,
} from '@/test-utils/util-functions';
import { FieldEntity } from '../../domains/admin/channel/field/field.entity';
import { FieldMySQLService } from '../../domains/admin/channel/field/field.mysql.service';
import { FieldService } from '../../domains/admin/channel/field/field.service';
import { FieldRepositoryStub } from '../stubs';
import { OptionServiceProviders } from './option.service.providers';
export const FieldServiceProviders = [
FieldService,
FieldMySQLService,
getMockProvider(OpensearchRepository, MockOpensearchRepository),
{
provide: getRepositoryToken(FieldEntity),
useClass: FieldRepositoryStub,
},
...OptionServiceProviders,
];
@@ -0,0 +1,43 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { SchedulerRegistry } from '@nestjs/schedule';
import { getRepositoryToken } from '@nestjs/typeorm';
import { IssueEntity } from '@/domains/admin/project/issue/issue.entity';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { IssueStatisticsEntity } from '@/domains/admin/statistics/issue/issue-statistics.entity';
import { IssueStatisticsService } from '@/domains/admin/statistics/issue/issue-statistics.service';
import { mockRepository } from '@/test-utils/util-functions';
import { IssueRepositoryStub, ProjectRepositoryStub } from '../stubs';
import { SchedulerLockServiceProviders } from './scheduler-lock.service.providers';
export const IssueStatisticsServiceProviders = [
IssueStatisticsService,
{
provide: getRepositoryToken(IssueStatisticsEntity),
useValue: mockRepository(),
},
{
provide: getRepositoryToken(IssueEntity),
useClass: IssueRepositoryStub,
},
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
SchedulerRegistry,
...SchedulerLockServiceProviders,
];
@@ -0,0 +1,28 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { IssueTrackerEntity } from '../../domains/admin/project/issue-tracker/issue-tracker.entity';
import { IssueTrackerService } from '../../domains/admin/project/issue-tracker/issue-tracker.service';
import { IssueTrackerRepositoryStub } from '../stubs/issue-tracker-repository.stub';
export const IssueTrackerServiceProviders = [
IssueTrackerService,
{
provide: getRepositoryToken(IssueTrackerEntity),
useClass: IssueTrackerRepositoryStub,
},
];
@@ -0,0 +1,37 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { EventEmitter2 } from '@nestjs/event-emitter';
import { getRepositoryToken } from '@nestjs/typeorm';
import { CategoryEntity } from '@/domains/admin/project/category/category.entity';
import { IssueEntity } from '../../domains/admin/project/issue/issue.entity';
import { IssueService } from '../../domains/admin/project/issue/issue.service';
import { IssueRepositoryStub } from '../stubs';
import { IssueStatisticsServiceProviders } from './issue-statistics.service.providers';
export const IssueServiceProviders = [
IssueService,
{
provide: getRepositoryToken(IssueEntity),
useValue: IssueRepositoryStub,
},
{
provide: getRepositoryToken(CategoryEntity),
useValue: {},
},
...IssueStatisticsServiceProviders,
EventEmitter2,
];
@@ -0,0 +1,35 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { TenantEntity } from '@/domains/admin/tenant/tenant.entity';
import { MemberEntity } from '../../domains/admin/project/member/member.entity';
import { MemberService } from '../../domains/admin/project/member/member.service';
import { TenantRepositoryStub } from '../stubs';
import { MemberRepositoryStub } from '../stubs/member-repository.stub';
import { RoleServiceProviders } from './role.service.providers';
import { UserServiceProviders } from './user.service.providers';
export const MemberServiceProviders = [
MemberService,
{
provide: getRepositoryToken(MemberEntity),
useClass: MemberRepositoryStub,
},
{ provide: getRepositoryToken(TenantEntity), useClass: TenantRepositoryStub },
...RoleServiceProviders,
...UserServiceProviders,
];
@@ -0,0 +1,28 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { OptionEntity } from '../../domains/admin/channel/option/option.entity';
import { OptionService } from '../../domains/admin/channel/option/option.service';
import { OptionRepositoryStub } from '../stubs/option-repository.stub';
export const OptionServiceProviders = [
OptionService,
{
provide: getRepositoryToken(OptionEntity),
useClass: OptionRepositoryStub,
},
];
@@ -0,0 +1,53 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { OpensearchRepository } from '@/common/repositories';
import { ChannelEntity } from '@/domains/admin/channel/channel/channel.entity';
import {
getMockProvider,
MockOpensearchRepository,
} from '@/test-utils/util-functions';
import { ProjectEntity } from '../../domains/admin/project/project/project.entity';
import { ProjectService } from '../../domains/admin/project/project/project.service';
import { ChannelRepositoryStub, ProjectRepositoryStub } from '../stubs';
import { ApiKeyServiceProviders } from './api-key.service.providers';
import { FeedbackIssueStatisticsServiceProviders } from './feedback-issue-statistics.service.providers';
import { FeedbackStatisticsServiceProviders } from './feedback-statistics.service.providers';
import { IssueStatisticsServiceProviders } from './issue-statistics.service.providers';
import { IssueTrackerServiceProviders } from './issue-tracker.service.provider';
import { MemberServiceProviders } from './member.service.providers';
import { RoleServiceProviders } from './role.service.providers';
export const ProjectServiceProviders = [
ProjectService,
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
{
provide: getRepositoryToken(ChannelEntity),
useClass: ChannelRepositoryStub,
},
getMockProvider(OpensearchRepository, MockOpensearchRepository),
...RoleServiceProviders,
...MemberServiceProviders,
...ApiKeyServiceProviders,
...IssueTrackerServiceProviders,
...FeedbackStatisticsServiceProviders,
...IssueStatisticsServiceProviders,
...FeedbackIssueStatisticsServiceProviders,
];
@@ -0,0 +1,28 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { RoleEntity } from '../../domains/admin/project/role/role.entity';
import { RoleService } from '../../domains/admin/project/role/role.service';
import { RoleRepositoryStub } from '../stubs';
export const RoleServiceProviders = [
RoleService,
{
provide: getRepositoryToken(RoleEntity),
useClass: RoleRepositoryStub,
},
];
@@ -0,0 +1,29 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { SchedulerLockEntity } from '@/domains/operation/scheduler-lock/scheduler-lock.entity';
import { SchedulerLockService } from '@/domains/operation/scheduler-lock/scheduler-lock.service';
import { mockRepository } from '../util-functions';
export const SchedulerLockServiceProviders = [
SchedulerLockService,
{
provide: getRepositoryToken(SchedulerLockEntity),
useValue: mockRepository(),
},
];
@@ -0,0 +1,53 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { SchedulerRegistry } from '@nestjs/schedule';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ResetPasswordMailingService } from '@/shared/mailing/reset-password-mailing.service';
import { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { TenantEntity } from '@/domains/admin/tenant/tenant.entity';
import { TenantService } from '@/domains/admin/tenant/tenant.service';
import { UserEntity } from '@/domains/admin/user/entities/user.entity';
import { UserPasswordService } from '@/domains/admin/user/user-password.service';
import { SchedulerLockService } from '@/domains/operation/scheduler-lock/scheduler-lock.service';
import {
FeedbackRepositoryStub,
ProjectRepositoryStub,
TenantRepositoryStub,
UserRepositoryStub,
} from '../stubs';
import { FeedbackServiceProviders } from './feedback.service.providers';
export const TenantServiceProviders = [
TenantService,
SchedulerRegistry,
SchedulerLockService,
UserPasswordService,
ResetPasswordMailingService,
{ provide: getRepositoryToken(TenantEntity), useClass: TenantRepositoryStub },
{ provide: getRepositoryToken(UserEntity), useClass: UserRepositoryStub },
{
provide: getRepositoryToken(FeedbackEntity),
useClass: FeedbackRepositoryStub,
},
{
provide: getRepositoryToken(ProjectEntity),
useClass: ProjectRepositoryStub,
},
...FeedbackServiceProviders,
];
@@ -0,0 +1,40 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { MailerService } from '@nestjs-modules/mailer';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ResetPasswordMailingService } from '@/shared/mailing/reset-password-mailing.service';
import { UserEntity } from '@/domains/admin/user/entities/user.entity';
import { UserPasswordService } from '@/domains/admin/user/user-password.service';
import { CodeServiceProviders } from '@/test-utils/providers/code.service.providers';
import { UserRepositoryStub } from '../stubs';
import { getMockProvider } from '../util-functions';
const MockMailerService = {
sendMail: jest.fn(),
};
const MockResetPasswordMailingService = {
send: jest.fn(),
};
export const UserPasswordServiceProviders = [
UserPasswordService,
...CodeServiceProviders,
getMockProvider(ResetPasswordMailingService, MockResetPasswordMailingService),
getMockProvider(MailerService, MockMailerService),
{ provide: getRepositoryToken(UserEntity), useClass: UserRepositoryStub },
];
@@ -0,0 +1,43 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { MailerService } from '@nestjs-modules/mailer';
import { getRepositoryToken } from '@nestjs/typeorm';
import { UserInvitationMailingService } from '@/shared/mailing/user-invitation-mailing.service';
import { UserEntity } from '@/domains/admin/user/entities/user.entity';
import { UserService } from '@/domains/admin/user/user.service';
import { CodeServiceProviders } from '@/test-utils/providers/code.service.providers';
import { UserRepositoryStub } from '../stubs';
import { getMockProvider } from '../util-functions';
export const MockUserInvitationMailingService = {
send: jest.fn(),
};
const MockMailerService = {
sendMail: jest.fn(),
};
export const UserServiceProviders = [
UserService,
{ provide: getRepositoryToken(UserEntity), useClass: UserRepositoryStub },
getMockProvider(
UserInvitationMailingService,
MockUserInvitationMailingService,
),
getMockProvider(MailerService, MockMailerService),
...CodeServiceProviders,
];
@@ -0,0 +1,50 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { HttpService } from '@nestjs/axios';
import { getRepositoryToken } from '@nestjs/typeorm';
import { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import { IssueEntity } from '@/domains/admin/project/issue/issue.entity';
import { WebhookEntity } from '@/domains/admin/project/webhook/webhook.entity';
import { WebhookListener } from '@/domains/admin/project/webhook/webhook.listener';
import {
FeedbackRepositoryStub,
IssueRepositoryStub,
WebhookRepositoryStub,
} from '../stubs';
export const WebhookListenerProviders = [
WebhookListener,
{
provide: getRepositoryToken(WebhookEntity),
useClass: WebhookRepositoryStub,
},
{
provide: getRepositoryToken(FeedbackEntity),
useClass: FeedbackRepositoryStub,
},
{
provide: getRepositoryToken(IssueEntity),
useClass: IssueRepositoryStub,
},
{
provide: HttpService,
useValue: {
get: jest.fn(),
post: jest.fn(),
},
},
];
@@ -0,0 +1,42 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { getRepositoryToken } from '@nestjs/typeorm';
import { ChannelEntity } from '@/domains/admin/channel/channel/channel.entity';
import { EventEntity } from '@/domains/admin/project/webhook/event.entity';
import { WebhookEntity } from '@/domains/admin/project/webhook/webhook.entity';
import { WebhookService } from '@/domains/admin/project/webhook/webhook.service';
import {
ChannelRepositoryStub,
EventRepositoryStub,
WebhookRepositoryStub,
} from '../stubs';
export const WebhookServiceProviders = [
WebhookService,
{
provide: getRepositoryToken(WebhookEntity),
useClass: WebhookRepositoryStub,
},
{
provide: getRepositoryToken(EventEntity),
useClass: EventRepositoryStub,
},
{
provide: getRepositoryToken(ChannelEntity),
useClass: ChannelRepositoryStub,
},
];
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { ApiKeyEntity } from '@/domains/admin/project/api-key/api-key.entity';
import { apiKeyFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class ApiKeyRepositoryStub extends CommonRepositoryStub<ApiKeyEntity> {
constructor() {
super([apiKeyFixture]);
}
}
@@ -0,0 +1,33 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type {
ChannelEntity,
ImageConfig,
} from '@/domains/admin/channel/channel/channel.entity';
import { channelFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class ChannelRepositoryStub extends CommonRepositoryStub<ChannelEntity> {
constructor() {
super([channelFixture]);
}
setImageConfig(config: Partial<ImageConfig>) {
this.entities?.forEach((entity) => {
entity.imageConfig = { ...entity.imageConfig, ...config } as ImageConfig;
});
}
}
@@ -0,0 +1,60 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { CodeTypeEnum } from '@/shared/code/code-type.enum';
import type { CodeEntity } from '@/shared/code/code.entity';
import { codeFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class CodeRepositoryStub extends CommonRepositoryStub<CodeEntity> {
constructor() {
super([codeFixture]);
}
setNull() {
this.entities = null;
}
setIsVerified(bool: boolean) {
this.entities?.forEach((entity) => {
entity.isVerified = bool;
});
}
setType(type: CodeTypeEnum) {
this.entities?.forEach((entity) => {
entity.type = type;
});
}
setTryCount(tryCount: number) {
this.entities?.forEach((entity) => {
entity.tryCount = tryCount;
});
}
getTryCount(): number {
return this.entities?.[0]?.tryCount ?? 0;
}
setData(data: unknown) {
this.entities?.forEach((entity) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(entity as any).data = data;
});
}
}
@@ -0,0 +1,90 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { faker } from '@faker-js/faker';
import { createQueryBuilder, removeUndefinedValues } from '../util-functions';
export class CommonRepositoryStub<T> {
entities: T[] | null;
constructor(entity: T[] | null) {
this.entities = entity;
}
findOne(input?: { where: Partial<T> }) {
const entity = this.entities?.[0];
if (!entity) return null;
return { ...entity, ...input?.where };
}
findOneBy() {
const entity = this.entities?.[0];
if (!entity) return null;
return entity;
}
find() {
return this.entities;
}
findBy() {
return this.entities;
}
findAndCount() {
return [this.entities, this.entities?.length];
}
findAndCountBy() {
return [this.entities, this.entities?.length];
}
save(entity: T) {
const entityToSave = removeUndefinedValues(entity as object);
this.entities?.push(entityToSave as T);
if (Array.isArray(entityToSave)) {
return (entityToSave as T[]).map((e) => ({
...this.entities?.[0],
...e,
id: faker.number.int(),
}));
}
return { ...this.entities?.[0], ...entityToSave };
}
update(entity: T) {
const entityToSave = removeUndefinedValues(entity as object);
if (Array.isArray(entityToSave)) {
return (entityToSave as T[]).map((e) => ({
...this.entities?.[0],
...e,
}));
}
return { ...this.entities?.[0], ...entityToSave };
}
count() {
return this.entities?.length ?? 0;
}
remove({ id }: { id: number }) {
return { id };
}
createQueryBuilder() {
createQueryBuilder.getMany = () => this.entities;
return createQueryBuilder;
}
}
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { EventEntity } from '@/domains/admin/project/webhook/event.entity';
import { eventFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class EventRepositoryStub extends CommonRepositoryStub<EventEntity> {
constructor() {
super([eventFixture]);
}
}
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import { feedbackFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class FeedbackRepositoryStub extends CommonRepositoryStub<FeedbackEntity> {
constructor() {
super([feedbackFixture]);
}
}
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { FieldEntity } from '@/domains/admin/channel/field/field.entity';
import { fieldsFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class FieldRepositoryStub extends CommonRepositoryStub<FieldEntity> {
constructor() {
super(fieldsFixture);
}
}
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
export { ApiKeyRepositoryStub } from './api-key-repository.stub';
export { ChannelRepositoryStub } from './channel-repository.stub';
export { CodeRepositoryStub } from './code-repository.stub';
export { EventRepositoryStub } from './event-repository.stub';
export { FeedbackRepositoryStub } from './feedback-repository.stub';
export { FieldRepositoryStub } from './field-repository.stub';
export { IssueRepositoryStub } from './issue-repository.stub';
export { ProjectRepositoryStub } from './project-repository.stub';
export { RoleRepositoryStub } from './role-repository.stub';
export { TenantRepositoryStub } from './tenant-repository.stub';
export { UserRepositoryStub } from './user-repository.stub';
export { WebhookRepositoryStub } from './webhook-repository.stub';
@@ -0,0 +1,48 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { faker } from '@faker-js/faker';
import { IssueStatusEnum } from '@/common/enums';
import type { IssueEntity } from '@/domains/admin/project/issue/issue.entity';
import { issueFixture } from '../fixtures';
import { removeUndefinedValues } from '../util-functions';
import { CommonRepositoryStub } from './common-repository.stub';
export class IssueRepositoryStub extends CommonRepositoryStub<IssueEntity> {
constructor() {
super([issueFixture]);
}
save(issue: Partial<IssueEntity> | Partial<IssueEntity>[]) {
const issueToSave = removeUndefinedValues(issue);
if (Array.isArray(issueToSave)) {
return issueToSave.map((e) => ({
...this.entities?.[0],
...e,
id: faker.number.int(),
status: e.status ?? IssueStatusEnum.INIT,
feedbackCount: e.feedbackCount ?? 0,
}));
} else {
return {
...this.entities?.[0],
...issueToSave,
status: issueToSave.status ?? IssueStatusEnum.INIT,
feedbackCount: issueToSave.feedbackCount ?? 0,
};
}
}
}
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { IssueTrackerEntity } from '@/domains/admin/project/issue-tracker/issue-tracker.entity';
import { issueTrackerFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class IssueTrackerRepositoryStub extends CommonRepositoryStub<IssueTrackerEntity> {
constructor() {
super([issueTrackerFixture]);
}
}
@@ -0,0 +1,44 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { faker } from '@faker-js/faker';
import type { MemberEntity } from '@/domains/admin/project/member/member.entity';
import { memberFixture } from '../fixtures';
import { removeUndefinedValues } from '../util-functions';
import { CommonRepositoryStub } from './common-repository.stub';
export class MemberRepositoryStub extends CommonRepositoryStub<MemberEntity> {
save(member: Partial<MemberEntity> | Partial<MemberEntity>[]) {
const memberToSave = removeUndefinedValues(member);
const entity = this.entities?.[0] ?? memberFixture;
if (Array.isArray(memberToSave)) {
return memberToSave.map((e) => ({
...entity,
...e,
role: { ...entity.role, ...e.role },
user: { ...entity.user, ...e.user },
id: faker.number.int(),
}));
} else {
return {
...entity,
role: { ...entity.role, ...memberToSave.role },
user: { ...entity.user, ...memberToSave.user },
...memberToSave,
};
}
}
}
@@ -0,0 +1,29 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { OptionEntity } from '@/domains/admin/channel/option/option.entity';
import { optionFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class OptionRepositoryStub extends CommonRepositoryStub<OptionEntity> {
constructor() {
super([optionFixture]);
}
query() {
return;
}
}
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import { projectFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class ProjectRepositoryStub extends CommonRepositoryStub<ProjectEntity> {
constructor() {
super([projectFixture]);
}
}
@@ -0,0 +1,25 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { RoleEntity } from '@/domains/admin/project/role/role.entity';
import { roleFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class RoleRepositoryStub extends CommonRepositoryStub<RoleEntity> {
constructor() {
super([roleFixture]);
}
}
@@ -0,0 +1,41 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type {
OAuthConfig,
TenantEntity,
} from '@/domains/admin/tenant/tenant.entity';
import { tenantFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class TenantRepositoryStub extends CommonRepositoryStub<TenantEntity> {
constructor() {
super([tenantFixture]);
}
setAllowDomains(domains: string[] = []) {
this.entities?.forEach((entity) => {
entity.allowDomains = domains;
});
}
setUseOAuth(bool: boolean, config: Partial<OAuthConfig> | null = null) {
this.entities?.forEach((entity) => {
entity.useOAuth = bool;
entity.oauthConfig = config as OAuthConfig;
});
}
}
@@ -0,0 +1,46 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { faker } from '@faker-js/faker';
import { UserTypeEnum } from '@/domains/admin/user/entities/enums';
import type { UserEntity } from '@/domains/admin/user/entities/user.entity';
import { userFixture } from '../fixtures';
import { removeUndefinedValues } from '../util-functions';
import { CommonRepositoryStub } from './common-repository.stub';
export class UserRepositoryStub extends CommonRepositoryStub<UserEntity> {
constructor() {
super([userFixture]);
}
save(user: Partial<UserEntity> | Partial<UserEntity>[]) {
const userToSave = removeUndefinedValues(user);
const entity = this.entities?.[0] ?? userFixture;
if (Array.isArray(userToSave)) {
return userToSave.map((e) => ({
...entity,
...e,
type: e.type ?? UserTypeEnum.GENERAL,
id: faker.number.int(),
}));
}
return {
...entity,
...userToSave,
type: userToSave.type ?? UserTypeEnum.GENERAL,
};
}
}
@@ -0,0 +1,42 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import type { EventTypeEnum } from '@/common/enums';
import type { WebhookEntity } from '@/domains/admin/project/webhook/webhook.entity';
import { webhookFixture } from '../fixtures';
import { CommonRepositoryStub } from './common-repository.stub';
export class WebhookRepositoryStub extends CommonRepositoryStub<WebhookEntity> {
constructor() {
super([webhookFixture]);
}
find(input?: {
where: { events: { type: EventTypeEnum } };
}): WebhookEntity[] | null {
if (input?.where.events.type) {
return (
this.entities?.filter(
(entity) =>
entity.events.filter(
(event) => event.type === input.where.events.type,
).length > 0,
) ?? null
);
}
return this.entities;
}
}
+276
View File
@@ -0,0 +1,276 @@
/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { faker } from '@faker-js/faker';
import type { InjectionToken, Provider } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import type { TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import type { DataSource, Repository } from 'typeorm';
import { initializeTransactionalContext } from 'typeorm-transactional';
import {
FieldFormatEnum,
FieldPropertyEnum,
FieldStatusEnum,
} from '@/common/enums';
import { appConfig } from '@/configs/app.config';
import { jwtConfig, jwtConfigSchema } from '@/configs/jwt.config';
import {
opensearchConfig,
opensearchConfigSchema,
} from '@/configs/opensearch.config';
import { smtpConfig, smtpConfigSchema } from '@/configs/smtp.config';
import type { AuthService } from '@/domains/admin/auth/auth.service';
import { ChannelEntity } from '@/domains/admin/channel/channel/channel.entity';
import type { ChannelService } from '@/domains/admin/channel/channel/channel.service';
import { FieldEntity } from '@/domains/admin/channel/field/field.entity';
import type { CreateFeedbackDto } from '@/domains/admin/feedback/dtos';
import { FeedbackEntity } from '@/domains/admin/feedback/feedback.entity';
import type { FeedbackService } from '@/domains/admin/feedback/feedback.service';
import { ProjectEntity } from '@/domains/admin/project/project/project.entity';
import type { ProjectService } from '@/domains/admin/project/project/project.service';
import { RoleEntity } from '@/domains/admin/project/role/role.entity';
import { SetupTenantRequestDto } from '@/domains/admin/tenant/dtos/requests';
import { TenantEntity } from '@/domains/admin/tenant/tenant.entity';
import type { TenantService } from '@/domains/admin/tenant/tenant.service';
import { UserDto } from '@/domains/admin/user/dtos';
import {
UserStateEnum,
UserTypeEnum,
} from '@/domains/admin/user/entities/enums';
import { UserEntity } from '@/domains/admin/user/entities/user.entity';
import { createFieldDto, getRandomValue } from '@/test-utils/fixtures';
initializeTransactionalContext();
export const getMockProvider = (
injectToken: InjectionToken,
factory: unknown,
): Provider => ({ provide: injectToken, useFactory: () => factory });
export const TestConfig = ConfigModule.forRoot({
load: [appConfig, smtpConfig, jwtConfig, opensearchConfig],
envFilePath: '.env.test',
validationSchema: smtpConfigSchema
.concat(jwtConfigSchema)
.concat(opensearchConfigSchema),
});
export const MockDataSource = {
initialize: jest.fn(),
};
export const getRandomEnumValue = <T extends object>(anEnum: T): T[keyof T] => {
const enumValues = Object.keys(anEnum) as (keyof T)[];
const randomIndex = faker.number.int(enumValues.length - 1);
const randomEnumKey = enumValues[randomIndex];
return anEnum[randomEnumKey];
};
export const getRandomEnumValues = <T extends object>(
anEnum: T,
): T[keyof T][] => {
const enumValues = Object.values(anEnum);
return faker.helpers.arrayElements(enumValues) as T[keyof T][];
};
export const createTenant = async (tenantService: TenantService) => {
const dto = new SetupTenantRequestDto();
dto.siteName = faker.string.sample();
dto.password = '12345678';
await tenantService.create(dto);
};
export const createProject = async (projectService: ProjectService) => {
return await projectService.create({
name: faker.lorem.words(),
description: faker.lorem.lines(1),
timezone: {
countryCode: 'KR',
name: 'Asia/Seoul',
offset: '+09:00',
},
});
};
export const createChannel = async (
channelService: ChannelService,
project: ProjectEntity,
) => {
return await channelService.create({
projectId: project.id,
name: faker.string.alphanumeric(20),
description: faker.lorem.lines(1),
feedbackSearchMaxDays: 1000,
fields: Array.from({
length: faker.number.int({ min: 1, max: 10 }),
}).map(() =>
createFieldDto({
format: FieldFormatEnum.keyword,
property: FieldPropertyEnum.EDITABLE,
status: FieldStatusEnum.ACTIVE,
}),
),
imageConfig: null,
});
};
export const createFeedback = async (
fields: FieldEntity[],
channelId: number,
feedbackService: FeedbackService,
) => {
const dto: CreateFeedbackDto = {
channelId: channelId,
data: {},
};
fields
.filter(
({ key }) =>
key !== 'id' &&
key !== 'issues' &&
key !== 'createdAt' &&
key !== 'updatedAt',
)
.forEach(({ key, format, options }) => {
dto.data[key] = getRandomValue(format, options);
});
await feedbackService.create(dto);
};
export const clearAllEntities = async (module: TestingModule) => {
const userRepo: Repository<UserEntity> = module.get(
getRepositoryToken(UserEntity),
);
const roleRepo: Repository<RoleEntity> = module.get(
getRepositoryToken(RoleEntity),
);
const tenantRepo: Repository<TenantEntity> = module.get(
getRepositoryToken(TenantEntity),
);
const projectRepo: Repository<ProjectEntity> = module.get(
getRepositoryToken(ProjectEntity),
);
const channelRepo: Repository<ChannelEntity> = module.get(
getRepositoryToken(ChannelEntity),
);
const fieldRepo: Repository<FieldEntity> = module.get(
getRepositoryToken(FieldEntity),
);
const feedbackRepo: Repository<FeedbackEntity> = module.get(
getRepositoryToken(FeedbackEntity),
);
await clearEntities([
userRepo,
roleRepo,
tenantRepo,
projectRepo,
channelRepo,
fieldRepo,
feedbackRepo,
]);
};
export const clearEntities = async (repos: Repository<any>[]) => {
for (const repo of repos) {
await repo.query('set foreign_key_checks = 0');
await repo.clear();
await repo.query('set foreign_key_checks = 1');
}
};
export const signInTestUser = async (
dataSource: DataSource,
authService: AuthService,
) => {
const userRepo = dataSource.getRepository(UserEntity);
const user = await userRepo.save({
email: faker.internet.email(),
state: UserStateEnum.Active,
hashPassword: faker.internet.password(),
type: UserTypeEnum.SUPER,
});
return { jwt: await authService.signIn(UserDto.transform(user)), user };
};
export const DEFAULT_FIELD_COUNT = 2;
export const createQueryBuilder: Record<string, object> = {
setFindOptions: () => createQueryBuilder,
select: () => createQueryBuilder,
innerJoin: () => createQueryBuilder,
leftJoin: () => createQueryBuilder,
leftJoinAndSelect: () => createQueryBuilder,
where: () => createQueryBuilder,
andWhere: () => createQueryBuilder,
groupBy: () => createQueryBuilder,
addOrderBy: () => createQueryBuilder,
getRawMany: () => createQueryBuilder,
insert: () => createQueryBuilder,
values: () => createQueryBuilder,
orUpdate: () => createQueryBuilder,
updateEntity: () => createQueryBuilder,
execute: () => createQueryBuilder,
offset: () => createQueryBuilder,
limit: () => createQueryBuilder,
getMany: () => createQueryBuilder,
getCount: () => createQueryBuilder,
clone: () => createQueryBuilder,
};
export const mockRepository = () => ({
findOneBy: jest.fn(),
findOne: jest.fn(),
find: jest.fn(),
findBy: jest.fn(),
findAndCount: jest.fn(),
findAndCountBy: jest.fn(),
save: jest.fn(),
create: jest.fn(),
update: jest.fn(),
upsert: jest.fn(),
count: jest.fn(),
remove: jest.fn(),
createQueryBuilder: jest.fn(() => createQueryBuilder),
query: jest.fn(),
manager: {
transaction: () => jest.fn().mockImplementation(mockRepository),
},
});
export const MockOpensearchRepository = {
createIndex: jest.fn(),
deleteIndex: jest.fn(),
putMappings: jest.fn(),
createData: jest.fn(),
getData: jest.fn().mockResolvedValue({ items: [], total: 0 }),
updateData: jest.fn(),
getTotal: jest.fn(),
deleteBulkData: jest.fn(),
scroll: jest.fn().mockResolvedValue({ items: [], total: 0 }),
};
export function removeUndefinedValues<T extends object>(obj: T): T {
Object.keys(obj).forEach((key) => {
if (obj[key] && typeof obj[key] === 'object') {
removeUndefinedValues(obj[key] as object);
} else if (obj[key] === undefined) {
delete obj[key];
}
});
return obj;
}