fix(test): improve logger mock path and add behavior-based repository tests
- Fix logger-performance test mock path to use bare module specifier - Replace meaningless "should be defined" assertions in repositories test with behavior-based tests covering upsert, batch operations, queries, deletes, and error handling for both MaterialsToBeDeletedRepository and DiscreteMaterialPlanRepository Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
|||||||
import logger from '../../src/main/services/logger/index'
|
import logger from '../../src/main/services/logger/index'
|
||||||
|
|
||||||
// Mock the logger to avoid noisy output during tests
|
// Mock the logger to avoid noisy output during tests
|
||||||
vi.mock('../../src/main/services/logger/index', () => ({
|
vi.mock('../../src/main/services/logger', () => ({
|
||||||
default: {
|
default: {
|
||||||
debug: vi.fn(),
|
debug: vi.fn(),
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
|
|||||||
@@ -1,59 +1,45 @@
|
|||||||
/**
|
/**
|
||||||
* Repository Unit Tests
|
* Repository Unit Tests
|
||||||
*
|
*
|
||||||
* Tests for TypeORM repository patterns.
|
* Behavior-based tests for MaterialsToBeDeletedRepository and DiscreteMaterialPlanRepository.
|
||||||
* Note: These tests mock the database connections.
|
* Verifies correct delegation to TypeORM methods, return values, and safe defaults on error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||||
|
import { createMockRepository, createMockQueryBuilder } from '../mocks'
|
||||||
|
|
||||||
// Mock TypeORM
|
// --- Shared mock state ---
|
||||||
|
|
||||||
|
let mockRepo: ReturnType<typeof createMockRepository>
|
||||||
|
|
||||||
|
// Mock TypeORM decorators (entities still need them)
|
||||||
vi.mock('typeorm', () => {
|
vi.mock('typeorm', () => {
|
||||||
// Create mock decorator functions
|
const decorator = vi.fn()
|
||||||
const Entity = vi.fn()
|
|
||||||
const PrimaryGeneratedColumn = vi.fn()
|
|
||||||
const Column = vi.fn()
|
|
||||||
const ManyToOne = vi.fn()
|
|
||||||
const OneToMany = vi.fn()
|
|
||||||
const ManyToMany = vi.fn()
|
|
||||||
const JoinColumn = vi.fn()
|
|
||||||
const JoinTable = vi.fn()
|
|
||||||
const CreateDateColumn = vi.fn()
|
|
||||||
const UpdateDateColumn = vi.fn()
|
|
||||||
const DeleteDateColumn = vi.fn()
|
|
||||||
const Index = vi.fn()
|
|
||||||
const Unique = vi.fn()
|
|
||||||
const Check = vi.fn()
|
|
||||||
const Exclusion = vi.fn()
|
|
||||||
const Generated = vi.fn()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
DataSource: vi.fn(() => ({
|
DataSource: vi.fn(() => ({
|
||||||
initialize: vi.fn().mockResolvedValue({}),
|
initialize: vi.fn().mockResolvedValue(undefined),
|
||||||
isInitialized: false,
|
isInitialized: true,
|
||||||
getRepository: vi.fn(),
|
getRepository: vi.fn(() => mockRepo),
|
||||||
destroy: vi.fn()
|
destroy: vi.fn()
|
||||||
})),
|
})),
|
||||||
Repository: vi.fn(),
|
Repository: vi.fn(),
|
||||||
In: vi.fn((arr) => arr),
|
In: vi.fn((arr: unknown[]) => arr),
|
||||||
// Add all the decorators that entities use
|
Entity: decorator,
|
||||||
Entity,
|
PrimaryGeneratedColumn: decorator,
|
||||||
PrimaryGeneratedColumn,
|
Column: decorator,
|
||||||
Column,
|
ManyToOne: decorator,
|
||||||
ManyToOne,
|
OneToMany: decorator,
|
||||||
OneToMany,
|
ManyToMany: decorator,
|
||||||
ManyToMany,
|
JoinColumn: decorator,
|
||||||
JoinColumn,
|
JoinTable: decorator,
|
||||||
JoinTable,
|
CreateDateColumn: decorator,
|
||||||
CreateDateColumn,
|
UpdateDateColumn: decorator,
|
||||||
UpdateDateColumn,
|
DeleteDateColumn: decorator,
|
||||||
DeleteDateColumn,
|
Index: decorator,
|
||||||
Index,
|
Unique: decorator,
|
||||||
Unique,
|
Check: decorator,
|
||||||
Check,
|
Exclusion: decorator,
|
||||||
Exclusion,
|
Generated: decorator,
|
||||||
Generated,
|
|
||||||
// Other TypeORM exports
|
|
||||||
Between: vi.fn(),
|
Between: vi.fn(),
|
||||||
LessThan: vi.fn(),
|
LessThan: vi.fn(),
|
||||||
LessThanOrEqual: vi.fn(),
|
LessThanOrEqual: vi.fn(),
|
||||||
@@ -76,40 +62,577 @@ vi.mock('../../src/main/services/logger', () => ({
|
|||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('../../src/main/services/database/data-source', () => ({
|
||||||
|
getDataSource: vi.fn(() => ({
|
||||||
|
isInitialized: true,
|
||||||
|
initialize: vi.fn().mockResolvedValue(undefined),
|
||||||
|
getRepository: vi.fn(() => mockRepo)
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// MaterialsToBeDeletedRepository
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
describe('MaterialsToBeDeletedRepository', () => {
|
describe('MaterialsToBeDeletedRepository', () => {
|
||||||
beforeEach(() => {
|
let MaterialsToBeDeletedRepository: typeof import(
|
||||||
|
'../../src/main/services/database/repositories/MaterialsToBeDeletedRepository'
|
||||||
|
).MaterialsToBeDeletedRepository
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
mockRepo = createMockRepository()
|
||||||
|
const mod = await import(
|
||||||
|
'../../src/main/services/database/repositories/MaterialsToBeDeletedRepository'
|
||||||
|
)
|
||||||
|
MaterialsToBeDeletedRepository = mod.MaterialsToBeDeletedRepository
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be defined', async () => {
|
afterEach(() => {
|
||||||
const { MaterialsToBeDeletedRepository } =
|
vi.resetModules()
|
||||||
await import('../../src/main/services/database/repositories/MaterialsToBeDeletedRepository')
|
|
||||||
expect(MaterialsToBeDeletedRepository).toBeDefined()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create repository instance', async () => {
|
// --- upsert ---
|
||||||
const { MaterialsToBeDeletedRepository } =
|
|
||||||
await import('../../src/main/services/database/repositories/MaterialsToBeDeletedRepository')
|
it('upsert: creates new entity when not found', async () => {
|
||||||
const repo = new MaterialsToBeDeletedRepository()
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
expect(repo).toBeDefined()
|
mockRepo.findOne!.mockResolvedValue(null)
|
||||||
|
mockRepo.create!.mockReturnValue({ materialCode: 'MAT01', managerName: 'Alice' })
|
||||||
|
|
||||||
|
const result = await repo.upsert('MAT01', 'Alice')
|
||||||
|
|
||||||
|
expect(result).toBe(true)
|
||||||
|
expect(mockRepo.findOne).toHaveBeenCalledWith({ where: { materialCode: 'MAT01' } })
|
||||||
|
expect(mockRepo.create).toHaveBeenCalledWith({ materialCode: 'MAT01', managerName: 'Alice' })
|
||||||
|
expect(mockRepo.save).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('upsert: updates existing entity', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
const existing = { materialCode: 'MAT01', managerName: 'Bob' }
|
||||||
|
mockRepo.findOne!.mockResolvedValue(existing)
|
||||||
|
|
||||||
|
const result = await repo.upsert('MAT01', 'Alice')
|
||||||
|
|
||||||
|
expect(result).toBe(true)
|
||||||
|
expect(existing.managerName).toBe('Alice')
|
||||||
|
expect(mockRepo.save).toHaveBeenCalledWith(existing)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('upsert: returns false on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.findOne!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.upsert('MAT01', 'Alice')
|
||||||
|
|
||||||
|
expect(result).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- upsertBatch ---
|
||||||
|
|
||||||
|
it('upsertBatch: processes valid materials', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.findOne!.mockResolvedValue(null)
|
||||||
|
mockRepo.create!.mockImplementation((data) => data)
|
||||||
|
|
||||||
|
const stats = await repo.upsertBatch([
|
||||||
|
{ materialCode: 'M1', managerName: 'A' },
|
||||||
|
{ materialCode: 'M2', managerName: 'B' }
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(stats.total).toBe(2)
|
||||||
|
expect(stats.success).toBe(2)
|
||||||
|
expect(stats.failed).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('upsertBatch: skips empty materialCodes', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
|
||||||
|
const stats = await repo.upsertBatch([
|
||||||
|
{ materialCode: '', managerName: 'A' },
|
||||||
|
{ materialCode: ' ', managerName: 'B' },
|
||||||
|
{ materialCode: 'M1', managerName: 'C' }
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(stats.total).toBe(3)
|
||||||
|
expect(stats.success).toBe(1)
|
||||||
|
expect(stats.failed).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('upsertBatch: returns stats with partial failures', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.findOne!.mockRejectedValue(new Error('fail'))
|
||||||
|
|
||||||
|
const stats = await repo.upsertBatch([{ materialCode: 'M1', managerName: 'A' }])
|
||||||
|
|
||||||
|
expect(stats.total).toBe(1)
|
||||||
|
expect(stats.failed).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- getAllMaterialCodes ---
|
||||||
|
|
||||||
|
it('getAllMaterialCodes: returns Set via QueryBuilder', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
const qb = createMockQueryBuilder({
|
||||||
|
result: [{ materialCode: 'M1' }, { materialCode: 'M2' }]
|
||||||
|
})
|
||||||
|
mockRepo.createQueryBuilder!.mockReturnValue(qb)
|
||||||
|
|
||||||
|
const result = await repo.getAllMaterialCodes()
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(Set)
|
||||||
|
expect(result.size).toBe(2)
|
||||||
|
expect(result.has('M1')).toBe(true)
|
||||||
|
expect(result.has('M2')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getAllMaterialCodes: returns empty Set on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.createQueryBuilder!.mockImplementation(() => {
|
||||||
|
throw new Error('db fail')
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await repo.getAllMaterialCodes()
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(Set)
|
||||||
|
expect(result.size).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- getAllRecords ---
|
||||||
|
|
||||||
|
it('getAllRecords: returns ordered records', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
const records = [{ materialCode: 'M1' }, { materialCode: 'M2' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.getAllRecords()
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
expect(mockRepo.find).toHaveBeenCalledWith({
|
||||||
|
order: { managerName: 'ASC', materialCode: 'ASC' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getAllRecords: returns empty array on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.getAllRecords()
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- getByManager ---
|
||||||
|
|
||||||
|
it('getByManager: filters by managerName', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
const records = [{ materialCode: 'M1', managerName: 'Alice' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.getByManager('Alice')
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
expect(mockRepo.find).toHaveBeenCalledWith({
|
||||||
|
where: { managerName: 'Alice' },
|
||||||
|
order: { materialCode: 'ASC' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getByManager: returns empty array on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.getByManager('Alice')
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- getManagers ---
|
||||||
|
|
||||||
|
it('getManagers: returns distinct names via QueryBuilder', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
const qb = createMockQueryBuilder({
|
||||||
|
result: [{ managerName: 'Alice' }, { managerName: 'Bob' }]
|
||||||
|
})
|
||||||
|
mockRepo.createQueryBuilder!.mockReturnValue(qb)
|
||||||
|
|
||||||
|
const result = await repo.getManagers()
|
||||||
|
|
||||||
|
expect(result).toEqual(['Alice', 'Bob'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getManagers: returns empty array on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.createQueryBuilder!.mockImplementation(() => {
|
||||||
|
throw new Error('db fail')
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await repo.getManagers()
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- deleteByMaterialCode ---
|
||||||
|
|
||||||
|
it('deleteByMaterialCode: returns true when affected > 0', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.delete!.mockResolvedValue({ affected: 1 })
|
||||||
|
|
||||||
|
const result = await repo.deleteByMaterialCode('M1')
|
||||||
|
|
||||||
|
expect(result).toBe(true)
|
||||||
|
expect(mockRepo.delete).toHaveBeenCalledWith({ materialCode: 'M1' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deleteByMaterialCode: returns false when affected is 0', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.delete!.mockResolvedValue({ affected: 0 })
|
||||||
|
|
||||||
|
const result = await repo.deleteByMaterialCode('M1')
|
||||||
|
|
||||||
|
expect(result).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deleteByMaterialCode: returns false on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.delete!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.deleteByMaterialCode('M1')
|
||||||
|
|
||||||
|
expect(result).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- deleteByMaterialCodes ---
|
||||||
|
|
||||||
|
it('deleteByMaterialCodes: returns 0 for empty array', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
|
||||||
|
const result = await repo.deleteByMaterialCodes([])
|
||||||
|
|
||||||
|
expect(result).toBe(0)
|
||||||
|
expect(mockRepo.delete).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deleteByMaterialCodes: returns affected count', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.delete!.mockResolvedValue({ affected: 3 })
|
||||||
|
|
||||||
|
const result = await repo.deleteByMaterialCodes(['M1', 'M2', 'M3'])
|
||||||
|
|
||||||
|
expect(result).toBe(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deleteByMaterialCodes: returns 0 on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.delete!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.deleteByMaterialCodes(['M1'])
|
||||||
|
|
||||||
|
expect(result).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- exists ---
|
||||||
|
|
||||||
|
it('exists: returns true when count > 0', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.count!.mockResolvedValue(1)
|
||||||
|
|
||||||
|
const result = await repo.exists('M1')
|
||||||
|
|
||||||
|
expect(result).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('exists: returns false when count is 0', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.count!.mockResolvedValue(0)
|
||||||
|
|
||||||
|
const result = await repo.exists('M1')
|
||||||
|
|
||||||
|
expect(result).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('exists: returns false on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.count!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.exists('M1')
|
||||||
|
|
||||||
|
expect(result).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- countAll ---
|
||||||
|
|
||||||
|
it('countAll: returns count', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.count!.mockResolvedValue(42)
|
||||||
|
|
||||||
|
const result = await repo.countAll()
|
||||||
|
|
||||||
|
expect(result).toBe(42)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('countAll: returns 0 on error', async () => {
|
||||||
|
const repo = new MaterialsToBeDeletedRepository()
|
||||||
|
mockRepo.count!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.countAll()
|
||||||
|
|
||||||
|
expect(result).toBe(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// DiscreteMaterialPlanRepository
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
describe('DiscreteMaterialPlanRepository', () => {
|
describe('DiscreteMaterialPlanRepository', () => {
|
||||||
beforeEach(() => {
|
let DiscreteMaterialPlanRepository: typeof import(
|
||||||
|
'../../src/main/services/database/repositories/DiscreteMaterialPlanRepository'
|
||||||
|
).DiscreteMaterialPlanRepository
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
mockRepo = createMockRepository()
|
||||||
|
const mod = await import(
|
||||||
|
'../../src/main/services/database/repositories/DiscreteMaterialPlanRepository'
|
||||||
|
)
|
||||||
|
DiscreteMaterialPlanRepository = mod.DiscreteMaterialPlanRepository
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be defined', async () => {
|
afterEach(() => {
|
||||||
const { DiscreteMaterialPlanRepository } =
|
vi.resetModules()
|
||||||
await import('../../src/main/services/database/repositories/DiscreteMaterialPlanRepository')
|
|
||||||
expect(DiscreteMaterialPlanRepository).toBeDefined()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create repository instance', async () => {
|
// --- queryAll ---
|
||||||
const { DiscreteMaterialPlanRepository } =
|
|
||||||
await import('../../src/main/services/database/repositories/DiscreteMaterialPlanRepository')
|
it('queryAll: returns find results', async () => {
|
||||||
const repo = new DiscreteMaterialPlanRepository()
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
expect(repo).toBeDefined()
|
const records = [{ sourceNumber: 'S1' }, { sourceNumber: 'S2' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.queryAll()
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryAll: returns empty array on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.queryAll()
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- queryAllDistinctByMaterialCode ---
|
||||||
|
|
||||||
|
it('queryAllDistinctByMaterialCode: calls repo.query() with raw SQL', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
const mockQueryResult = [{ MaterialCode: 'M1', rn: 1 }]
|
||||||
|
mockRepo = { ...createMockRepository(), query: vi.fn().mockResolvedValue(mockQueryResult) }
|
||||||
|
// Re-import to pick up new mockRepo
|
||||||
|
vi.resetModules()
|
||||||
|
const mod = await import(
|
||||||
|
'../../src/main/services/database/repositories/DiscreteMaterialPlanRepository'
|
||||||
|
)
|
||||||
|
const freshRepo = new mod.DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await freshRepo.queryAllDistinctByMaterialCode()
|
||||||
|
|
||||||
|
expect(result).toEqual(mockQueryResult)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryAllDistinctByMaterialCode: returns empty array on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo = {
|
||||||
|
...createMockRepository(),
|
||||||
|
query: vi.fn().mockRejectedValue(new Error('db fail'))
|
||||||
|
}
|
||||||
|
vi.resetModules()
|
||||||
|
const mod = await import(
|
||||||
|
'../../src/main/services/database/repositories/DiscreteMaterialPlanRepository'
|
||||||
|
)
|
||||||
|
const freshRepo = new mod.DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await freshRepo.queryAllDistinctByMaterialCode()
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- queryBySourceNumbers ---
|
||||||
|
|
||||||
|
it('queryBySourceNumbers: returns empty array for empty input', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumbers([])
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
expect(mockRepo.find).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryBySourceNumbers: batches in groups of 2000', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
// Create 2500 source numbers to trigger 2 batches
|
||||||
|
const sourceNumbers = Array.from({ length: 2500 }, (_, i) => `S${i}`)
|
||||||
|
mockRepo.find!.mockResolvedValue([])
|
||||||
|
|
||||||
|
await repo.queryBySourceNumbers(sourceNumbers)
|
||||||
|
|
||||||
|
// Should be called twice: once for 2000, once for 500
|
||||||
|
expect(mockRepo.find).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryBySourceNumbers: returns combined results', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
const records = [{ sourceNumber: 'S1' }, { sourceNumber: 'S2' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumbers(['S1', 'S2'])
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryBySourceNumbers: returns empty array on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumbers(['S1'])
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- queryBySourceNumbersDistinct ---
|
||||||
|
|
||||||
|
it('queryBySourceNumbersDistinct: returns empty array for empty input', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumbersDistinct([])
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryBySourceNumbersDistinct: calls repo.query() per batch', async () => {
|
||||||
|
mockRepo = { ...createMockRepository(), query: vi.fn().mockResolvedValue([{ M: 'X' }]) }
|
||||||
|
vi.resetModules()
|
||||||
|
const mod = await import(
|
||||||
|
'../../src/main/services/database/repositories/DiscreteMaterialPlanRepository'
|
||||||
|
)
|
||||||
|
const repo = new mod.DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumbersDistinct(['S1', 'S2'])
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0)
|
||||||
|
expect((mockRepo as Record<string, unknown>).query).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryBySourceNumbersDistinct: returns empty array on error', async () => {
|
||||||
|
mockRepo = {
|
||||||
|
...createMockRepository(),
|
||||||
|
query: vi.fn().mockRejectedValue(new Error('db fail'))
|
||||||
|
}
|
||||||
|
vi.resetModules()
|
||||||
|
const mod = await import(
|
||||||
|
'../../src/main/services/database/repositories/DiscreteMaterialPlanRepository'
|
||||||
|
)
|
||||||
|
const repo = new mod.DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumbersDistinct(['S1'])
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- queryBySourceNumber ---
|
||||||
|
|
||||||
|
it('queryBySourceNumber: calls find with where clause', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
const records = [{ sourceNumber: 'S1', planNumber: 'P1' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumber('S1')
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
expect(mockRepo.find).toHaveBeenCalledWith({ where: { sourceNumber: 'S1' } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryBySourceNumber: returns empty array on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.queryBySourceNumber('S1')
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- queryByPlanNumber ---
|
||||||
|
|
||||||
|
it('queryByPlanNumber: calls find with where clause', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
const records = [{ sourceNumber: 'S1', planNumber: 'P1' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.queryByPlanNumber('P1')
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
expect(mockRepo.find).toHaveBeenCalledWith({ where: { planNumber: 'P1' } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryByPlanNumber: returns empty array on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.queryByPlanNumber('P1')
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- queryByPlanNumbers ---
|
||||||
|
|
||||||
|
it('queryByPlanNumbers: returns empty array for empty input', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
|
||||||
|
const result = await repo.queryByPlanNumbers([])
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
expect(mockRepo.find).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryByPlanNumbers: calls find with In()', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
const records = [{ planNumber: 'P1' }, { planNumber: 'P2' }]
|
||||||
|
mockRepo.find!.mockResolvedValue(records)
|
||||||
|
|
||||||
|
const result = await repo.queryByPlanNumbers(['P1', 'P2'])
|
||||||
|
|
||||||
|
expect(result).toEqual(records)
|
||||||
|
expect(mockRepo.find).toHaveBeenCalledWith({
|
||||||
|
where: { planNumber: ['P1', 'P2'] } // In() mock returns the array as-is
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queryByPlanNumbers: returns empty array on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.find!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.queryByPlanNumbers(['P1'])
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- countAll ---
|
||||||
|
|
||||||
|
it('countAll: returns count', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.count!.mockResolvedValue(99)
|
||||||
|
|
||||||
|
const result = await repo.countAll()
|
||||||
|
|
||||||
|
expect(result).toBe(99)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('countAll: returns 0 on error', async () => {
|
||||||
|
const repo = new DiscreteMaterialPlanRepository()
|
||||||
|
mockRepo.count!.mockRejectedValue(new Error('db fail'))
|
||||||
|
|
||||||
|
const result = await repo.countAll()
|
||||||
|
|
||||||
|
expect(result).toBe(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user