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
298 lines
8.0 KiB
TypeScript
298 lines
8.0 KiB
TypeScript
/**
|
|
* Integration Tests: Products (ERP) Store
|
|
* Tests product catalog management with mocked API
|
|
*
|
|
* Run: npm run test:integration
|
|
*/
|
|
|
|
import { beforeEach, describe, expect, it } from 'vitest'
|
|
import { server } from '@/tests/mocks/server'
|
|
import { http, HttpResponse } from 'msw'
|
|
|
|
/**
|
|
* Mock Products Store (placeholder until actual store is created)
|
|
*/
|
|
class MockProductsStore {
|
|
products: any[] = []
|
|
loading = false
|
|
error: string | null = null
|
|
filters: Record<string, any> = {}
|
|
|
|
async fetchProducts() {
|
|
this.loading = true
|
|
try {
|
|
const response = await fetch(
|
|
`${process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'}/products`
|
|
)
|
|
const data = await response.json()
|
|
this.products = data.data || []
|
|
} catch (err) {
|
|
this.error = (err as Error).message
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
}
|
|
|
|
async createProduct(payload: any) {
|
|
this.loading = true
|
|
try {
|
|
const response = await fetch(
|
|
`${process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'}/products`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload)
|
|
}
|
|
)
|
|
if (!response.ok) throw new Error('Creation failed')
|
|
const data = await response.json()
|
|
this.products.push(data)
|
|
return data
|
|
} catch (err) {
|
|
this.error = (err as Error).message
|
|
throw err
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
}
|
|
|
|
async getProduct(productId: string) {
|
|
this.loading = true
|
|
try {
|
|
const response = await fetch(
|
|
`${process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'}/products/${productId}`
|
|
)
|
|
if (!response.ok) throw new Error('Not found')
|
|
const data = await response.json()
|
|
return data
|
|
} catch (err) {
|
|
this.error = (err as Error).message
|
|
throw err
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
}
|
|
|
|
setFilter(key: string, value: any) {
|
|
this.filters[key] = value
|
|
}
|
|
|
|
clearFilters() {
|
|
this.filters = {}
|
|
}
|
|
}
|
|
|
|
describe('Products (ERP) Store Integration', () => {
|
|
let store: MockProductsStore
|
|
|
|
beforeEach(() => {
|
|
store = new MockProductsStore()
|
|
})
|
|
|
|
describe('fetchProducts', () => {
|
|
it('loads products from ERP API', async () => {
|
|
await store.fetchProducts()
|
|
|
|
expect(store.products).toHaveLength(1)
|
|
expect(store.products[0].productId).toBe('PROD-001')
|
|
expect(store.products[0].productName).toBe('Product A')
|
|
expect(store.loading).toBe(false)
|
|
})
|
|
|
|
it('includes product metadata', async () => {
|
|
await store.fetchProducts()
|
|
|
|
const product = store.products[0]
|
|
expect(product).toHaveProperty('sku')
|
|
expect(product).toHaveProperty('categoryId')
|
|
expect(product).toHaveProperty('status')
|
|
expect(product).toHaveProperty('createdAt')
|
|
})
|
|
|
|
it('handles empty product list', async () => {
|
|
server.use(
|
|
http.get('*/api/products', () => {
|
|
return HttpResponse.json({ data: [] })
|
|
})
|
|
)
|
|
|
|
await store.fetchProducts()
|
|
|
|
expect(store.products).toHaveLength(0)
|
|
expect(store.error).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('createProduct', () => {
|
|
it('creates new product with required fields', async () => {
|
|
const payload = {
|
|
sku: 'SKU-NEW',
|
|
productName: 'New Product',
|
|
categoryId: 'CAT-002'
|
|
}
|
|
|
|
const created = await store.createProduct(payload)
|
|
|
|
expect(created.productId).toBe('PROD-NEW')
|
|
expect(created.sku).toBe('SKU-NEW')
|
|
expect(created.productName).toBe('New Product')
|
|
expect(created.status).toBe('ACTIVE')
|
|
})
|
|
|
|
it('adds created product to store', async () => {
|
|
const initialCount = store.products.length
|
|
|
|
await store.createProduct({
|
|
sku: 'SKU-NEW-2',
|
|
productName: 'Another Product',
|
|
categoryId: 'CAT-003'
|
|
})
|
|
|
|
expect(store.products.length).toBeGreaterThan(initialCount)
|
|
})
|
|
|
|
it('validates required fields', async () => {
|
|
server.use(
|
|
http.post('*/api/products', () => {
|
|
return HttpResponse.json(
|
|
{ message: 'Product name is required' },
|
|
{ status: 400 }
|
|
)
|
|
})
|
|
)
|
|
|
|
await expect(
|
|
store.createProduct({ sku: 'SKU-INVALID' })
|
|
).rejects.toThrow()
|
|
})
|
|
|
|
it('ensures unique SKU', async () => {
|
|
server.use(
|
|
http.post('*/api/products', () => {
|
|
return HttpResponse.json(
|
|
{ message: 'SKU already exists' },
|
|
{ status: 409 }
|
|
)
|
|
})
|
|
)
|
|
|
|
await expect(
|
|
store.createProduct({ sku: 'SKU-001', productName: 'Duplicate' })
|
|
).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
describe('getProduct', () => {
|
|
it('fetches single product by ID', async () => {
|
|
const product = await store.getProduct('PROD-001')
|
|
|
|
expect(product.productId).toBe('PROD-001')
|
|
expect(product.productName).toBe('Product Sample')
|
|
})
|
|
|
|
it('handles product not found', async () => {
|
|
server.use(
|
|
http.get('*/api/products/:productId', () => {
|
|
return HttpResponse.json(
|
|
{ message: 'Product not found' },
|
|
{ status: 404 }
|
|
)
|
|
})
|
|
)
|
|
|
|
await expect(store.getProduct('PROD-INVALID')).rejects.toThrow()
|
|
expect(store.error).not.toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('product filtering', () => {
|
|
beforeEach(async () => {
|
|
server.use(
|
|
http.get('*/api/products', () => {
|
|
return HttpResponse.json({
|
|
data: [
|
|
{ productId: 'PROD-001', categoryId: 'CAT-001', status: 'ACTIVE' },
|
|
{ productId: 'PROD-002', categoryId: 'CAT-001', status: 'ACTIVE' },
|
|
{ productId: 'PROD-003', categoryId: 'CAT-002', status: 'INACTIVE' },
|
|
{ productId: 'PROD-004', categoryId: 'CAT-002', status: 'ACTIVE' }
|
|
]
|
|
})
|
|
})
|
|
)
|
|
await store.fetchProducts()
|
|
})
|
|
|
|
it('filters by category', () => {
|
|
store.setFilter('categoryId', 'CAT-001')
|
|
|
|
const filtered = store.products.filter((p) => p.categoryId === store.filters.categoryId)
|
|
expect(filtered).toHaveLength(2)
|
|
})
|
|
|
|
it('filters by status', () => {
|
|
store.setFilter('status', 'ACTIVE')
|
|
|
|
const filtered = store.products.filter((p) => p.status === store.filters.status)
|
|
expect(filtered.length).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('combines multiple filters', () => {
|
|
store.setFilter('categoryId', 'CAT-001')
|
|
store.setFilter('status', 'ACTIVE')
|
|
|
|
const filtered = store.products.filter(
|
|
(p) =>
|
|
p.categoryId === store.filters.categoryId &&
|
|
p.status === store.filters.status
|
|
)
|
|
expect(filtered).toHaveLength(2)
|
|
})
|
|
|
|
it('clears all filters', () => {
|
|
store.setFilter('categoryId', 'CAT-001')
|
|
store.setFilter('status', 'ACTIVE')
|
|
store.clearFilters()
|
|
|
|
expect(Object.keys(store.filters)).toHaveLength(0)
|
|
})
|
|
})
|
|
|
|
describe('bulk operations', () => {
|
|
it('handles bulk product creation', async () => {
|
|
server.use(
|
|
http.post('*/api/products/bulk', async ({ request }) => {
|
|
const body = await request.json() as any
|
|
return HttpResponse.json(
|
|
{
|
|
created: body.items.length,
|
|
items: body.items.map((item: any, idx: number) => ({
|
|
productId: `PROD-BULK-${idx + 1}`,
|
|
...item
|
|
}))
|
|
},
|
|
{ status: 201 }
|
|
)
|
|
})
|
|
)
|
|
|
|
const items = [
|
|
{ sku: 'SKU-BULK-1', productName: 'Bulk Product 1' },
|
|
{ sku: 'SKU-BULK-2', productName: 'Bulk Product 2' }
|
|
]
|
|
|
|
const response = await fetch(
|
|
`${process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'}/products/bulk`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ items })
|
|
}
|
|
)
|
|
|
|
const data = await response.json()
|
|
expect(data.created).toBe(2)
|
|
expect(data.items).toHaveLength(2)
|
|
})
|
|
})
|
|
})
|