mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 13:16:16 -08:00
fix(playback): expose strict position persistence
This commit is contained in:
1 parent
ac12272db6
commit
7b6d576fef
8 files changed
+434
-29
No files matched your search
@@ -199,6 +199,130 @@ describe('PlaybackPositionRuntimeBridgeService', () => {
|
||||
expect(service.onPlaybackPositionUpdate(callback)).toBe(unsubscribe);
|
||||
expect(onPlaybackPositionUpdate).toHaveBeenCalledWith(callback);
|
||||
});
|
||||
|
||||
describe.each([
|
||||
{
|
||||
name: 'save',
|
||||
installBridge: (implementation: jest.Mock) => {
|
||||
window.electron = {
|
||||
...window.electron,
|
||||
dbSavePlaybackPosition: implementation,
|
||||
} as unknown as typeof window.electron;
|
||||
},
|
||||
invokeLenient: (
|
||||
target: PlaybackPositionRuntimeBridgeService
|
||||
) => target.savePlaybackPosition('playlist-1', createPosition()),
|
||||
invokeStrict: (
|
||||
target: PlaybackPositionRuntimeBridgeService
|
||||
) =>
|
||||
target.savePlaybackPositionOrThrow(
|
||||
'playlist-1',
|
||||
createPosition()
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'clear',
|
||||
installBridge: (implementation: jest.Mock) => {
|
||||
window.electron = {
|
||||
...window.electron,
|
||||
dbClearPlaybackPosition: implementation,
|
||||
} as unknown as typeof window.electron;
|
||||
},
|
||||
invokeLenient: (
|
||||
target: PlaybackPositionRuntimeBridgeService
|
||||
) =>
|
||||
target.clearPlaybackPosition(
|
||||
'playlist-1',
|
||||
100,
|
||||
'vod'
|
||||
),
|
||||
invokeStrict: (
|
||||
target: PlaybackPositionRuntimeBridgeService
|
||||
) =>
|
||||
target.clearPlaybackPositionOrThrow(
|
||||
'playlist-1',
|
||||
100,
|
||||
'vod'
|
||||
),
|
||||
},
|
||||
])('$name persistence', (operation) => {
|
||||
it('accepts only an explicit success result', async () => {
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
operation.installBridge(
|
||||
jest.fn().mockResolvedValue({ success: true })
|
||||
);
|
||||
|
||||
await expect(operation.invokeStrict(service)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('propagates rejected IPC', async () => {
|
||||
const error = new Error('database is locked');
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
operation.installBridge(jest.fn().mockRejectedValue(error));
|
||||
|
||||
await expect(operation.invokeStrict(service)).rejects.toBe(error);
|
||||
});
|
||||
|
||||
it.each([{ success: false }, {}, undefined])(
|
||||
'rejects a non-success result %#',
|
||||
async (result) => {
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
operation.installBridge(jest.fn().mockResolvedValue(result));
|
||||
|
||||
await expect(operation.invokeStrict(service)).rejects.toThrow(
|
||||
'did not succeed'
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('rejects when the storage capability is unavailable', async () => {
|
||||
const bridgeMethod = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ success: true });
|
||||
operation.installBridge(bridgeMethod);
|
||||
|
||||
await expect(operation.invokeStrict(service)).rejects.toThrow(
|
||||
'storage is unavailable'
|
||||
);
|
||||
expect(bridgeMethod).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects when the expected bridge method is unavailable', async () => {
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
|
||||
await expect(operation.invokeStrict(service)).rejects.toThrow(
|
||||
'method is unavailable'
|
||||
);
|
||||
});
|
||||
|
||||
it.each([{ success: false }, {}, undefined])(
|
||||
'ignores a non-success result through the lenient method %#',
|
||||
async (result) => {
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
operation.installBridge(jest.fn().mockResolvedValue(result));
|
||||
|
||||
await expect(
|
||||
operation.invokeLenient(service)
|
||||
).resolves.toBeUndefined();
|
||||
}
|
||||
);
|
||||
|
||||
it('resolves the lenient method when its bridge method is missing', async () => {
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
|
||||
await expect(
|
||||
operation.invokeLenient(service)
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('propagates rejected IPC through the lenient method', async () => {
|
||||
const error = new Error('database is locked');
|
||||
runtimeCapabilities.supportsPlaybackPositionStorage = true;
|
||||
operation.installBridge(jest.fn().mockRejectedValue(error));
|
||||
|
||||
await expect(operation.invokeLenient(service)).rejects.toBe(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createPosition(
|
||||
|
||||
@@ -65,6 +65,27 @@ export class PlaybackPositionRuntimeBridgeService {
|
||||
await this.bridge?.dbSavePlaybackPosition?.(playlistId, data);
|
||||
}
|
||||
|
||||
async savePlaybackPositionOrThrow(
|
||||
playlistId: string,
|
||||
data: PlaybackPositionData
|
||||
): Promise<void> {
|
||||
if (!this.supportsStorage) {
|
||||
throw new Error('Playback position storage is unavailable');
|
||||
}
|
||||
|
||||
const bridge = this.bridge;
|
||||
if (typeof bridge?.dbSavePlaybackPosition !== 'function') {
|
||||
throw new Error(
|
||||
'Playback position save method is unavailable'
|
||||
);
|
||||
}
|
||||
|
||||
const result = await bridge.dbSavePlaybackPosition(playlistId, data);
|
||||
if (result?.success !== true) {
|
||||
throw new Error('Playback position save did not succeed');
|
||||
}
|
||||
}
|
||||
|
||||
getPlaybackPosition(
|
||||
playlistId: string,
|
||||
contentXtreamId: number,
|
||||
@@ -150,6 +171,32 @@ export class PlaybackPositionRuntimeBridgeService {
|
||||
);
|
||||
}
|
||||
|
||||
async clearPlaybackPositionOrThrow(
|
||||
playlistId: string,
|
||||
contentXtreamId: number,
|
||||
contentType: PlaybackPositionContentType
|
||||
): Promise<void> {
|
||||
if (!this.supportsStorage) {
|
||||
throw new Error('Playback position storage is unavailable');
|
||||
}
|
||||
|
||||
const bridge = this.bridge;
|
||||
if (typeof bridge?.dbClearPlaybackPosition !== 'function') {
|
||||
throw new Error(
|
||||
'Playback position clear method is unavailable'
|
||||
);
|
||||
}
|
||||
|
||||
const result = await bridge.dbClearPlaybackPosition(
|
||||
playlistId,
|
||||
contentXtreamId,
|
||||
contentType
|
||||
);
|
||||
if (result?.success !== true) {
|
||||
throw new Error('Playback position clear did not succeed');
|
||||
}
|
||||
}
|
||||
|
||||
onPlaybackPositionUpdate(
|
||||
callback: (data: PlaybackPositionData) => void
|
||||
): (() => void) | undefined {
|
||||
|
||||
Reference in new issue
Block a user