mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Python 3.11 is now required, old versions were causing unpredictability in tests. (Sanic does not yet support 3.12) Sanic has been upgraded to 23.6.0, which is the latest version that avoids this bug: https://github.com/sanic-org/sanic/issues/2921 New strategy for multiprocessing is to create all multiprocessing tools in one process, then fork to other processes. The previous strategy was to declare multiprocessing tools at the top of every file, or wherever they were needed at import/creation. Now all multiprocessing tools are attached to the app.shared_ctx. This means `api_app` is imported in many, many places. This forced a change in how the DownloadManager works. Previously, it would continually run download workers which would pull downloads from a multiprocessing.Queue. Now, a single worker checks for new downloads and sends a Sanic signal. Flags have been reworked to use the `api_app`. I removed the `which` flag functionality because the `which` are called at import and needed their own multiprocessing.Event.
192 lines
8.1 KiB
Python
192 lines
8.1 KiB
Python
import json
|
|
from datetime import timedelta
|
|
from http import HTTPStatus
|
|
from json import dumps
|
|
|
|
import pytest
|
|
|
|
from modules.videos.models import Video
|
|
from wrolpi.dates import now
|
|
|
|
|
|
def test_get_video_prev_next(test_session, channel_factory, video_factory):
|
|
"""
|
|
Test that the previous and next videos will be retrieved when fetching a video.
|
|
"""
|
|
channel1, channel2, channel3, channel4 = channel_factory(), channel_factory(), channel_factory(), channel_factory()
|
|
|
|
now_ = now()
|
|
second = timedelta(seconds=1)
|
|
|
|
# The upload_date decides the order of the prev/next videos.
|
|
video_factory(title='vid1', channel_id=channel1.id, upload_date=now_)
|
|
video_factory(title='vid2', channel_id=channel1.id, upload_date=now_ + second)
|
|
video_factory(title='vid3', channel_id=channel2.id, upload_date=now_ + (second * 4))
|
|
video_factory(title='vid4', channel_id=channel1.id, upload_date=now_ + (second * 3))
|
|
video_factory(title='vid5', channel_id=channel2.id, upload_date=now_ + (second * 2))
|
|
video_factory(title='vid6', channel_id=channel2.id, upload_date=now_ + (second * 5))
|
|
video_factory(title='vid7', channel_id=channel1.id)
|
|
video_factory(title='vid8', channel_id=channel2.id, upload_date=now_ + (second * 7))
|
|
video_factory(title='vid9', channel_id=channel3.id, upload_date=now_ + (second * 8))
|
|
# Videos without an upload date can't be placed.
|
|
video_factory(title='vid10', channel_id=channel4.id)
|
|
video_factory(title='vid11', channel_id=channel4.id)
|
|
|
|
test_session.commit()
|
|
|
|
tests = [
|
|
# Channel 1's videos were inserted in upload_date order.
|
|
(1, (None, 'vid2')),
|
|
(2, ('vid1', 'vid4')),
|
|
(4, ('vid2', None)), # 7 has no upload_date, so it doesn't come after 4.
|
|
(7, ('vid4', None)), # 7 has no upload_date, so we get the video next to it's file.
|
|
# Channel 3 has only one video.
|
|
(9, (None, None)),
|
|
# Channel 2 was inserted out of order.
|
|
(5, (None, 'vid3')),
|
|
(3, ('vid5', 'vid6')),
|
|
(8, ('vid6', None)),
|
|
# Channel 4's videos have no upload date, so we don't know what is previous/next.
|
|
(10, (None, 'vid11')),
|
|
(11, ('vid10', None)),
|
|
]
|
|
|
|
for id_, (prev_title, next_title) in tests:
|
|
video = test_session.query(Video).filter_by(id=id_).one()
|
|
prev_video, next_video = video.get_surrounding_videos()
|
|
|
|
try:
|
|
if prev_title is None:
|
|
assert prev_video is None
|
|
else:
|
|
assert prev_video and prev_video.file_group.title == prev_title
|
|
|
|
if next_title is None:
|
|
assert next_video is None
|
|
else:
|
|
assert next_video and next_video.file_group.title == next_title
|
|
except AssertionError as e:
|
|
raise AssertionError(f'Assert failed for {id_=} {prev_title=} {next_title=}') from e
|
|
|
|
|
|
def test_get_video_prev_next_no_upload_date(test_session, video_factory, channel_factory):
|
|
"""
|
|
Previous and Next Videos should be those next to a Video file when videos do not have upload dates.
|
|
"""
|
|
channel1, channel2 = channel_factory(), channel_factory()
|
|
vid0, vid1, vid2, vid3, vid4, vid5 = video_factory(channel2.id, title='vid0'), \
|
|
video_factory(channel1.id, title='vid1'), \
|
|
video_factory(channel1.id, title='vid2'), \
|
|
video_factory(channel1.id, title='vid3'), \
|
|
video_factory(title='vid4'), \
|
|
video_factory(title='vid5')
|
|
test_session.commit()
|
|
|
|
# Channel 2 has only 1 video.
|
|
prev, next_ = vid0.get_surrounding_videos()
|
|
assert prev is None
|
|
assert next_ is None
|
|
|
|
# Channel 1 has 3 videos, finally getting surrounding videos.
|
|
prev, next_ = vid1.get_surrounding_videos()
|
|
assert prev is None
|
|
assert next_ == vid2
|
|
prev, next_ = vid2.get_surrounding_videos()
|
|
assert prev == vid1
|
|
assert next_ == vid3
|
|
prev, next_ = vid3.get_surrounding_videos()
|
|
assert prev == vid2
|
|
assert next_ is None
|
|
|
|
# Videos without a channel
|
|
prev, next_ = vid4.get_surrounding_videos()
|
|
assert prev is None
|
|
assert next_ is vid5
|
|
prev, next_ = vid5.get_surrounding_videos()
|
|
assert prev == vid4
|
|
assert next_ is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_video_delete(test_async_client, test_session, channel_factory, video_factory, test_download_manager):
|
|
"""Video.delete() removes the video's files, but leave the DB record."""
|
|
channel1, channel2 = channel_factory(), channel_factory()
|
|
vid1 = video_factory(channel_id=channel1.id, with_video_file=True, with_caption_file=True,
|
|
with_info_json={'url': '1'})
|
|
vid2 = video_factory(channel_id=channel2.id, with_video_file=True, with_info_json={'url': '2'})
|
|
test_session.commit()
|
|
|
|
assert test_session.query(Video).count() == 2
|
|
|
|
vid1_video_path, vid1_caption_path = vid1.video_path, vid1.caption_paths[0]
|
|
vid2_video_path, vid2_info_json_path = vid2.video_path, vid2.info_json_path
|
|
|
|
# No videos have been deleted yet.
|
|
assert vid1_video_path.is_file() and vid1_caption_path.is_file()
|
|
assert vid2_video_path.is_file() and vid2_info_json_path.is_file()
|
|
|
|
request, response = await test_async_client.delete(f'/api/videos/video/{vid1.id}')
|
|
assert response.status_code == HTTPStatus.NO_CONTENT
|
|
|
|
# Video was added to skip list.
|
|
assert test_download_manager.is_skipped(vid1.file_group.url)
|
|
# Video was deleted.
|
|
assert test_session.query(Video).count() == 1
|
|
assert vid1_video_path.is_file() is False and vid1_caption_path.is_file() is False
|
|
assert vid2_video_path.is_file() and vid2_info_json_path.is_file()
|
|
|
|
request, response = await test_async_client.delete(f'/api/videos/video/{vid2.id}')
|
|
assert response.status_code == HTTPStatus.NO_CONTENT
|
|
|
|
assert test_download_manager.is_skipped(vid1.file_group.url, vid2.file_group.url)
|
|
assert test_session.query(Video).count() == 0
|
|
assert vid1_video_path.is_file() is False and vid1_caption_path.is_file() is False
|
|
assert vid2_video_path.is_file() is False and vid2_info_json_path.is_file() is False
|
|
|
|
# 3 does not exist.
|
|
request, response = await test_async_client.delete(f'/api/videos/video/3')
|
|
assert response.status_code == HTTPStatus.NOT_FOUND
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_wrol_mode(test_async_client, simple_channel, simple_video, wrol_mode_fixture,
|
|
test_download_manager, tag_factory):
|
|
"""Many methods are blocked when WROL Mode is enabled."""
|
|
channel = dumps(dict(name=simple_channel.name, directory=str(simple_channel.directory)))
|
|
tag = tag_factory()
|
|
|
|
await wrol_mode_fixture(True)
|
|
|
|
# Can't create, update, or delete a channel.
|
|
_, resp = await test_async_client.post('/api/videos/channels', content=channel)
|
|
assert resp.status_code == HTTPStatus.FORBIDDEN
|
|
assert resp.json['code'] == 'WROL_MODE_ENABLED'
|
|
_, resp = await test_async_client.put(f'/api/videos/channels/{simple_channel.id}', content=channel)
|
|
assert resp.status_code == HTTPStatus.FORBIDDEN
|
|
assert resp.json['code'] == 'WROL_MODE_ENABLED'
|
|
_, resp = await test_async_client.delete(f'/api/videos/channels/{simple_channel.id}')
|
|
assert resp.status_code == HTTPStatus.FORBIDDEN
|
|
assert resp.json['code'] == 'WROL_MODE_ENABLED'
|
|
|
|
# Can't delete a video
|
|
_, resp = await test_async_client.delete(f'/api/videos/video/{simple_video.id}')
|
|
assert resp.status_code == HTTPStatus.FORBIDDEN
|
|
assert resp.json['code'] == 'WROL_MODE_ENABLED'
|
|
|
|
# Can't refresh or download
|
|
_, resp = await test_async_client.post('/api/files/refresh')
|
|
assert resp.status_code == HTTPStatus.FORBIDDEN
|
|
assert resp.json['code'] == 'WROL_MODE_ENABLED'
|
|
_, resp = await test_async_client.post(f'/api/videos/channels/download/{simple_channel.id}')
|
|
assert resp.status_code == HTTPStatus.FORBIDDEN
|
|
assert resp.json['code'] == 'WROL_MODE_ENABLED'
|
|
|
|
# THE REST OF THESE METHODS ARE ALLOWED
|
|
content = dict(file_group_id=simple_video.file_group_id, tag_name=tag.name)
|
|
_, resp = await test_async_client.post('/api/files/tag', content=json.dumps(content))
|
|
assert resp.status_code == HTTPStatus.CREATED
|
|
|
|
assert test_download_manager.stopped.is_set()
|
|
|
|
await wrol_mode_fixture(False)
|
|
assert not test_download_manager.stopped.is_set()
|