Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 7m11s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m49s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
POST /api/libraries/{id}/scan-show resolved the target show via
GetShowIdByTitle, an EF.Functions.Like "%title%" substring match with
no OrderBy - non-deterministic under duplicate/overlapping titles and
capable of scanning the wrong show. The Blazor UI never had this bug
(it always passed the exact show id); this endpoint shipped days ago
in PR #216 with no external consumers, so the contract break is safe.
BREAKING CHANGE: ScanShowRequest now takes `showId: int` instead of
`showTitle: string`. Replaced ITelevisionRepository.GetShowIdByTitle
with GetShowTitle(libraryId, showId), which also enforces the show
belongs to the given library. LibrariesController.ScanShow now returns
a genuine 404 ProblemDetails (via ApiResults.NotFoundProblem, the
established pre-check pattern from TemplateController.DeleteGroup)
when the show id doesn't exist in that library, then queues
QueueShowScanByLibraryId with the DB-resolved title.
SPA: libraries.ts ScanShowParams.showId replaces showTitle;
MediaDetailScreen.tsx passes show.id. Extended
ApiErrorResponseMetadataTests and OpenApiErrorResponseContractTests
with the new 404 contract for ScanShow. Regenerated v1.json / v1.d.ts
via scripts/update-openapi.sh + npm run generate:api.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
41 lines
1.6 KiB
TypeScript
41 lines
1.6 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { scanLibrary, scanShow } from './libraries';
|
|
|
|
function noContent(): Response {
|
|
return new Response(null, { status: 200 });
|
|
}
|
|
|
|
function lastCall(fetchMock: ReturnType<typeof vi.spyOn>) {
|
|
const call = fetchMock.mock.calls[fetchMock.mock.calls.length - 1];
|
|
return { init: call[1] as RequestInit | undefined, url: String(call[0]) };
|
|
}
|
|
|
|
describe('libraries api client', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('scanLibrary POSTs to the library scan endpoint', async () => {
|
|
const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(noContent());
|
|
await scanLibrary(4);
|
|
expect(fetchMock).toHaveBeenCalledWith('/api/libraries/4/scan', expect.objectContaining({ method: 'POST' }));
|
|
});
|
|
|
|
it('scanShow POSTs the show id and deepScan flag', async () => {
|
|
const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(noContent());
|
|
await scanShow(4, { deepScan: true, showId: 42 });
|
|
const { init, url } = lastCall(fetchMock);
|
|
expect(url).toBe('/api/libraries/4/scan-show');
|
|
expect(init?.method).toBe('POST');
|
|
expect(JSON.parse(String(init?.body))).toEqual({ deepScan: true, showId: 42 });
|
|
});
|
|
|
|
it('scanShow defaults deepScan to false when omitted', async () => {
|
|
const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(noContent());
|
|
await scanShow(9, { showId: 17 });
|
|
const { init } = lastCall(fetchMock);
|
|
expect(JSON.parse(String(init?.body))).toEqual({ deepScan: false, showId: 17 });
|
|
});
|
|
});
|