Files
siftlode/backend/tests/test_share_paths.py
T
peter 8383459f70 fix: share, upload, crop, auto-skip, tag chips and See all hold what they claim
- Share: a link that expired is never copied (the dialog and one-click Share pick a live one,
  else make one), says it expired, and the copy-on-open claim clears on the next copy.
- A password-protected file link asks to open a file, not to watch a video.
- Upload: Cancel, the close button or the backdrop while naming a folder makes no folder; a failed
  refetch keeps the target; a folder being made says so.
- Crop: an infinite number is dropped, not a 500; an unprobed clip's size follows a shown-size crop;
  the edit_spec docs name frame_w/frame_h; the join and rotated paths are tested.
- Auto-skip: the wait gives way on a marker too short for it, so the skip still fires.
- Tag chips count the followed channels (subscribed_count) and a toggled chip keeps the focus.
- Continue watching's See all sorts by last watched when in progress was already on.
2026-11-06 00:09:39 +01:00

149 lines
5.7 KiB
Python

"""A share link's address says what it opens: /watch/ a video, /file/ anything else, /folder/ a folder.
Only a word for the reader: the token is the key, so every path opens every link, and the links
already sent out on /watch/ keep working for good. DB-lane.
"""
import pytest
from cryptography.fernet import Fernet
from app import main, security
from app.config import settings
from app.downloads import og
from app.downloads import probe as probemod
_PAGE = """<!doctype html><html><head>
<!--OG:START--><meta property="og:title" content="Siftlode" /><!--OG:END-->
</head><body><script src="/assets/{entry}.js"></script></body></html>"""
_VIDEO = {
"streams": [
{"codec_type": "video", "codec_name": "h264", "pix_fmt": "yuv420p"},
{"codec_type": "audio", "codec_name": "aac"},
],
"format": {"format_name": "mov,mp4", "duration": "5"},
}
_AUDIO = {
"streams": [{"codec_type": "audio", "codec_name": "aac"}],
"format": {"format_name": "mov,mp4", "duration": "5"},
}
@pytest.fixture
def pages(tmp_path, monkeypatch):
index, watch = tmp_path / "index.html", tmp_path / "watch.html"
index.write_text(_PAGE.format(entry="app"), encoding="utf-8")
watch.write_text(_PAGE.format(entry="watch"), encoding="utf-8")
monkeypatch.setattr(main, "INDEX_HTML", index)
monkeypatch.setattr(main, "WATCH_HTML", watch)
@pytest.fixture
def key(monkeypatch):
"""A link's address is read back from its encrypted token."""
monkeypatch.setattr(security, "_fernet", Fernet(Fernet.generate_key()))
@pytest.fixture
def shared(as_user, make_user, tmp_path, monkeypatch, upload_headers):
"""Uploads a file ffprobe reads as `data` (None: it cannot read it) and shares it."""
monkeypatch.setattr(settings, "files_root", str(tmp_path / "media"))
owner = as_user(make_user())
def share(name: str, data: dict | None, **link: object) -> dict:
monkeypatch.setattr(probemod, "probe_json", lambda _p: data)
job = owner.post(
"/api/files/upload",
content=b"some bytes",
headers=upload_headers(name, display_name=name),
)
assert job.status_code == 200, job.text
made = owner.post(f"/api/downloads/{job.json()['id']}/links", json=link)
assert made.status_code == 200, made.text
return made.json()
share.owner = owner # type: ignore[attr-defined]
return share
@pytest.mark.parametrize(
("name", "data", "prefix"),
[
("clip.mp4", _VIDEO, "/watch/"),
("song.m4a", _AUDIO, "/file/"),
("backup.tar.gz", None, "/file/"),
],
)
def test_a_link_is_given_out_on_the_path_of_what_it_opens(shared, name, data, prefix):
made = shared(name, data)
assert made["url"] == f"{prefix}{made['token']}"
def test_a_folder_link_is_given_out_on_folder(shared, key):
owner = shared.owner
folder = owner.post("/api/files/folders", json={"name": "Friends"}).json()
made = owner.post(f"/api/downloads/folders/{folder['id']}/links", json={}).json()
assert made["url"] == f"/folder/{made['token']}"
listed = owner.get(f"/api/downloads/folders/{folder['id']}/links").json()
assert [entry["url"] for entry in listed] == [made["url"]]
def test_the_list_gives_the_same_address_as_the_making(shared, key):
made = shared("backup.tar.gz", None)
owner = shared.owner
files = owner.get("/api/files").json()
job_id = next(f["id"] for f in files if f["display_name"] == "backup.tar.gz")
listed = owner.get(f"/api/downloads/{job_id}/links").json()
assert [entry["url"] for entry in listed] == [made["url"]]
@pytest.mark.parametrize("path", ["watch", "file", "folder"])
@pytest.mark.parametrize(
("name", "data"), [("clip.mp4", _VIDEO), ("backup.tar.gz", None)], ids=["video", "file"]
)
def test_every_path_opens_every_link(pages, shared, client, path, name, data):
"""The links already sent on /watch/ open whatever they open, for good."""
token = shared(name, data)["token"]
r = client.get(f"/{path}/{token}")
assert r.status_code == 200
assert "/assets/watch.js" in r.text and "/assets/app.js" not in r.text
assert f'content="{name}"' in r.text
@pytest.mark.parametrize("path", ["/file/no-such-token", "/folder/no-such-token", "/file/a/b"])
def test_a_new_path_without_a_live_link_still_gets_the_player(pages, client, path):
r = client.get(path)
assert r.status_code == 200
assert "/assets/watch.js" in r.text and "/assets/app.js" not in r.text
def test_a_card_names_a_file_as_a_file(shared, db):
made = shared("backup.tar.gz", None)
tags = og._watch_tags(made["token"], db)
assert f'{made["url"]}"' in tags
assert "video.other" not in tags
locked = shared("notes.tar.gz", None, password="pw")
hidden = og._watch_tags(locked["token"], db)
assert "Password-protected file" in hidden and "notes" not in hidden
assert "video.other" not in hidden
def test_a_card_of_a_video_stays_a_video(shared, db):
made = shared("clip.mp4", _VIDEO)
tags = og._watch_tags(made["token"], db)
assert f"/watch/{made['token']}" in tags and "video.other" in tags
locked = shared("other.mp4", _VIDEO, password="pw")
assert "Password-protected video" in og._watch_tags(locked["token"], db)
@pytest.mark.parametrize(
("name", "data", "video"),
[("clip.mp4", _VIDEO, True), ("song.m4a", _AUDIO, False), ("backup.tar.gz", None, False)],
)
def test_a_password_link_says_whether_it_opens_a_video(shared, client, name, data, video):
"""The lock screen asks to watch a video and to open anything else, whichever path it is on."""
token = shared(name, data, password="pw")["token"]
meta = client.get(f"/api/public/watch/{token}").json()
assert meta == {"needs_password": True, "kind": "file", "video": video}