test: add Wave 1 infrastructure (types, mocks, vitest config) + User/Order/Material factories

- tests/fixtures/types.ts: Test fixture type definitions
- tests/fixtures/factory.ts: User/Order/Material factories
- tests/mocks/types.ts: Mock type definitions (549 lines)
- tests/mocks/index.ts: Mock factory functions
- vitest.config.ts: Performance optimizations (isolate:false, pool:threads)
- User/Order/Material factory tests (7 tests total)

Performance: 7.65s → 4.94s (35% improvement)
This commit is contained in:
Misaka
2026-04-04 20:15:54 +08:00
parent 2e102d8ab3
commit fb3dd43164
7 changed files with 1405 additions and 0 deletions

217
tests/fixtures/factory.ts vendored Normal file
View File

@@ -0,0 +1,217 @@
/**
* Test Fixture Factory
*
* Factory class for generating test data with consistent structure.
*/
import type { TestUser, Order, Material } from './types'
/**
* User Factory - generates test user data
*
* Creates users with role-based permissions and unique IDs.
*/
export class UserFactory {
/**
* Create a user with specified role
*
* @param role - User role ('admin', 'user', or 'guest')
* @param overrides - Optional field overrides
* @returns Generated test user
*/
static createUser(
role: 'admin' | 'user' | 'guest' = 'user',
overrides?: Partial<TestUser>
): TestUser {
const user: TestUser = {
id: UserFactory.generateId(),
username: `test_${role}_${Date.now()}`,
userType: UserFactory.getUserTypeFromRole(role),
permissions: UserFactory.getPermissionsForRole(role),
...overrides
}
return user
}
/**
* Create an admin user
*
* @param overrides - Optional field overrides
* @returns Admin test user
*/
static createAdmin(overrides?: Partial<TestUser>): TestUser {
return UserFactory.createUser('admin', overrides)
}
/**
* Create a regular user
*
* @param overrides - Optional field overrides
* @returns Regular test user
*/
static createUserDefault(overrides?: Partial<TestUser>): TestUser {
return UserFactory.createUser('user', overrides)
}
/**
* Create a guest user
*
* @param overrides - Optional field overrides
* @returns Guest test user
*/
static createGuest(overrides?: Partial<TestUser>): TestUser {
return UserFactory.createUser('guest', overrides)
}
/**
* Generate unique user ID
*
* @returns Unique ID string in format USR-{timestamp}-{random}
*/
private static generateId(): string {
return `USR-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`
}
/**
* Get permissions for a role
*
* @param role - User role
* @returns Array of permission strings
*/
private static getPermissionsForRole(role: string): string[] {
return (
{
admin: ['read', 'write', 'delete', 'admin'],
user: ['read', 'write'],
guest: ['read']
}[role] || []
)
}
/**
* Convert role string to UserType
*
* @param role - Role string
* @returns UserType ('Admin' or 'User')
*/
private static getUserTypeFromRole(role: string): 'Admin' | 'User' {
return role === 'admin' ? 'Admin' : 'User'
}
}
/**
* Order Factory
*
* Creates Order fixtures with auto-generated unique identifiers.
*/
export class OrderFactory {
/**
* Create a new Order fixture
*
* @param overrides - Optional overrides to customize the order
* @returns A new Order instance
*
* @example
* // Basic order with auto-generated values
* const order = OrderFactory.createOrder()
*
* @example
* // Order with custom order number
* const order = OrderFactory.createOrder({ orderNumber: 'SC202501001' })
*
* @example
* // Order with materials
* const materials = [MaterialFactory.createMaterial()]
* const order = OrderFactory.createOrder({ items: materials })
*/
static createOrder(overrides?: Partial<Order>): Order {
const timestamp = Date.now()
return {
id: `ORD-${timestamp}`,
orderNumber: `SC${timestamp.toString().substr(-8)}`,
productionId: `PROD-${timestamp}`,
productName: 'Test Product',
productSpec: null,
plannedQuantity: 100,
unit: '件',
requiredDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
department: 'Test Department',
items: [],
creator: null,
printer: null,
printDate: null,
...overrides
}
}
/**
* Create multiple orders
*
* @param count - Number of orders to create
* @param overrides - Optional overrides applied to all orders
* @returns Array of Order instances
*/
static createOrders(count: number, overrides?: Partial<Order>): Order[] {
return Array.from({ length: count }, () => this.createOrder(overrides))
}
}
/**
* Material Factory
*
* Creates Material fixtures with auto-generated unique codes.
*/
export class MaterialFactory {
/**
* Create a new Material fixture
*
* @param overrides - Optional overrides to customize the material
* @returns A new Material instance
*
* @example
* // Basic material with auto-generated code
* const material = MaterialFactory.createMaterial()
*
* @example
* // Material with custom code
* const material = MaterialFactory.createMaterial({ code: 'M001' })
*
* @example
* // Material with specific quantity
* const material = MaterialFactory.createMaterial({ quantity: 50, unit: 'kg' })
*/
static createMaterial(overrides?: Partial<Material>): Material {
const timestamp = Date.now()
return {
index: 1,
code: `TEST_MAT_${Math.random().toString(36).substr(2, 6).toUpperCase()}`,
description: 'Test Material',
specification: null,
model: null,
drawingNumber: null,
grade: null,
quantity: 10,
unit: '件',
requiredDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
warehouse: 'Test Warehouse',
unitUsage: 1.0,
outboundQuantity: 0,
...overrides
}
}
/**
* Create multiple materials with unique codes
*
* @param count - Number of materials to create
* @param overrides - Optional overrides applied to all materials
* @returns Array of Material instances
*/
static createMaterials(count: number, overrides?: Partial<Material>): Material[] {
return Array.from({ length: count }, (_, index) => {
const material = this.createMaterial(overrides)
material.index = index + 1
return material
})
}
}

