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
@@ -0,0 +1,456 @@
/**
* 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 { Test } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import type { Repository } from 'typeorm';
import { optionFixture } from '@/test-utils/fixtures';
import { TestConfig } from '@/test-utils/util-functions';
import { OptionServiceProviders } from '../../../../test-utils/providers/option.service.providers';
import {
CreateManyOptionsDto,
CreateOptionDto,
ReplaceManyOptionsDto,
} from './dtos';
import {
OptionKeyDuplicatedException,
OptionNameDuplicatedException,
} from './exceptions';
import { OptionEntity } from './option.entity';
import { OptionService } from './option.service';
describe('Option Test suite', () => {
let optionService: OptionService;
let optionRepo: Repository<OptionEntity>;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [TestConfig],
providers: OptionServiceProviders,
}).compile();
optionService = module.get<OptionService>(OptionService);
optionRepo = module.get(getRepositoryToken(OptionEntity));
});
describe('create', () => {
it('creating an option succeeds with a new valid input', async () => {
const fieldId = faker.number.int();
const dto = new CreateOptionDto();
dto.fieldId = fieldId;
dto.key = faker.string.sample();
dto.name = faker.string.sample();
jest.spyOn(optionRepo, 'findBy').mockResolvedValue([
{
key: faker.string.sample(),
name: faker.string.sample(),
},
] as OptionEntity[]);
jest.spyOn(optionRepo, 'save');
const option = await optionService.create(dto);
expect(option.key).toBe(dto.key);
expect(option.name).toBe(dto.name);
});
it('creating an option succeeds with an inactive input', async () => {
const fieldId = faker.number.int();
const optionId = faker.number.int();
const dto = new CreateOptionDto();
dto.fieldId = fieldId;
dto.key = faker.string.sample();
dto.name = faker.string.sample();
jest.spyOn(optionRepo, 'findBy').mockResolvedValue([
{
id: optionId,
key: 'deleted_' + dto.key,
name: dto.name,
},
] as OptionEntity[]);
jest.spyOn(optionRepo, 'save');
const option = await optionService.create(dto);
expect(option.key).toBe(dto.key);
expect(option.name).toBe(dto.name);
});
it('creating an option fails with a duplicate name', async () => {
const fieldId = faker.number.int();
const duplicateName = optionFixture.name;
const dto = new CreateOptionDto();
dto.fieldId = fieldId;
dto.key = faker.string.sample();
dto.name = duplicateName;
await expect(optionService.create(dto)).rejects.toThrow(
OptionNameDuplicatedException,
);
});
it('creating an option fails with a duplicate key', async () => {
const fieldId = faker.number.int();
const duplicateKey = optionFixture.key;
const dto = new CreateOptionDto();
dto.fieldId = fieldId;
dto.key = duplicateKey;
dto.name = faker.string.sample();
await expect(optionService.create(dto)).rejects.toThrow(
OptionKeyDuplicatedException,
);
});
});
describe('createMany', () => {
it('creating many options succeeds with valid inputs', async () => {
const fieldId = faker.number.int();
const optionLength = faker.number.int({ min: 1, max: 10 });
const dto = new CreateManyOptionsDto();
dto.fieldId = fieldId;
dto.options = Array.from({
length: optionLength,
}).map(() => ({
key: faker.string.sample(),
name: faker.string.sample(),
}));
jest.spyOn(optionRepo, 'save');
const options = await optionService.createMany(dto);
for (let i = 0; i < optionLength; i++) {
expect(options[i].key).toBe(dto.options[i].key);
expect(options[i].name).toBe(dto.options[i].name);
}
});
it('creating many options fails with duplicate names', async () => {
const fieldId = faker.number.int();
const dto = new CreateManyOptionsDto();
dto.fieldId = fieldId;
dto.options = Array.from({
length: faker.number.int({ min: 2, max: 10 }),
}).map(() => ({
key: faker.string.sample(),
name: 'duplicateName',
}));
await expect(optionService.createMany(dto)).rejects.toThrow(
OptionNameDuplicatedException,
);
});
it('creating many options fails with duplicate keys', async () => {
const fieldId = faker.number.int();
const dto = new CreateManyOptionsDto();
dto.fieldId = fieldId;
dto.options = Array.from({
length: faker.number.int({ min: 2, max: 10 }),
}).map(() => ({
key: 'duplicateKey',
name: faker.string.sample(),
}));
await expect(optionService.createMany(dto)).rejects.toThrow(
OptionKeyDuplicatedException,
);
});
});
describe('replaceMany', () => {
it('replacing many options succeeds with valid inputs', async () => {
const fieldId = faker.number.int();
const length = faker.number.int({ min: 1, max: 10 });
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = Array.from({
length,
}).map(() => ({
id: faker.number.int(),
key: faker.string.sample(),
name: faker.string.sample(),
}));
jest.spyOn(optionRepo, 'find').mockResolvedValue(
Array.from({
length: faker.number.int({ min: 1, max: 10 }),
}).map(() => ({
id: faker.number.int(),
key: faker.string.sample(),
name: faker.string.sample(),
deletedAt: null,
})) as unknown as OptionEntity[],
);
jest.spyOn(optionRepo, 'query');
jest.spyOn(optionRepo, 'save');
await optionService.replaceMany(dto);
expect(optionRepo.save).toHaveBeenCalledTimes(length);
});
it('replacing many options fails with duplicate names', async () => {
const fieldId = faker.number.int();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = Array.from({
length: faker.number.int({ min: 2, max: 10 }),
}).map(() => ({
id: faker.number.int(),
key: faker.string.sample(),
name: 'duplicateName',
}));
await expect(optionService.replaceMany(dto)).rejects.toThrow(
OptionNameDuplicatedException,
);
});
it('replacing many options fails with duplicate keys', async () => {
const fieldId = faker.number.int();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = Array.from({
length: faker.number.int({ min: 2, max: 10 }),
}).map(() => ({
id: faker.number.int(),
key: 'duplicateKey',
name: faker.string.sample(),
}));
await expect(optionService.replaceMany(dto)).rejects.toThrow(
OptionKeyDuplicatedException,
);
});
it('replacing many options succeeds with empty options array', async () => {
const fieldId = faker.number.int();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = [];
jest.spyOn(optionRepo, 'find').mockResolvedValue([]);
jest.spyOn(optionRepo, 'query');
jest.spyOn(optionRepo, 'save');
await optionService.replaceMany(dto);
expect(optionRepo.save).toHaveBeenCalledTimes(0);
});
it('replacing many options handles inactive options correctly', async () => {
const fieldId = faker.number.int();
const optionId = faker.number.int();
const key = faker.string.sample();
const name = faker.string.sample();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = [{ id: optionId, key, name }];
jest.spyOn(optionRepo, 'find').mockResolvedValue([
{
id: optionId,
key: 'deleted_' + key,
name,
deletedAt: new Date(),
},
] as unknown as OptionEntity[]);
jest.spyOn(optionRepo, 'query');
jest.spyOn(optionRepo, 'save');
await optionService.replaceMany(dto);
expect(optionRepo.save).toHaveBeenCalledTimes(1);
});
it('replacing many options deletes unused options', async () => {
const fieldId = faker.number.int();
const existingOptionId = faker.number.int();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = [
{
id: faker.number.int(),
key: faker.string.sample(),
name: faker.string.sample(),
},
];
jest.spyOn(optionRepo, 'find').mockResolvedValue([
{
id: existingOptionId,
key: faker.string.sample(),
name: faker.string.sample(),
deletedAt: null,
},
] as unknown as OptionEntity[]);
jest.spyOn(optionRepo, 'query');
jest.spyOn(optionRepo, 'save');
await optionService.replaceMany(dto);
expect(optionRepo.query).toHaveBeenCalledWith(
expect.stringContaining('UPDATE'),
expect.arrayContaining([expect.any(String), [existingOptionId]]),
);
});
});
describe('findByFieldId', () => {
it('finding options by field id succeeds', async () => {
const fieldId = faker.number.int();
const mockOptions = Array.from({
length: faker.number.int({ min: 1, max: 10 }),
}).map(() => ({
id: faker.number.int(),
key: faker.string.sample(),
name: faker.string.sample(),
fieldId,
})) as unknown as OptionEntity[];
jest.spyOn(optionRepo, 'findBy').mockResolvedValue(mockOptions);
const result = await optionService.findByFieldId({ fieldId });
expect(optionRepo.findBy).toHaveBeenCalledWith({
field: { id: fieldId },
});
expect(result).toEqual(mockOptions);
});
it('finding options by field id returns empty array when no options exist', async () => {
const fieldId = faker.number.int();
jest.spyOn(optionRepo, 'findBy').mockResolvedValue([]);
const result = await optionService.findByFieldId({ fieldId });
expect(result).toEqual([]);
});
});
describe('create edge cases', () => {
it('creating an option with empty key succeeds', async () => {
const fieldId = faker.number.int();
const name = faker.string.sample();
const dto = new CreateOptionDto();
dto.fieldId = fieldId;
dto.key = '';
dto.name = name;
jest.spyOn(optionRepo, 'findBy').mockResolvedValue([]);
jest
.spyOn(optionRepo, 'save')
.mockImplementation(() => Promise.resolve({ key: '', name } as any));
const result = await optionService.create(dto);
expect(result.key).toBe('');
});
it('creating an option with empty name succeeds', async () => {
const fieldId = faker.number.int();
const key = faker.string.sample();
const dto = new CreateOptionDto();
dto.fieldId = fieldId;
dto.key = key;
dto.name = '';
jest.spyOn(optionRepo, 'findBy').mockResolvedValue([]);
jest
.spyOn(optionRepo, 'save')
.mockImplementation(() => Promise.resolve({ key, name: '' } as any));
const result = await optionService.create(dto);
expect(result.name).toBe('');
});
it('creating an option with null fieldId succeeds', async () => {
const dto = new CreateOptionDto();
dto.fieldId = null as any;
dto.key = faker.string.sample();
dto.name = faker.string.sample();
jest.spyOn(optionRepo, 'findBy').mockResolvedValue([]);
jest
.spyOn(optionRepo, 'save')
.mockImplementation(() =>
Promise.resolve({ key: '', name: faker.string.sample() } as any),
);
const result = await optionService.create(dto);
expect(result).toBeDefined();
});
});
describe('createMany edge cases', () => {
it('creating many options with empty array succeeds', async () => {
const fieldId = faker.number.int();
const dto = new CreateManyOptionsDto();
dto.fieldId = fieldId;
dto.options = [];
jest
.spyOn(optionRepo, 'save')
.mockImplementation(() => Promise.resolve([] as any));
const result = await optionService.createMany(dto);
expect(result).toEqual([]);
expect(optionRepo.save).toHaveBeenCalledWith([]);
});
it('creating many options with single option succeeds', async () => {
const fieldId = faker.number.int();
const dto = new CreateManyOptionsDto();
dto.fieldId = fieldId;
dto.options = [
{ key: faker.string.sample(), name: faker.string.sample() },
];
jest
.spyOn(optionRepo, 'save')
.mockImplementation(() => Promise.resolve([{}] as any));
const result = await optionService.createMany(dto);
expect(result).toHaveLength(1);
});
});
describe('replaceMany edge cases', () => {
it('replacing many options with null options array succeeds', async () => {
const fieldId = faker.number.int();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = null as any;
jest.spyOn(optionRepo, 'find').mockResolvedValue([]);
jest.spyOn(optionRepo, 'query');
jest.spyOn(optionRepo, 'save');
await optionService.replaceMany(dto);
expect(optionRepo.save).toHaveBeenCalledTimes(0);
});
it('replacing many options with undefined options array succeeds', async () => {
const fieldId = faker.number.int();
const dto = new ReplaceManyOptionsDto();
dto.fieldId = fieldId;
dto.options = undefined as any;
jest.spyOn(optionRepo, 'find').mockResolvedValue([]);
jest.spyOn(optionRepo, 'query');
jest.spyOn(optionRepo, 'save');
await optionService.replaceMany(dto);
expect(optionRepo.save).toHaveBeenCalledTimes(0);
});
});
});