Files
Psychotoxical-psysonic/src/api/subsonicScrobble.test.ts
T
cucadmuh 707a41f615 fix(scrobble): Navidrome Now Playing with local playback and mixed-server queue (#1055)
* fix(scrobble): report Now Playing on playback server with local bytes

Navidrome presence and play-count scrobbles no longer skip when audio plays
from hot cache, offline pins, or favorites-auto, and reachability follows the
queue/playback server instead of the browsed active server.

* docs: changelog and credits for PR #1055
2026-06-10 04:25:22 +03:00

72 lines
2.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useAuthStore } from '../store/authStore';
import { usePlayerStore } from '../store/playerStore';
import { reportNowPlaying, scrobbleSong } from './subsonicScrobble';
import { shouldAttemptSubsonicForServer } from '../utils/network/subsonicNetworkGuard';
const { apiForServerMock } = vi.hoisted(() => ({
apiForServerMock: vi.fn(async () => ({})),
}));
vi.mock('./subsonicClient', () => ({
api: vi.fn(),
apiForServer: apiForServerMock,
}));
vi.mock('../utils/network/subsonicNetworkGuard', () => ({
shouldAttemptSubsonicForServer: vi.fn(() => true),
}));
describe('subsonicScrobble', () => {
beforeEach(() => {
apiForServerMock.mockClear();
vi.mocked(shouldAttemptSubsonicForServer).mockImplementation(() => true);
useAuthStore.setState({
servers: [
{ id: 'a', name: 'A', url: 'http://a.test', username: 'u', password: 'p' },
{ id: 'b', name: 'B', url: 'http://b.test', username: 'u', password: 'p' },
],
activeServerId: 'b',
isLoggedIn: true,
});
usePlayerStore.setState({
queueItems: [{ serverId: 'a', trackId: 't1' }],
queueServerId: 'a',
queueIndex: 0,
});
});
it('scrobbleSong targets the queue server when active server differs', async () => {
await scrobbleSong('t1', 1_700_000_000_000, 'a');
expect(apiForServerMock).toHaveBeenCalledWith(
'a',
'scrobble.view',
expect.objectContaining({ id: 't1', submission: true, time: 1_700_000_000_000 }),
);
});
it('reportNowPlaying and scrobbleSong use the presence guard without trackId', async () => {
vi.mocked(shouldAttemptSubsonicForServer).mockImplementation(
(_serverId: string, trackId?: string) => trackId === undefined,
);
await reportNowPlaying('t-local', 'a');
await scrobbleSong('t-local', 1_700_000_000_000, 'a');
expect(shouldAttemptSubsonicForServer).toHaveBeenCalledWith('a');
expect(shouldAttemptSubsonicForServer).not.toHaveBeenCalledWith('a', expect.anything());
expect(apiForServerMock).toHaveBeenCalledTimes(2);
expect(apiForServerMock).toHaveBeenNthCalledWith(
1,
'a',
'scrobble.view',
expect.objectContaining({ id: 't-local', submission: false }),
);
expect(apiForServerMock).toHaveBeenNthCalledWith(
2,
'a',
'scrobble.view',
expect.objectContaining({ id: 't-local', submission: true }),
);
});
});