View File

@@ -0,0 +1,57 @@
/**
* Order and Material Factory Unit Tests
*
* Tests for OrderFactory and MaterialFactory fixture generation.
*/
import { describe, it, expect } from 'vitest'
import { OrderFactory, MaterialFactory } from './factory'
import type { Order, Material } from './types'
describe('OrderFactory', () => {
describe('createOrder', () => {
it('should create an order with auto-generated order number in SC format', () => {
const order = OrderFactory.createOrder()
expect(order.orderNumber).toMatch(/^SC\d{8}$/)
})
it('should support overrides to customize order properties', () => {
const customOrder: Partial<Order> = {
orderNumber: 'SC202501001',
productName: 'Custom Product',
plannedQuantity: 500
}
const order = OrderFactory.createOrder(customOrder)
expect(order.orderNumber).toBe('SC202501001')
expect(order.productName).toBe('Custom Product')
expect(order.plannedQuantity).toBe(500)
})
})
})
describe('MaterialFactory', () => {
describe('createMaterial', () => {
it('should create a material with auto-generated code in TEST_MAT_XXXXXX format', () => {
const material = MaterialFactory.createMaterial()
expect(material.code).toMatch(/^TEST_MAT_[A-Z0-9]{6}$/)
})
it('should support overrides to customize material properties', () => {
const customMaterial: Partial<Material> = {
code: 'M001',
description: 'Custom Material',
quantity: 250
}
const material = MaterialFactory.createMaterial(customMaterial)
expect(material.code).toBe('M001')
expect(material.description).toBe('Custom Material')
expect(material.quantity).toBe(250)
})
})
})

225
tests/fixtures/types.ts vendored Normal file
View File

