Skip to content

Commit 83009b2

Browse files
committed
fix(request): restore media status correctly when deleting requests
Reset media status to DELETED (or UNKNOWN) when the last active request is removed, based on whether completed requests exist
1 parent 32169d9 commit 83009b2

2 files changed

Lines changed: 157 additions & 6 deletions

File tree

server/routes/request.test.ts

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,132 @@ describe('POST /request/:requestId/retry', () => {
265265
assert.ok(persisted.updatedAt > failed.updatedAt);
266266
});
267267
});
268+
269+
describe('DELETE /request/:requestId, deleted media status restoration', () => {
270+
async function seedDeletedMediaScenario() {
271+
const userRepo = getRepository(User);
272+
const mediaRepo = getRepository(Media);
273+
const requestRepo = getRepository(MediaRequest);
274+
275+
const admin = await userRepo.findOneOrFail({
276+
where: { email: 'admin@seerr.dev' },
277+
});
278+
279+
const media = await mediaRepo.save(
280+
new Media({
281+
mediaType: MediaType.MOVIE,
282+
tmdbId: 99001,
283+
status: MediaStatus.DELETED,
284+
status4k: MediaStatus.UNKNOWN,
285+
})
286+
);
287+
288+
const staleRequest = await requestRepo.save(
289+
new MediaRequest({
290+
type: MediaType.MOVIE,
291+
status: MediaRequestStatus.COMPLETED,
292+
media,
293+
requestedBy: admin,
294+
is4k: false,
295+
isAutoRequest: true,
296+
})
297+
);
298+
299+
media.status = MediaStatus.PENDING;
300+
await mediaRepo.save(media);
301+
302+
const newRequest = await requestRepo.save(
303+
new MediaRequest({
304+
type: MediaType.MOVIE,
305+
status: MediaRequestStatus.APPROVED,
306+
media,
307+
requestedBy: admin,
308+
is4k: false,
309+
})
310+
);
311+
312+
return { media, staleRequest, newRequest, admin };
313+
}
314+
315+
it('restores media status to DELETED when the re-request is deleted and a stale completed request remains', async () => {
316+
const mediaRepo = getRepository(Media);
317+
const { media, newRequest } = await seedDeletedMediaScenario();
318+
319+
const agent = await loginAs('admin@seerr.dev', 'test1234');
320+
const res = await agent.delete(`/request/${newRequest.id}`);
321+
322+
assert.strictEqual(res.status, 204);
323+
324+
const updated = await mediaRepo.findOneOrFail({ where: { id: media.id } });
325+
assert.strictEqual(updated.status, MediaStatus.DELETED);
326+
});
327+
328+
it('resets media status to UNKNOWN when the stale completed request is also deleted', async () => {
329+
const mediaRepo = getRepository(Media);
330+
const requestRepo = getRepository(MediaRequest);
331+
const { media, newRequest, staleRequest } =
332+
await seedDeletedMediaScenario();
333+
334+
const agent = await loginAs('admin@seerr.dev', 'test1234');
335+
336+
await agent.delete(`/request/${newRequest.id}`);
337+
338+
const res = await agent.delete(`/request/${staleRequest.id}`);
339+
assert.strictEqual(res.status, 204);
340+
341+
const updated = await mediaRepo.findOneOrFail({ where: { id: media.id } });
342+
assert.strictEqual(updated.status, MediaStatus.UNKNOWN);
343+
344+
const remaining = await requestRepo.find({
345+
where: { media: { id: media.id } },
346+
});
347+
assert.strictEqual(remaining.length, 0);
348+
});
349+
350+
it('does not reset media status when other active requests still exist', async () => {
351+
const userRepo = getRepository(User);
352+
const mediaRepo = getRepository(Media);
353+
const requestRepo = getRepository(MediaRequest);
354+
355+
const admin = await userRepo.findOneOrFail({
356+
where: { email: 'admin@seerr.dev' },
357+
});
358+
359+
const media = await mediaRepo.save(
360+
new Media({
361+
mediaType: MediaType.MOVIE,
362+
tmdbId: 99002,
363+
status: MediaStatus.PENDING,
364+
status4k: MediaStatus.UNKNOWN,
365+
})
366+
);
367+
368+
const req1 = await requestRepo.save(
369+
new MediaRequest({
370+
type: MediaType.MOVIE,
371+
status: MediaRequestStatus.PENDING,
372+
media,
373+
requestedBy: admin,
374+
is4k: false,
375+
})
376+
);
377+
378+
await requestRepo.save(
379+
new MediaRequest({
380+
type: MediaType.MOVIE,
381+
status: MediaRequestStatus.PENDING,
382+
media,
383+
requestedBy: admin,
384+
is4k: false,
385+
})
386+
);
387+
388+
const agent = await loginAs('admin@seerr.dev', 'test1234');
389+
const res = await agent.delete(`/request/${req1.id}`);
390+
391+
assert.strictEqual(res.status, 204);
392+
393+
const updated = await mediaRepo.findOneOrFail({ where: { id: media.id } });
394+
assert.strictEqual(updated.status, MediaStatus.PENDING);
395+
});
396+
});

server/subscriber/MediaRequestSubscriber.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -952,13 +952,24 @@ export class MediaRequestSubscriber implements EntitySubscriberInterface<MediaRe
952952
relations: { requests: true },
953953
});
954954

955+
const hasActiveNonFk = fullMedia.requests.some(
956+
(request) =>
957+
!request.is4k &&
958+
request.status !== MediaRequestStatus.COMPLETED &&
959+
request.status !== MediaRequestStatus.DECLINED
960+
);
961+
const hasActive4k = fullMedia.requests.some(
962+
(request) =>
963+
request.is4k &&
964+
request.status !== MediaRequestStatus.COMPLETED &&
965+
request.status !== MediaRequestStatus.DECLINED
966+
);
967+
955968
const needsStatusUpdate =
956-
!fullMedia.requests.some((request) => !request.is4k) &&
957-
fullMedia.status !== MediaStatus.AVAILABLE;
969+
!hasActiveNonFk && fullMedia.status !== MediaStatus.AVAILABLE;
958970

959971
const needs4kStatusUpdate =
960-
!fullMedia.requests.some((request) => request.is4k) &&
961-
fullMedia.status4k !== MediaStatus.AVAILABLE;
972+
!hasActive4k && fullMedia.status4k !== MediaStatus.AVAILABLE;
962973

963974
if (needsStatusUpdate || needs4kStatusUpdate) {
964975
// Re-fetch WITHOUT requests to avoid cascade issues on save
@@ -967,10 +978,21 @@ export class MediaRequestSubscriber implements EntitySubscriberInterface<MediaRe
967978
});
968979

969980
if (needsStatusUpdate) {
970-
cleanMedia.status = MediaStatus.UNKNOWN;
981+
const hadCompleted = fullMedia.requests.some(
982+
(r) => !r.is4k && r.status === MediaRequestStatus.COMPLETED
983+
);
984+
cleanMedia.status = hadCompleted
985+
? MediaStatus.DELETED
986+
: MediaStatus.UNKNOWN;
971987
}
988+
972989
if (needs4kStatusUpdate) {
973-
cleanMedia.status4k = MediaStatus.UNKNOWN;
990+
const hadCompleted4k = fullMedia.requests.some(
991+
(r) => r.is4k && r.status === MediaRequestStatus.COMPLETED
992+
);
993+
cleanMedia.status4k = hadCompleted4k
994+
? MediaStatus.DELETED
995+
: MediaStatus.UNKNOWN;
974996
}
975997

976998
await manager.save(cleanMedia);

0 commit comments

Comments
 (0)