mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-19 17:47:40 +00:00
143 lines
5.4 KiB
Python
143 lines
5.4 KiB
Python
"""
|
|
DELETE /api/m3u/accounts/{id}/ behavior.
|
|
|
|
The endpoint always cascade-deletes auto-created channels owned by the
|
|
account. Manual channels survive even if some of their streams were
|
|
owned by the deleted account; only the streams from that account go
|
|
away with the account, and the channel keeps any streams sourced from
|
|
other accounts. The legacy ``cleanup_channels`` query parameter is
|
|
accepted but ignored.
|
|
"""
|
|
from unittest.mock import patch
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
from django.utils import timezone
|
|
from rest_framework import status
|
|
from rest_framework.test import APIClient
|
|
|
|
from apps.channels.models import (
|
|
Channel,
|
|
ChannelGroup,
|
|
ChannelStream,
|
|
Stream,
|
|
)
|
|
from apps.m3u.models import M3UAccount
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class M3UAccountDestroyTests(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(
|
|
username="destroyer", password="testpass123"
|
|
)
|
|
self.user.user_level = 10
|
|
self.user.save()
|
|
self.client = APIClient()
|
|
self.client.force_authenticate(user=self.user)
|
|
# Patching the proxy stop here keeps the Redis-backed call out
|
|
# of the test path. The endpoint loops over every auto-created
|
|
# channel before the DB transaction; the real implementation is
|
|
# exercised by integration tests, not unit tests.
|
|
self._stop_patch = patch(
|
|
"apps.proxy.live_proxy.services.channel_service."
|
|
"ChannelService.stop_channel"
|
|
)
|
|
self._stop_patch.start()
|
|
self.addCleanup(self._stop_patch.stop)
|
|
|
|
self.account = M3UAccount.objects.create(
|
|
name="ProviderA",
|
|
server_url="http://example.com/a.m3u",
|
|
)
|
|
self.other_account = M3UAccount.objects.create(
|
|
name="ProviderB",
|
|
server_url="http://example.com/b.m3u",
|
|
)
|
|
self.group = ChannelGroup.objects.create(name="News")
|
|
|
|
def _make_stream(self, account, name="ESPN"):
|
|
return Stream.objects.create(
|
|
name=name,
|
|
url=f"http://example.com/{name.lower()}.m3u8",
|
|
m3u_account=account,
|
|
channel_group=self.group,
|
|
tvg_id=name.lower(),
|
|
last_seen=timezone.now(),
|
|
)
|
|
|
|
def test_cascade_deletes_auto_created_channels(self):
|
|
# Two auto-created channels under the account being deleted.
|
|
for n in (101.0, 102.0):
|
|
ch = Channel.objects.create(
|
|
channel_number=n,
|
|
name=f"Auto {n}",
|
|
channel_group=self.group,
|
|
auto_created=True,
|
|
auto_created_by=self.account,
|
|
)
|
|
ChannelStream.objects.create(
|
|
channel=ch,
|
|
stream=self._make_stream(self.account, name=f"Auto{int(n)}"),
|
|
order=0,
|
|
)
|
|
|
|
response = self.client.delete(
|
|
f"/api/m3u/accounts/{self.account.id}/"
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["deleted_channels"], 2)
|
|
self.assertFalse(M3UAccount.objects.filter(id=self.account.id).exists())
|
|
self.assertFalse(
|
|
Channel.objects.filter(auto_created_by_id=self.account.id).exists()
|
|
)
|
|
|
|
def test_manual_channel_survives_with_other_provider_streams(self):
|
|
# Manual channel with one stream from each account. The provider
|
|
# account's stream goes away with the account; the other
|
|
# account's stream stays, and the channel survives.
|
|
manual = Channel.objects.create(
|
|
channel_number=200.0,
|
|
name="Manual",
|
|
channel_group=self.group,
|
|
auto_created=False,
|
|
)
|
|
provider_stream = self._make_stream(self.account, name="ProviderA")
|
|
other_stream = self._make_stream(self.other_account, name="ProviderB")
|
|
ChannelStream.objects.create(channel=manual, stream=provider_stream, order=0)
|
|
ChannelStream.objects.create(channel=manual, stream=other_stream, order=1)
|
|
|
|
response = self.client.delete(
|
|
f"/api/m3u/accounts/{self.account.id}/"
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["deleted_channels"], 0)
|
|
|
|
manual.refresh_from_db()
|
|
remaining = list(manual.channelstream_set.values_list("stream__id", flat=True))
|
|
self.assertEqual(remaining, [other_stream.id])
|
|
|
|
def test_legacy_cleanup_channels_param_is_ignored(self):
|
|
# Behavior must be identical with or without the deprecated
|
|
# ``cleanup_channels`` query parameter.
|
|
ch = Channel.objects.create(
|
|
channel_number=300.0,
|
|
name="Auto",
|
|
channel_group=self.group,
|
|
auto_created=True,
|
|
auto_created_by=self.account,
|
|
)
|
|
response = self.client.delete(
|
|
f"/api/m3u/accounts/{self.account.id}/?cleanup_channels=false"
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["deleted_channels"], 1)
|
|
self.assertFalse(Channel.objects.filter(id=ch.id).exists())
|
|
|
|
def test_no_op_when_account_has_no_auto_created_channels(self):
|
|
response = self.client.delete(
|
|
f"/api/m3u/accounts/{self.account.id}/"
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["deleted_channels"], 0)
|