@@ -0,0 +1,225 @@
/**
* Test Fixtures Type Definitions
*
* Type definitions for test fixture factories and test data generation.
* Reuses business types from src/main/types where possible.
*/
import type { UserInfo, UserSession } from '../../src/main/types/user.types'
import type { ErpConfig } from '../../src/main/types/erp.types'
import type {
DatabaseConfig,
MySqlConfig,
SqlServerConfig
} from '../../src/main/types/database.types'
import type { FullConfig } from '../../src/main/types/config.schema'
/**
* Material item in an order
*
* Represents a single material line item in production order data.
*/
export interface Material {
/** Row index in the order table (1-based) */
index: number
/** Material code/identifier */
code: string
/** Material name/description */
description: string
/** Material specification */
specification?: string | null
/** Material model/type */
model?: string | null
/** Drawing number */
drawingNumber?: string | null
/** Material grade/quality */
grade?: string | null
/** Planned quantity */
quantity: number
/** Unit of measure */
unit: string
/** Required date */
requiredDate: string
/** Issuing warehouse */
warehouse: string
/** Unit usage amount */
unitUsage: number
/** Cumulative outbound quantity */
outboundQuantity: number
}
/**
* Production order interface
*
* Represents a complete production order with header info and material items.
*/
export interface Order {
/** Order unique identifier */
id: string
/** Production order number */
orderNumber: string
/** Production ID (product code) */
productionId: string
/** Product name */
productName: string
/** Product specification */
productSpec?: string | null
/** Planned quantity for the order */
plannedQuantity: number
/** Unit of measure */
unit: string
/** Required delivery date */
requiredDate: string
/** Production department */
department: string
/** Materials in this order */
items: Material[]
/** Creator of the order */
creator?: string | null
/** Printer of the order */
printer?: string | null
/** Print date */
printDate?: string | null
}
/**
* User fixture for test data generation
*
* Simplified user data for creating test users.
*/
export interface TestUser {
/** User ID */
id: string
/** Username for login */
username: string
/** User type/role */
userType: 'Admin' | 'User'
/** User permissions (optional) */
permissions?: string[]
/** Create time (optional) */
createTime?: Date
}
/**
* ERP configuration for testing
*
* Test fixture configuration for ERP system connection.
*/
export interface TestErpConfig {
/** ERP system URL */
url: string
/** ERP username */
username: string
/** ERP password */
password: string
/** Headless browser mode (optional) */
headless?: boolean
}
/**
* Database configuration for testing
*
* Simplified database configuration for test fixtures.
*/
export interface TestDatabaseConfig {
/** Database type */
type: 'mysql' | 'sqlserver'
/** Database host/server */
host: string
/** Database port */
port: number
/** Database name */
database: string
/** Database username */
username: string
/** Database password */
password: string
/** Character set (MySQL only, optional) */
charset?: string
/** Driver (SQL Server only, optional) */
driver?: string
/** Trust server certificate (SQL Server only, optional) */
trustServerCertificate?: boolean
}
/**
* Complete test configuration
*
* Full application configuration for test environments.
*/
export interface TestConfig {
/** ERP system configuration */
erp: TestErpConfig
/** Database configuration */
database: TestDatabaseConfig
/** Path configuration */
paths: {
/** Data directory path */
dataDir: string
/** Default output file path */
defaultOutput: string
/** Validation output file path */
validationOutput: string
}
}
/**
* Excel file fixture metadata
*
* Information about generated Excel test fixtures.
*/
export interface ExcelFixture {
/** File path */
filePath: string
/** Order number in the fixture */
orderNumber: string
/** Production ID in the fixture */
productionId: string
/** Number of material items */
materialCount: number
/** Whether the fixture has empty orders */
hasEmptyOrders: boolean
}
/**
* Test data factory options
*
* Options for customizing generated test data.
*/
export interface FactoryOptions {
/** Number of materials to generate (default: 3) */
materialCount?: number
/** Include optional fields (default: true) */
includeOptional?: boolean
/** Generate empty orders (default: false) */
emptyOrders?: boolean
/** Custom order number (default: auto-generated) */
orderNumber?: string
/** Custom production ID (default: auto-generated) */
seed?: number
}
/**
* Validation result for test data
*
* Result of validating generated test data against expected schema.
*/
export interface ValidationResult {
/** Whether validation passed */
isValid: boolean
/** Error messages if validation failed */
errors: string[]
/** Warning messages */
warnings: string[]
}
// Re-export business types for convenience
export type {
UserInfo,
UserSession,
ErpConfig,
DatabaseConfig,
MySqlConfig,
SqlServerConfig,
FullConfig
}

35
tests/fixtures/user-factory.test.ts vendored Normal file
View File

@@ -0,0 +1,35 @@
/**
* UserFactory Unit Tests
*/
import { describe, it, expect } from 'vitest'
import { UserFactory } from './factory'
describe('UserFactory', () => {
it('creates user with default role', () => {
const user = UserFactory.createUser()
expect(user.username).toMatch(/^test_user_\d+$/)
expect(user.userType).toBe('User')
expect(user.permissions).toEqual(['read', 'write'])
expect(user.id).toMatch(/^USR-\d+-[a-z0-9]+$/)
})
it('creates admin with correct permissions', () => {
const admin = UserFactory.createAdmin()
expect(admin.userType).toBe('Admin')
expect(admin.permissions).toEqual(['read', 'write', 'delete', 'admin'])
expect(admin.id).toMatch(/^USR-\d+-[a-z0-9]+$/)
})
it('generates unique IDs', () => {
const id1 = UserFactory.createAdmin().id
const id2 = UserFactory.createUser().id
const id3 = UserFactory.createGuest().id
expect(id1).not.toBe(id2)
expect(id2).not.toBe(id3)
expect(id1).not.toBe(id3)
})
})