mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 01:56:16 -08:00
fix(downloads): fail closed on invalid stored coordinates
This commit is contained in:
1 parent
87727f14b7
commit
aaf4568d89
2 files changed
+47
-18
No files matched your search
@@ -1,6 +1,9 @@
|
||||
import * as schema from '../../database/schema';
|
||||
import type { DownloadsDatabase } from './download-task';
|
||||
import { resolveExistingDownloadIdentity } from './download-request-identity';
|
||||
import {
|
||||
type ExistingDownloadIdentityResolution,
|
||||
resolveExistingDownloadIdentity,
|
||||
} from './download-request-identity';
|
||||
|
||||
type DownloadRow = typeof schema.downloads.$inferSelect;
|
||||
|
||||
@@ -66,9 +69,10 @@ describe('resolveExistingDownloadIdentity', () => {
|
||||
const row = createDownloadRow();
|
||||
const harness = createQueryHarness([row], []);
|
||||
|
||||
await expect(
|
||||
resolveExistingDownloadIdentity(harness.db, episodeRequest)
|
||||
).resolves.toEqual({
|
||||
const resolution: ExistingDownloadIdentityResolution =
|
||||
await resolveExistingDownloadIdentity(harness.db, episodeRequest);
|
||||
|
||||
expect(resolution).toEqual({
|
||||
item: row,
|
||||
kind: 'match',
|
||||
migrateCanonicalId: false,
|
||||
@@ -112,6 +116,24 @@ describe('resolveExistingDownloadIdentity', () => {
|
||||
).resolves.toEqual({ kind: 'conflict' });
|
||||
});
|
||||
|
||||
it.each([
|
||||
['series id', { seriesXtreamId: Number.MAX_SAFE_INTEGER + 1 }],
|
||||
['season number', { seasonNumber: 2.5 }],
|
||||
['episode number', { episodeNumber: Number.NaN }],
|
||||
])(
|
||||
'fails closed when the canonical row has a present but unsafe %s',
|
||||
async (_label, override) => {
|
||||
const harness = createQueryHarness(
|
||||
[createDownloadRow(override)],
|
||||
[]
|
||||
);
|
||||
|
||||
await expect(
|
||||
resolveExistingDownloadIdentity(harness.db, episodeRequest)
|
||||
).resolves.toEqual({ kind: 'conflict' });
|
||||
}
|
||||
);
|
||||
|
||||
it('fails closed when multiple rows share the legacy coordinates', async () => {
|
||||
const harness = createQueryHarness([], [
|
||||
createDownloadRow({ id: 42, xtreamId: 77 }),
|
||||
|
||||
@@ -11,20 +11,20 @@ export const DOWNLOAD_IDENTITY_KIND = {
|
||||
} as const;
|
||||
|
||||
interface DownloadIdentityNone {
|
||||
kind: typeof DOWNLOAD_IDENTITY_KIND.NONE;
|
||||
readonly kind: typeof DOWNLOAD_IDENTITY_KIND.NONE;
|
||||
}
|
||||
|
||||
interface DownloadIdentityConflict {
|
||||
kind: typeof DOWNLOAD_IDENTITY_KIND.CONFLICT;
|
||||
readonly kind: typeof DOWNLOAD_IDENTITY_KIND.CONFLICT;
|
||||
}
|
||||
|
||||
interface DownloadIdentityMatch {
|
||||
item: DownloadRow;
|
||||
kind: typeof DOWNLOAD_IDENTITY_KIND.MATCH;
|
||||
migrateCanonicalId: boolean;
|
||||
readonly item: DownloadRow;
|
||||
readonly kind: typeof DOWNLOAD_IDENTITY_KIND.MATCH;
|
||||
readonly migrateCanonicalId: boolean;
|
||||
}
|
||||
|
||||
export type DownloadIdentityResolution =
|
||||
export type ExistingDownloadIdentityResolution =
|
||||
| DownloadIdentityNone
|
||||
| DownloadIdentityConflict
|
||||
| DownloadIdentityMatch;
|
||||
@@ -38,7 +38,7 @@ export interface DownloadIdentityRequest {
|
||||
xtreamId: number;
|
||||
}
|
||||
|
||||
function isSafeInteger(value: number | undefined): value is number {
|
||||
function isSafeInteger(value: number | null | undefined): value is number {
|
||||
return Number.isSafeInteger(value);
|
||||
}
|
||||
|
||||
@@ -51,25 +51,32 @@ function rowHasConflictingCoordinates(
|
||||
>
|
||||
>
|
||||
): boolean {
|
||||
const { episodeNumber, seasonNumber, seriesXtreamId } = row;
|
||||
if (
|
||||
!isSafeInteger(row.seriesXtreamId ?? undefined) ||
|
||||
!isSafeInteger(row.seasonNumber ?? undefined) ||
|
||||
!isSafeInteger(row.episodeNumber ?? undefined)
|
||||
seriesXtreamId === null ||
|
||||
seriesXtreamId === undefined ||
|
||||
seasonNumber === null ||
|
||||
seasonNumber === undefined ||
|
||||
episodeNumber === null ||
|
||||
episodeNumber === undefined
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
row.seriesXtreamId !== request.seriesXtreamId ||
|
||||
row.seasonNumber !== request.seasonNumber ||
|
||||
row.episodeNumber !== request.episodeNumber
|
||||
!isSafeInteger(seriesXtreamId) ||
|
||||
!isSafeInteger(seasonNumber) ||
|
||||
!isSafeInteger(episodeNumber) ||
|
||||
seriesXtreamId !== request.seriesXtreamId ||
|
||||
seasonNumber !== request.seasonNumber ||
|
||||
episodeNumber !== request.episodeNumber
|
||||
);
|
||||
}
|
||||
|
||||
export async function resolveExistingDownloadIdentity(
|
||||
db: DownloadsDatabase,
|
||||
request: DownloadIdentityRequest
|
||||
): Promise<DownloadIdentityResolution> {
|
||||
): Promise<ExistingDownloadIdentityResolution> {
|
||||
const canonicalRows = await db
|
||||
.select()
|
||||
.from(schema.downloads)
|
||||
|
||||
Reference in new issue
Block a user