// 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', 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', 12_345_678, false); assert.equal(capturedPath, '/Users/u-alice/PlayingItems/item1/Progress'); assert.equal(capturedToken, 'tok-alice'); assert.deepEqual(capturedOptions, { method: 'POST', body: { PositionTicks: 12345678, IsPaused: false } }); }); it('rounds a fractional positionTicks (Emby expects an integer)', async (t) => { let capturedOptions: { body?: { PositionTicks?: number } } | undefined; t.mock.method(EmbyClient, 'fetch', async (_path: string, _token: string, options: typeof capturedOptions) => { capturedOptions = options; return new Response('{}', { status: 200 }); }); await PlaystateService.reportProgress('tok', 'u1', 'item1', 100.7, true); assert.equal(capturedOptions?.body?.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', 0, false)); }); });