|
|
|
|
@ -8,7 +8,8 @@ from django.urls import reverse
|
|
|
|
|
from django.utils import timezone
|
|
|
|
|
from music.models import Album, Artist, Track
|
|
|
|
|
from podcasts.models import PodcastEpisode
|
|
|
|
|
from scrobbles.models import Scrobble
|
|
|
|
|
from scrobbles.models import Scrobble, ShareViewLog
|
|
|
|
|
from scrobbles.sqids import encode_scrobble_share
|
|
|
|
|
from tasks.models import Task
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -512,6 +513,7 @@ def test_scrobble_detail_view_with_notes_as_flat_list(client):
|
|
|
|
|
task=task,
|
|
|
|
|
media_type="Task",
|
|
|
|
|
user=user,
|
|
|
|
|
visibility="public",
|
|
|
|
|
log={
|
|
|
|
|
"notes": ["First note", "Second note"],
|
|
|
|
|
"description": "Test description",
|
|
|
|
|
@ -534,6 +536,7 @@ def test_scrobble_detail_view_with_notes_as_dict_timestamps(client):
|
|
|
|
|
task=task,
|
|
|
|
|
media_type="Task",
|
|
|
|
|
user=user,
|
|
|
|
|
visibility="public",
|
|
|
|
|
log={
|
|
|
|
|
"notes": [
|
|
|
|
|
{"2024-01-01 10:00:00": "Note at first timestamp"},
|
|
|
|
|
@ -562,6 +565,7 @@ def test_scrobble_detail_view_with_notes_and_labels(client):
|
|
|
|
|
task=task,
|
|
|
|
|
media_type="Task",
|
|
|
|
|
user=user,
|
|
|
|
|
visibility="public",
|
|
|
|
|
log={
|
|
|
|
|
"notes": [
|
|
|
|
|
{"2024-01-01 10:00:00": "Note with label"},
|
|
|
|
|
@ -739,3 +743,293 @@ def test_gps_webhook_creates_location(client, valid_auth_token):
|
|
|
|
|
)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
assert "scrobble_id" in response.data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_view_shared_visibility(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="shareuser", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
url = reverse(
|
|
|
|
|
"scrobbles:shared-detail",
|
|
|
|
|
kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},
|
|
|
|
|
)
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_view_public_visibility(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="shareuser2", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="public",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
url = reverse(
|
|
|
|
|
"scrobbles:shared-detail",
|
|
|
|
|
kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},
|
|
|
|
|
)
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_view_private_visibility_returns_404(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="shareuser3", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="private",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
url = reverse(
|
|
|
|
|
"scrobbles:shared-detail",
|
|
|
|
|
kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},
|
|
|
|
|
)
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_view_invalid_sqid_returns_404(client):
|
|
|
|
|
url = reverse("scrobbles:shared-detail", kwargs={"sqid": "InvalidSqid123"})
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_view_expired_token_returns_404(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="shareuser4", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
old_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)
|
|
|
|
|
scrobble.regenerate_share_token()
|
|
|
|
|
url = reverse("scrobbles:shared-detail", kwargs={"sqid": old_sqid})
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_view_increments_count_and_logs_view(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="shareuser5", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
assert scrobble.share_view_count == 0
|
|
|
|
|
|
|
|
|
|
url = reverse(
|
|
|
|
|
"scrobbles:shared-detail",
|
|
|
|
|
kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},
|
|
|
|
|
)
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
scrobble.refresh_from_db()
|
|
|
|
|
assert scrobble.share_view_count == 1
|
|
|
|
|
assert ShareViewLog.objects.filter(scrobble=scrobble).count() == 1
|
|
|
|
|
|
|
|
|
|
log_entry = ShareViewLog.objects.filter(scrobble=scrobble).first()
|
|
|
|
|
assert log_entry.ip_address == "127.0.0.1"
|
|
|
|
|
assert log_entry.user_agent == ""
|
|
|
|
|
assert log_entry.referrer == ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_explore_view_shows_only_public_scrobbles(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="exploreuser", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
public_task = Task.objects.create(title="Public Task Title")
|
|
|
|
|
shared_task = Task.objects.create(title="Shared Task Title")
|
|
|
|
|
private_task = Task.objects.create(title="Private Task Title")
|
|
|
|
|
ts = timezone.now()
|
|
|
|
|
public_scrobble = Scrobble.objects.create(
|
|
|
|
|
task=public_task, media_type="Task", user=user, visibility="public",
|
|
|
|
|
timestamp=ts,
|
|
|
|
|
)
|
|
|
|
|
Scrobble.objects.create(
|
|
|
|
|
task=shared_task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=ts,
|
|
|
|
|
)
|
|
|
|
|
Scrobble.objects.create(
|
|
|
|
|
task=private_task, media_type="Task", user=user, visibility="private",
|
|
|
|
|
timestamp=ts,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
url = reverse("scrobbles:explore")
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
content = response.content.decode()
|
|
|
|
|
assert "Public Task Title" in content
|
|
|
|
|
assert "Shared Task Title" not in content
|
|
|
|
|
assert "Private Task Title" not in content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_change_visibility_owner_can_change(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="visuser", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="private",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
client.force_login(user)
|
|
|
|
|
url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.post(url, {"visibility": "shared"})
|
|
|
|
|
assert response.status_code == 302
|
|
|
|
|
|
|
|
|
|
scrobble.refresh_from_db()
|
|
|
|
|
assert scrobble.visibility == "shared"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_change_visibility_non_owner_gets_404(client):
|
|
|
|
|
owner = get_user_model().objects.create_user(
|
|
|
|
|
username="owner", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
other = get_user_model().objects.create_user(
|
|
|
|
|
username="other", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=owner, visibility="private",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
client.force_login(other)
|
|
|
|
|
url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.post(url, {"visibility": "shared"})
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
scrobble.refresh_from_db()
|
|
|
|
|
assert scrobble.visibility == "private"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_change_visibility_anonymous_redirects_to_login(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="anontest", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="private",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.post(url, {"visibility": "shared"})
|
|
|
|
|
assert response.status_code == 302
|
|
|
|
|
assert "/login/" in response.url
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_regenerate_share_token_invalidates_old_sqid(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="regentest", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
old_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)
|
|
|
|
|
|
|
|
|
|
client.force_login(user)
|
|
|
|
|
url = reverse("scrobbles:regenerate-share-token", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.post(url)
|
|
|
|
|
assert response.status_code == 302
|
|
|
|
|
|
|
|
|
|
scrobble.refresh_from_db()
|
|
|
|
|
assert scrobble.share_token_version == 1
|
|
|
|
|
|
|
|
|
|
old_url = reverse("scrobbles:shared-detail", kwargs={"sqid": old_sqid})
|
|
|
|
|
old_response = client.get(old_url)
|
|
|
|
|
assert old_response.status_code == 404
|
|
|
|
|
|
|
|
|
|
new_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)
|
|
|
|
|
new_url = reverse("scrobbles:shared-detail", kwargs={"sqid": new_sqid})
|
|
|
|
|
new_response = client.get(new_url)
|
|
|
|
|
assert new_response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_analytics_owner_can_view(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="analyticsuser", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
client.force_login(user)
|
|
|
|
|
url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_analytics_non_owner_gets_404(client):
|
|
|
|
|
owner = get_user_model().objects.create_user(
|
|
|
|
|
username="analyticsowner", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
other = get_user_model().objects.create_user(
|
|
|
|
|
username="analyticsother", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=owner, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
client.force_login(other)
|
|
|
|
|
url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_share_analytics_shows_view_logs(client):
|
|
|
|
|
user = get_user_model().objects.create_user(
|
|
|
|
|
username="analyticsviews", password="testpass"
|
|
|
|
|
)
|
|
|
|
|
task = Task.objects.create(title="Test Task")
|
|
|
|
|
scrobble = Scrobble.objects.create(
|
|
|
|
|
task=task, media_type="Task", user=user, visibility="shared",
|
|
|
|
|
timestamp=timezone.now(),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)
|
|
|
|
|
share_url = reverse("scrobbles:shared-detail", kwargs={"sqid": sqid})
|
|
|
|
|
client.get(share_url)
|
|
|
|
|
client.get(share_url)
|
|
|
|
|
|
|
|
|
|
client.force_login(user)
|
|
|
|
|
url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid})
|
|
|
|
|
response = client.get(url)
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
content = response.content.decode()
|
|
|
|
|
assert "127.0.0.1" in content
|
|
|
|
|
assert ShareViewLog.objects.filter(scrobble=scrobble).count() == 2
|
|
|
|
|
|