b34b0dd7d6
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
202 lines
5.7 KiB
TypeScript
202 lines
5.7 KiB
TypeScript
/**
|
|
* Integration Tests: Inventory (WMS) Store
|
|
* Tests warehouse inventory management with mocked API
|
|
*
|
|
* Run: npm run test:integration
|
|
*/
|
|
|
|
import { beforeEach, describe, expect, it } from 'vitest'
|
|
import { setActivePinia, createPinia } from 'pinia'
|
|
import { server } from '@/tests/mocks/server'
|
|
import { http, HttpResponse } from 'msw'
|
|
|
|
/**
|
|
* Mock Inventory Store (placeholder until actual store is created)
|
|
*/
|
|
class MockInventoryStore {
|
|
inventory: any[] = []
|
|
loading = false
|
|
error: string | null = null
|
|
|
|
async fetchInventory() {
|
|
this.loading = true
|
|
try {
|
|
const response = await fetch(`${process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'}/inventory`)
|
|
const data = await response.json()
|
|
this.inventory = data.data || []
|
|
} catch (err) {
|
|
this.error = (err as Error).message
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
}
|
|
|
|
async updateInventory(inventoryId: string, payload: any) {
|
|
this.loading = true
|
|
try {
|
|
const response = await fetch(
|
|
`${process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'}/inventory/${inventoryId}`,
|
|
{
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload)
|
|
}
|
|
)
|
|
if (!response.ok) throw new Error('Update failed')
|
|
const data = await response.json()
|
|
const index = this.inventory.findIndex((i) => i.inventoryId === inventoryId)
|
|
if (index !== -1) this.inventory[index] = data
|
|
return data
|
|
} catch (err) {
|
|
this.error = (err as Error).message
|
|
throw err
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
}
|
|
}
|
|
|
|
describe('Inventory (WMS) Store Integration', () => {
|
|
let store: MockInventoryStore
|
|
|
|
beforeEach(() => {
|
|
store = new MockInventoryStore()
|
|
})
|
|
|
|
describe('fetchInventory', () => {
|
|
it('loads inventory from WMS API', async () => {
|
|
await store.fetchInventory()
|
|
|
|
expect(store.inventory).toHaveLength(1)
|
|
expect(store.inventory[0].inventoryId).toBe('INV-001')
|
|
expect(store.inventory[0].qtyOnHand).toBe(1000)
|
|
expect(store.loading).toBe(false)
|
|
expect(store.error).toBeNull()
|
|
})
|
|
|
|
it('calculates available quantity correctly', async () => {
|
|
await store.fetchInventory()
|
|
|
|
const inv = store.inventory[0]
|
|
const available = inv.qtyOnHand - inv.qtyReserved
|
|
expect(available).toBe(900)
|
|
expect(inv.qtyAvailable).toBe(available)
|
|
})
|
|
|
|
it('handles API errors gracefully', async () => {
|
|
server.use(
|
|
http.get('*/api/inventory', () => {
|
|
return HttpResponse.json(
|
|
{ message: 'Database connection failed' },
|
|
{ status: 500 }
|
|
)
|
|
})
|
|
)
|
|
|
|
await store.fetchInventory()
|
|
|
|
expect(store.error).not.toBeNull()
|
|
expect(store.inventory).toHaveLength(0)
|
|
})
|
|
})
|
|
|
|
describe('updateInventory', () => {
|
|
beforeEach(async () => {
|
|
await store.fetchInventory()
|
|
})
|
|
|
|
it('updates inventory quantities', async () => {
|
|
const updates = { qtyOnHand: 1200, qtyReserved: 150 }
|
|
const updated = await store.updateInventory('INV-001', updates)
|
|
|
|
expect(updated.qtyOnHand).toBe(1200)
|
|
expect(updated.qtyReserved).toBe(150)
|
|
expect(updated.qtyAvailable).toBe(1050)
|
|
})
|
|
|
|
it('reflects updates in store', async () => {
|
|
const updates = { qtyOnHand: 800, qtyReserved: 100 }
|
|
await store.updateInventory('INV-001', updates)
|
|
|
|
const inv = store.inventory.find((i) => i.inventoryId === 'INV-001')
|
|
expect(inv?.qtyOnHand).toBe(800)
|
|
})
|
|
|
|
it('handles update validation errors', async () => {
|
|
server.use(
|
|
http.patch('*/api/inventory/:inventoryId', () => {
|
|
return HttpResponse.json(
|
|
{ message: 'Quantity cannot be negative' },
|
|
{ status: 400 }
|
|
)
|
|
})
|
|
)
|
|
|
|
await expect(
|
|
store.updateInventory('INV-001', { qtyOnHand: -100 })
|
|
).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe('stock availability checks', () => {
|
|
beforeEach(async () => {
|
|
await store.fetchInventory()
|
|
})
|
|
|
|
it('reserves stock correctly', async () => {
|
|
const initial = store.inventory[0]
|
|
const reservation = 100
|
|
|
|
const updated = await store.updateInventory('INV-001', {
|
|
qtyOnHand: initial.qtyOnHand,
|
|
qtyReserved: initial.qtyReserved + reservation
|
|
})
|
|
|
|
expect(updated.qtyAvailable).toBe(initial.qtyAvailable - reservation)
|
|
})
|
|
|
|
it('prevents over-reservation', async () => {
|
|
const current = store.inventory[0]
|
|
// Try to reserve more than available
|
|
server.use(
|
|
http.patch('*/api/inventory/:inventoryId', () => {
|
|
return HttpResponse.json(
|
|
{ message: 'Cannot reserve more than available quantity' },
|
|
{ status: 422 }
|
|
)
|
|
})
|
|
)
|
|
|
|
await expect(
|
|
store.updateInventory('INV-001', {
|
|
qtyOnHand: current.qtyOnHand,
|
|
qtyReserved: 10000 // Exceed available
|
|
})
|
|
).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe('multi-warehouse scenarios', () => {
|
|
it('handles inventory across multiple warehouses', async () => {
|
|
server.use(
|
|
http.get('*/api/inventory', () => {
|
|
return HttpResponse.json({
|
|
data: [
|
|
{ inventoryId: 'INV-001', warehouseId: 'WH-SEOUL', qtyOnHand: 1000 },
|
|
{ inventoryId: 'INV-002', warehouseId: 'WH-BUSAN', qtyOnHand: 500 },
|
|
{ inventoryId: 'INV-003', warehouseId: 'WH-DAEGU', qtyOnHand: 300 }
|
|
]
|
|
})
|
|
})
|
|
)
|
|
|
|
const tempStore = new MockInventoryStore()
|
|
await tempStore.fetchInventory()
|
|
|
|
expect(tempStore.inventory).toHaveLength(3)
|
|
const totalQty = tempStore.inventory.reduce((sum, inv) => sum + inv.qtyOnHand, 0)
|
|
expect(totalQty).toBe(1800)
|
|
})
|
|
})
|
|
})
|