mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 15:10:06 +00:00
397 lines
11 KiB
TypeScript
397 lines
11 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import { api } from '@/scripts/api'
|
|
import {
|
|
type FileNameMapping,
|
|
FileNameMappingService
|
|
} from '@/services/fileNameMappingService'
|
|
|
|
// Mock api module
|
|
vi.mock('@/scripts/api', () => ({
|
|
api: {
|
|
fetchApi: vi.fn()
|
|
}
|
|
}))
|
|
|
|
describe('FileNameMappingService', () => {
|
|
let service: FileNameMappingService
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
// Create a new instance for each test to avoid cache pollution
|
|
service = new FileNameMappingService()
|
|
})
|
|
|
|
describe('getMapping', () => {
|
|
it('should fetch mappings from API', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation_photo.png',
|
|
'def456.jpg': 'profile_picture.jpg'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
const result = await service.getMapping('input')
|
|
|
|
expect(api.fetchApi).toHaveBeenCalledWith('/files/mappings')
|
|
expect(result).toEqual(mockData)
|
|
})
|
|
|
|
it('should cache mappings and not refetch within TTL', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation_photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
// First call
|
|
await service.getMapping('input')
|
|
expect(api.fetchApi).toHaveBeenCalledTimes(1)
|
|
|
|
// Second call should use cache
|
|
const result = await service.getMapping('input')
|
|
expect(api.fetchApi).toHaveBeenCalledTimes(1)
|
|
expect(result).toEqual(mockData)
|
|
})
|
|
|
|
it('should return empty object on API failure', async () => {
|
|
vi.mocked(api.fetchApi).mockRejectedValue(new Error('Network error'))
|
|
|
|
const result = await service.getMapping('input')
|
|
|
|
expect(result).toEqual({})
|
|
})
|
|
|
|
it('should return empty object on non-200 response', async () => {
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: false,
|
|
status: 404,
|
|
statusText: 'Not Found'
|
|
} as any)
|
|
|
|
const result = await service.getMapping('input')
|
|
|
|
expect(result).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe('getCachedMapping', () => {
|
|
it('should return empty object if no cached data', () => {
|
|
const result = service.getCachedMapping('input')
|
|
expect(result).toEqual({})
|
|
})
|
|
|
|
it('should return cached data after successful fetch', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation_photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
await service.getMapping('input')
|
|
|
|
const result = service.getCachedMapping('input')
|
|
expect(result).toEqual(mockData)
|
|
})
|
|
})
|
|
|
|
describe('getCachedReverseMapping', () => {
|
|
it('should return reverse mapping (human -> hash)', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation_photo.png',
|
|
'def456.jpg': 'profile_picture.jpg'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
await service.getMapping('input')
|
|
|
|
const reverseMapping = service.getCachedReverseMapping('input')
|
|
expect(reverseMapping).toEqual({
|
|
'vacation_photo.png': 'abc123.png',
|
|
'profile_picture.jpg': 'def456.jpg'
|
|
})
|
|
})
|
|
|
|
it('should return empty object if no cached data', () => {
|
|
const result = service.getCachedReverseMapping('input')
|
|
expect(result).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe('getHashFromHumanName', () => {
|
|
it('should convert human name to hash', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation_photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
await service.getMapping('input')
|
|
|
|
const hash = service.getHashFromHumanName('vacation_photo.png', 'input')
|
|
expect(hash).toBe('abc123.png')
|
|
})
|
|
|
|
it('should return original name if no mapping exists', async () => {
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({})
|
|
} as any)
|
|
|
|
await service.getMapping('input')
|
|
|
|
const result = service.getHashFromHumanName('unknown.png', 'input')
|
|
expect(result).toBe('unknown.png')
|
|
})
|
|
})
|
|
|
|
describe('getHumanReadableName', () => {
|
|
it('should convert hash to human-readable name', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation_photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
const humanName = await service.getHumanReadableName(
|
|
'abc123.png',
|
|
'input'
|
|
)
|
|
expect(humanName).toBe('vacation_photo.png')
|
|
})
|
|
|
|
it('should return hash if no mapping exists', async () => {
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({})
|
|
} as any)
|
|
|
|
const result = await service.getHumanReadableName('xyz789.png', 'input')
|
|
expect(result).toBe('xyz789.png')
|
|
})
|
|
})
|
|
|
|
describe('refreshMapping', () => {
|
|
it('should invalidate cache and fetch fresh data', async () => {
|
|
const mockData1: FileNameMapping = {
|
|
'abc123.png': 'old_photo.png'
|
|
}
|
|
const mockData2: FileNameMapping = {
|
|
'def456.png': 'new_photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi)
|
|
.mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData1
|
|
} as any)
|
|
.mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData2
|
|
} as any)
|
|
|
|
// First fetch
|
|
await service.getMapping('input')
|
|
expect(service.getCachedMapping('input')).toEqual(mockData1)
|
|
|
|
// Refresh should fetch new data
|
|
const refreshedData = await service.refreshMapping('input')
|
|
expect(api.fetchApi).toHaveBeenCalledTimes(2)
|
|
expect(refreshedData).toEqual(mockData2)
|
|
expect(service.getCachedMapping('input')).toEqual(mockData2)
|
|
})
|
|
})
|
|
|
|
describe('invalidateCache', () => {
|
|
it('should clear cache for specific file type', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
await service.getMapping('input')
|
|
expect(service.getCachedMapping('input')).toEqual(mockData)
|
|
|
|
service.invalidateCache('input')
|
|
expect(service.getCachedMapping('input')).toEqual({})
|
|
})
|
|
|
|
it('should clear all caches when no type specified', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
await service.getMapping('input')
|
|
await service.getMapping('output')
|
|
|
|
service.invalidateCache()
|
|
|
|
expect(service.getCachedMapping('input')).toEqual({})
|
|
expect(service.getCachedMapping('output')).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe('ensureMappingsLoaded', () => {
|
|
it('should preload mappings for immediate synchronous access', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'photo.png'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
// Ensure mappings are loaded
|
|
await service.ensureMappingsLoaded('input')
|
|
|
|
// Should be available synchronously
|
|
const cached = service.getCachedMapping('input')
|
|
expect(cached).toEqual(mockData)
|
|
})
|
|
|
|
it('should not throw on API failure', async () => {
|
|
vi.mocked(api.fetchApi).mockRejectedValue(new Error('Network error'))
|
|
|
|
// Should not throw
|
|
await expect(service.ensureMappingsLoaded('input')).resolves.not.toThrow()
|
|
|
|
// Should have empty mapping
|
|
expect(service.getCachedMapping('input')).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe('applyMappingToArray', () => {
|
|
it('should apply mapping to array of filenames', async () => {
|
|
const mockData: FileNameMapping = {
|
|
'abc123.png': 'vacation.png',
|
|
'def456.jpg': 'profile.jpg'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
const result = await service.applyMappingToArray(
|
|
['abc123.png', 'def456.jpg', 'unknown.gif'],
|
|
'input'
|
|
)
|
|
|
|
expect(result).toEqual(['vacation.png', 'profile.jpg', 'unknown.gif'])
|
|
})
|
|
|
|
it('should return original array on API failure', async () => {
|
|
vi.mocked(api.fetchApi).mockRejectedValue(new Error('Network error'))
|
|
|
|
const input = ['abc123.png', 'def456.jpg']
|
|
const result = await service.applyMappingToArray(input, 'input')
|
|
|
|
expect(result).toEqual(input)
|
|
})
|
|
})
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle invalid JSON response gracefully', async () => {
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => {
|
|
throw new Error('Invalid JSON')
|
|
}
|
|
} as any)
|
|
|
|
const result = await service.getMapping('input')
|
|
expect(result).toEqual({})
|
|
})
|
|
|
|
it('should filter out invalid entries from response', async () => {
|
|
const mockData = {
|
|
'valid.png': 'photo.png',
|
|
invalid: 123, // Invalid value type - will be filtered
|
|
123: 'number_key', // Numeric key becomes string "123" in JS
|
|
'another_valid.jpg': 'image.jpg'
|
|
}
|
|
|
|
vi.mocked(api.fetchApi).mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => mockData
|
|
} as any)
|
|
|
|
const result = await service.getMapping('input')
|
|
|
|
// Should filter out non-string values but keep string keys (including coerced numeric keys)
|
|
expect(result).toEqual({
|
|
'valid.png': 'photo.png',
|
|
'123': 'number_key', // Numeric key becomes string
|
|
'another_valid.jpg': 'image.jpg'
|
|
})
|
|
})
|
|
|
|
it('should handle null or array responses', async () => {
|
|
// Test null response
|
|
vi.mocked(api.fetchApi).mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => null
|
|
} as any)
|
|
|
|
let result = await service.getMapping('input')
|
|
expect(result).toEqual({})
|
|
|
|
// Test array response
|
|
vi.mocked(api.fetchApi).mockResolvedValueOnce({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => []
|
|
} as any)
|
|
|
|
result = await service.getMapping('output')
|
|
expect(result).toEqual({})
|
|
})
|
|
})
|
|
})
|