mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
refactor(playback): move thin-state queue resolver out of utils/library into the feature
The queue-resolver family (queueTrackResolver, queueRestore, queueItemRef, queueTrackView) runtime-imports usePlayerStore / playerStoreTypes — it is the playback engine's thin-state queue subsystem, not shared library infra. It sat in utils/library only because it queries the local index. Co-locate into features/playback/store so those edges become intra-feature; its remaining deps (advancedSearchLocal, libraryReady, serverLookup, serverIndexKey, authStore) are plain core/infra (feature -> infra, no inversion). Removes 4 of the 6 store/utils -> feature runtime inversions that blocked the utils/library -> lib move. 8 files moved, 46 consumers rewritten. tsc 0, lint 0, targeted suites green.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import type { QueueItemRef, Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { seedQueueResolver, _resetQueueResolverForTest } from '@/features/playback/store/queueTrackResolver';
|
||||
import { resolveQueueTrack, getQueueTracksView } from './queueTrackView';
|
||||
|
||||
const track = (id: string, over: Partial<Track> = {}): Track =>
|
||||
({ id, title: id, artist: 'A', album: 'Al', albumId: 'Al', duration: 1, ...over });
|
||||
const ref = (trackId: string, over: Partial<QueueItemRef> = {}): QueueItemRef =>
|
||||
({ serverId: 's1', trackId, ...over });
|
||||
|
||||
describe('queueTrackView', () => {
|
||||
beforeEach(() => {
|
||||
_resetQueueResolverForTest();
|
||||
usePlayerStore.setState({ starredOverrides: {}, userRatingOverrides: {} });
|
||||
});
|
||||
|
||||
it('resolves from the resolver cache when present', () => {
|
||||
seedQueueResolver('s1', [track('t1', { title: 'Cached' })]);
|
||||
expect(resolveQueueTrack(ref('t1')).title).toBe('Cached');
|
||||
});
|
||||
|
||||
it('falls back to the provided Track on cache miss', () => {
|
||||
expect(resolveQueueTrack(ref('t2'), track('t2', { title: 'Fallback' })).title).toBe('Fallback');
|
||||
});
|
||||
|
||||
it('returns a placeholder on miss with no fallback', () => {
|
||||
const r = resolveQueueTrack(ref('t3'));
|
||||
expect(r.id).toBe('t3');
|
||||
expect(r.title).toBe('…');
|
||||
});
|
||||
|
||||
it('carries the ref queue-only flags onto the resolved track', () => {
|
||||
seedQueueResolver('s1', [track('t4')]);
|
||||
const r = resolveQueueTrack(ref('t4', { radioAdded: true }));
|
||||
expect(r.radioAdded).toBe(true);
|
||||
});
|
||||
|
||||
it('merges session star/rating overrides', () => {
|
||||
seedQueueResolver('s1', [track('t5')]);
|
||||
usePlayerStore.setState({ starredOverrides: { t5: true }, userRatingOverrides: { t5: 4 } });
|
||||
const r = resolveQueueTrack(ref('t5'));
|
||||
expect(!!r.starred).toBe(true);
|
||||
expect(r.userRating).toBe(4);
|
||||
});
|
||||
|
||||
it('getQueueTracksView resolves each ref, preferring cache then fallback', () => {
|
||||
seedQueueResolver('s1', [track('a', { title: 'CachedA' })]);
|
||||
const out = getQueueTracksView([ref('a'), ref('b')], [track('a'), track('b', { title: 'FbB' })]);
|
||||
expect(out.map(t => t.title)).toEqual(['CachedA', 'FbB']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user