// PlaystateService.reportProgress - mocks EmbyClient.fetch (not the // global fetch it wraps), same isolation style as catalogService.test.ts, // since this is about what PlaystateService itself sends, not // EmbyClient's own plumbing (see embyClient.test.ts for that). import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { PlaystateService } from '../src/services/PlaystateService'; import { EmbyClient } from '../src/emby/EmbyClient'; describe('PlaystateService.reportProgress', () => { it('POSTs to this user\'s own PlayingItems/Progress with their own token, params in the query string not a body', async (t) => { let capturedPath = ''; let capturedToken = ''; let capturedOptions: unknown; t.mock.method(EmbyClient, 'fetch', async (path: string, token: string, options: unknown) => { capturedPath = path; capturedToken = token; capturedOptions = options; return new Response('{}', { status: 200 }); }); await PlaystateService.reportProgress('tok-alice', 'u-alice', 'item1', 'src1', 12_345_678, false); // Query params, not a JSON body - confirmed against Emby's real // OpenAPI spec (see PlaystateService.ts's own comment): the // original body-based guess was missing a required MediaSourceId // entirely and would have failed outright. const [path, query] = capturedPath.split('?'); assert.equal(path, '/Users/u-alice/PlayingItems/item1/Progress'); assert.equal(new URLSearchParams(query).get('MediaSourceId'), 'src1'); assert.equal(new URLSearchParams(query).get('PositionTicks'), '12345678'); assert.equal(new URLSearchParams(query).get('IsPaused'), 'false'); assert.equal(capturedToken, 'tok-alice'); assert.deepEqual(capturedOptions, { method: 'POST' }, 'no body at all - the real body schema (Api.OnPlaybackProgress) has none of these fields'); }); it('rounds a fractional positionTicks (Emby expects an integer)', async (t) => { let capturedPath = ''; t.mock.method(EmbyClient, 'fetch', async (path: string) => { capturedPath = path; return new Response('{}', { status: 200 }); }); await PlaystateService.reportProgress('tok', 'u1', 'item1', 'src1', 100.7, true); const query = capturedPath.split('?')[1]; assert.equal(new URLSearchParams(query).get('PositionTicks'), '101'); }); it('swallows a failure instead of throwing - this runs on a timer with nothing to catch it', async (t) => { t.mock.method(EmbyClient, 'fetch', async () => { throw new Error('Emby unreachable'); }); await assert.doesNotReject(() => PlaystateService.reportProgress('tok', 'u1', 'item1', 'src1', 0, false)); }); });