238 lines
8.4 KiB
TypeScript
238 lines
8.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
import { useConstructeurLinks } from '~/composables/useConstructeurLinks'
|
|
import {
|
|
mockLinkSKF,
|
|
mockLinkFAG,
|
|
mockConstructeurSKF,
|
|
mockConstructeurFAG,
|
|
wrapCollection,
|
|
} from '../fixtures/mockData'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Mocks
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const mockGet = vi.fn()
|
|
const mockPost = vi.fn()
|
|
const mockPatch = vi.fn()
|
|
const mockDel = vi.fn()
|
|
|
|
vi.mock('~/composables/useApi', () => ({
|
|
useApi: () => ({
|
|
get: mockGet,
|
|
post: mockPost,
|
|
patch: mockPatch,
|
|
put: vi.fn(),
|
|
delete: mockDel,
|
|
postFormData: vi.fn(),
|
|
}),
|
|
}))
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// fetchLinks
|
|
// ---------------------------------------------------------------------------
|
|
describe('fetchLinks', () => {
|
|
it('returns parsed links with all properties for composant', async () => {
|
|
const apiLinks = [
|
|
{
|
|
id: mockLinkSKF.linkId,
|
|
constructeur: mockConstructeurSKF,
|
|
supplierReference: mockLinkSKF.supplierReference,
|
|
},
|
|
{
|
|
id: mockLinkFAG.linkId,
|
|
constructeur: mockConstructeurFAG,
|
|
supplierReference: mockLinkFAG.supplierReference,
|
|
},
|
|
]
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection(apiLinks) })
|
|
|
|
const { fetchLinks } = useConstructeurLinks()
|
|
const result = await fetchLinks('composant', 'comp-001')
|
|
|
|
expect(result).toHaveLength(2)
|
|
expect(result[0]).toEqual({
|
|
linkId: mockLinkSKF.linkId,
|
|
constructeurId: mockConstructeurSKF.id,
|
|
constructeur: mockConstructeurSKF,
|
|
supplierReference: mockLinkSKF.supplierReference,
|
|
})
|
|
expect(result[1]).toEqual({
|
|
linkId: mockLinkFAG.linkId,
|
|
constructeurId: mockConstructeurFAG.id,
|
|
constructeur: mockConstructeurFAG,
|
|
supplierReference: mockLinkFAG.supplierReference,
|
|
})
|
|
})
|
|
|
|
it('returns supplierReference as null when absent from API', async () => {
|
|
const apiLinks = [
|
|
{
|
|
id: 'link-no-ref',
|
|
constructeur: mockConstructeurSKF,
|
|
// no supplierReference key
|
|
},
|
|
]
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection(apiLinks) })
|
|
|
|
const { fetchLinks } = useConstructeurLinks()
|
|
const result = await fetchLinks('composant', 'comp-001')
|
|
|
|
expect(result).toHaveLength(1)
|
|
expect(result[0]!.supplierReference).toBeNull()
|
|
})
|
|
|
|
it.each([
|
|
['machine', '/machine_constructeur_links?machine=/api/machines/m-001', 'm-001'],
|
|
['product', '/product_constructeur_links?product=/api/products/p-001', 'p-001'],
|
|
['piece', '/piece_constructeur_links?piece=/api/pieces/pc-001', 'pc-001'],
|
|
['composant', '/composant_constructeur_links?composant=/api/composants/c-001', 'c-001'],
|
|
] as const)('uses correct endpoint for %s', async (entityType, expectedUrl, entityId) => {
|
|
mockGet.mockResolvedValue({ success: true, data: wrapCollection([]) })
|
|
|
|
const { fetchLinks } = useConstructeurLinks()
|
|
await fetchLinks(entityType, entityId)
|
|
|
|
expect(mockGet).toHaveBeenCalledWith(expectedUrl)
|
|
})
|
|
|
|
it('returns empty array on API failure', async () => {
|
|
mockGet.mockResolvedValue({ success: false, data: null })
|
|
|
|
const { fetchLinks } = useConstructeurLinks()
|
|
const result = await fetchLinks('composant', 'comp-001')
|
|
|
|
expect(result).toEqual([])
|
|
})
|
|
})
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// syncLinks — 3-way diff
|
|
// ---------------------------------------------------------------------------
|
|
describe('syncLinks', () => {
|
|
it('creates new links via POST', async () => {
|
|
mockPost.mockResolvedValue({ success: true })
|
|
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [], [mockLinkSKF])
|
|
|
|
expect(mockPost).toHaveBeenCalledWith('/composant_constructeur_links', {
|
|
composant: '/api/composants/comp-001',
|
|
constructeur: `/api/constructeurs/${mockConstructeurSKF.id}`,
|
|
supplierReference: mockLinkSKF.supplierReference,
|
|
})
|
|
expect(mockDel).not.toHaveBeenCalled()
|
|
expect(mockPatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('deletes removed links via DELETE', async () => {
|
|
mockDel.mockResolvedValue({ success: true })
|
|
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [mockLinkSKF], [])
|
|
|
|
expect(mockDel).toHaveBeenCalledWith(`/composant_constructeur_links/${mockLinkSKF.linkId}`)
|
|
expect(mockPost).not.toHaveBeenCalled()
|
|
expect(mockPatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('patches when supplierReference changes (value to new value)', async () => {
|
|
mockPatch.mockResolvedValue({ success: true })
|
|
|
|
const updatedLink = { ...mockLinkSKF, supplierReference: 'NEW-REF-999' }
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [mockLinkSKF], [updatedLink])
|
|
|
|
expect(mockPatch).toHaveBeenCalledWith(`/composant_constructeur_links/${mockLinkSKF.linkId}`, {
|
|
supplierReference: 'NEW-REF-999',
|
|
})
|
|
expect(mockPost).not.toHaveBeenCalled()
|
|
expect(mockDel).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('patches when supplierReference changes from value to null', async () => {
|
|
mockPatch.mockResolvedValue({ success: true })
|
|
|
|
const updatedLink = { ...mockLinkSKF, supplierReference: null }
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [mockLinkSKF], [updatedLink])
|
|
|
|
expect(mockPatch).toHaveBeenCalledWith(`/composant_constructeur_links/${mockLinkSKF.linkId}`, {
|
|
supplierReference: null,
|
|
})
|
|
})
|
|
|
|
it('does nothing when links are identical (no API calls)', async () => {
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [mockLinkSKF], [mockLinkSKF])
|
|
|
|
expect(mockPost).not.toHaveBeenCalled()
|
|
expect(mockDel).not.toHaveBeenCalled()
|
|
expect(mockPatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('handles add + delete in same operation', async () => {
|
|
mockPost.mockResolvedValue({ success: true })
|
|
mockDel.mockResolvedValue({ success: true })
|
|
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [mockLinkSKF], [mockLinkFAG])
|
|
|
|
expect(mockDel).toHaveBeenCalledWith(`/composant_constructeur_links/${mockLinkSKF.linkId}`)
|
|
expect(mockPost).toHaveBeenCalledWith('/composant_constructeur_links', {
|
|
composant: '/api/composants/comp-001',
|
|
constructeur: `/api/constructeurs/${mockConstructeurFAG.id}`,
|
|
supplierReference: mockLinkFAG.supplierReference,
|
|
})
|
|
expect(mockPatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('handles empty original and empty form (no-op)', async () => {
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [], [])
|
|
|
|
expect(mockPost).not.toHaveBeenCalled()
|
|
expect(mockDel).not.toHaveBeenCalled()
|
|
expect(mockPatch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('sends supplierReference as null when empty string', async () => {
|
|
mockPost.mockResolvedValue({ success: true })
|
|
|
|
const linkWithEmpty = { ...mockLinkFAG, supplierReference: '' }
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks('composant', 'comp-001', [], [linkWithEmpty])
|
|
|
|
expect(mockPost).toHaveBeenCalledWith('/composant_constructeur_links', {
|
|
composant: '/api/composants/comp-001',
|
|
constructeur: `/api/constructeurs/${mockConstructeurFAG.id}`,
|
|
supplierReference: null,
|
|
})
|
|
})
|
|
|
|
it.each([
|
|
['machine', '/machine_constructeur_links', 'machine', '/api/machines/m-001', 'm-001'],
|
|
['product', '/product_constructeur_links', 'product', '/api/products/p-001', 'p-001'],
|
|
['piece', '/piece_constructeur_links', 'piece', '/api/pieces/pc-001', 'pc-001'],
|
|
['composant', '/composant_constructeur_links', 'composant', '/api/composants/c-001', 'c-001'],
|
|
] as const)('uses correct endpoint and entity IRI for %s', async (entityType, endpoint, key, entityIri, entityId) => {
|
|
mockPost.mockResolvedValue({ success: true })
|
|
mockDel.mockResolvedValue({ success: true })
|
|
|
|
const { syncLinks } = useConstructeurLinks()
|
|
await syncLinks(entityType, entityId, [mockLinkSKF], [mockLinkFAG])
|
|
|
|
expect(mockDel).toHaveBeenCalledWith(`${endpoint}/${mockLinkSKF.linkId}`)
|
|
expect(mockPost).toHaveBeenCalledWith(endpoint, {
|
|
[key]: entityIri,
|
|
constructeur: `/api/constructeurs/${mockConstructeurFAG.id}`,
|
|
supplierReference: mockLinkFAG.supplierReference,
|
|
})
|
|
})
|
|
})
|