Mint ChannelDetailResponseModel (faithful detail DTO exposing the raw editable field set the channel editor reads: raw FFmpegProfileId/WatermarkId/FallbackFillerId ids, the mode enums, logo, playoutCount, id) and route GetById/Create/Update through it, replacing the lean list ChannelResponseModel that resolved the profile to a name and dropped the editable ids (a functional regression for draftFromChannel). The lean ChannelResponseModel stays unchanged for GET /api/channels. webEncodedName dropped (SPA never reads it). Logo is mirrored as a Core ChannelLogoResponseModel since the Application ArtworkContentTypeModel can't be referenced from Core. Repoint the hand-written SPA client aliases now that the VMs are gone from the schema: Channel -> ChannelDetailResponseModel, MediaCollection/SmartCollection -> *ResponseModel, ProgramSchedule -> ProgramScheduleResponseModel. Fix #288 honest-nullability test fallout in search.test.ts (null -> [] for now-non-null id arrays). Include the already-on-disk playouts.ts WithDayNames removal and regenerate v1.json + v1.d.ts + endpoint-index.md (authoritative final regen; the reset endpoint's {channelNumber}->{id} re-key surfaces in the generated docs and the OpenApi error-contract test). Refs #288 #197 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { getSearchAllItems, getSearchResults, toAddItemsRequestFromSearch } from './search';
|
|
|
|
const emptyGroup = { totalCount: 0, items: [] };
|
|
const sampleResults = {
|
|
movies: emptyGroup,
|
|
shows: emptyGroup,
|
|
seasons: emptyGroup,
|
|
artists: emptyGroup,
|
|
episodes: emptyGroup,
|
|
musicVideos: emptyGroup,
|
|
songs: emptyGroup,
|
|
otherVideos: emptyGroup,
|
|
images: emptyGroup,
|
|
remoteStreams: emptyGroup
|
|
};
|
|
|
|
describe('getSearchResults', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('GETs /api/search with the query', async () => {
|
|
const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify(sampleResults), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 200
|
|
})
|
|
);
|
|
|
|
await getSearchResults({ query: 'star' });
|
|
|
|
const [url, init] = fetchSpy.mock.calls[0];
|
|
expect(url).toBe('/api/search?query=star');
|
|
expect((init?.method ?? 'GET').toUpperCase()).toBe('GET');
|
|
});
|
|
|
|
it('includes pageSize when provided', async () => {
|
|
const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify(sampleResults), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 200
|
|
})
|
|
);
|
|
|
|
await getSearchResults({ query: 'star wars', pageSize: 50 });
|
|
|
|
const [url] = fetchSpy.mock.calls[0];
|
|
expect(url).toBe('/api/search?query=star+wars&pageSize=50');
|
|
});
|
|
|
|
it('rejects with the ApiError status on failure', async () => {
|
|
vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify({ status: 422, title: 'Validation failed' }), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 422
|
|
})
|
|
);
|
|
|
|
await expect(getSearchResults({ query: '' })).rejects.toMatchObject({ status: 422 });
|
|
});
|
|
});
|
|
|
|
describe('getSearchAllItems', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('GETs /api/search/all-items with the query', async () => {
|
|
const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify({ movieIds: [1], showIds: [2] }), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 200
|
|
})
|
|
);
|
|
|
|
await getSearchAllItems('star wars');
|
|
|
|
const [url, init] = fetchSpy.mock.calls[0];
|
|
expect(url).toBe('/api/search/all-items?query=star+wars');
|
|
expect((init?.method ?? 'GET').toUpperCase()).toBe('GET');
|
|
});
|
|
});
|
|
|
|
describe('toAddItemsRequestFromSearch', () => {
|
|
it('fills every bucket, defaulting null arrays to []', () => {
|
|
expect(
|
|
toAddItemsRequestFromSearch({
|
|
artistIds: [],
|
|
episodeIds: [],
|
|
imageIds: [],
|
|
movieIds: [1, 2],
|
|
musicVideoIds: [],
|
|
otherVideoIds: [],
|
|
remoteStreamIds: [],
|
|
seasonIds: [],
|
|
showIds: [],
|
|
songIds: []
|
|
})
|
|
).toEqual({
|
|
artistIds: [],
|
|
episodeIds: [],
|
|
imageIds: [],
|
|
movieIds: [1, 2],
|
|
musicVideoIds: [],
|
|
otherVideoIds: [],
|
|
remoteStreamIds: [],
|
|
seasonIds: [],
|
|
showIds: [],
|
|
songIds: []
|
|
});
|
|
});
|
|
});
|