test_views.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. from datetime import datetime, timedelta
  2. from unittest.mock import patch
  3. from django.utils import timezone
  4. import pytest
  5. import time_machine
  6. from django.urls import reverse
  7. from music.models import Track
  8. from podcasts.models import PodcastEpisode
  9. from scrobbles.models import Scrobble
  10. @pytest.mark.django_db
  11. def test_get_not_allowed_from_mopidy(client, valid_auth_token):
  12. url = reverse("scrobbles:mopidy-webhook")
  13. headers = {"Authorization": f"Token {valid_auth_token}"}
  14. response = client.get(url, headers=headers)
  15. assert response.status_code == 405
  16. @pytest.mark.django_db
  17. def test_bad_mopidy_request_data(client, valid_auth_token):
  18. url = reverse("scrobbles:mopidy-webhook")
  19. headers = {"Authorization": f"Token {valid_auth_token}"}
  20. response = client.post(url, headers)
  21. assert response.status_code == 400
  22. assert (
  23. response.data["detail"]
  24. == "JSON parse error - Expecting value: line 1 column 1 (char 0)"
  25. )
  26. @pytest.mark.parametrize(
  27. "seconds, expected_percent_played, expected_scrobble_id",
  28. [
  29. (1, 1, 1),
  30. (58, 96, 1),
  31. (59, 98, 1),
  32. (60, 100, 1),
  33. (1, 1, 1),
  34. (1, 1, 1),
  35. ],
  36. )
  37. @pytest.mark.django_db
  38. def test_scrobble_mopidy_track(
  39. client,
  40. mopidy_track,
  41. valid_auth_token,
  42. seconds,
  43. expected_percent_played,
  44. expected_scrobble_id,
  45. ):
  46. url = reverse("scrobbles:mopidy-webhook")
  47. headers = {"Authorization": f"Token {valid_auth_token}"}
  48. # Start new scrobble
  49. minutes = 0
  50. calc_seconds = seconds
  51. if seconds >= 60:
  52. minutes = 1
  53. calc_seconds = calc_seconds % 10
  54. with time_machine.travel(datetime(2024, 1, 14, 12, minutes, calc_seconds)):
  55. mopidy_track.request_data["playback_time_ticks"] = seconds * 1000
  56. response = client.post(
  57. url,
  58. mopidy_track.request_json,
  59. content_type="application/json",
  60. headers=headers,
  61. )
  62. assert response.status_code == 200
  63. assert response.data == {"scrobble_id": expected_scrobble_id}
  64. scrobble = Scrobble.objects.get(id=1)
  65. assert scrobble.percent_played == expected_percent_played
  66. assert scrobble.media_obj.__class__ == Track
  67. assert scrobble.media_obj.title == "Same in the End"
  68. @pytest.mark.skip(reason="Allmusic API is unstable")
  69. @pytest.mark.django_db
  70. def test_scrobble_mopidy_same_track_different_album(
  71. client,
  72. mopidy_track,
  73. mopidy_track_diff_album_request_data,
  74. valid_auth_token,
  75. ):
  76. url = reverse("scrobbles:mopidy-webhook")
  77. headers = {"Authorization": f"Token {valid_auth_token}"}
  78. response = client.post(
  79. url,
  80. mopidy_track.request_data,
  81. content_type="application/json",
  82. headers=headers,
  83. )
  84. assert response.status_code == 200
  85. assert response.data == {"scrobble_id": 1}
  86. scrobble = Scrobble.objects.last()
  87. assert scrobble.media_obj.album.name == "Sublime"
  88. response = client.post(
  89. url,
  90. mopidy_track_diff_album_request_data,
  91. content_type="application/json",
  92. )
  93. assert response.status_code == 200
  94. assert response.data == {"scrobble_id": 2}
  95. scrobble = Scrobble.objects.last()
  96. assert scrobble.media_obj.__class__ == Track
  97. assert scrobble.media_obj.album.name == "Gold"
  98. assert scrobble.media_obj.title == "Same in the End"
  99. @pytest.mark.skip("Need to add a mock podcast request data, tho Google Podcasts is gone :thinking:")
  100. @pytest.mark.django_db
  101. def test_scrobble_mopidy_podcast(
  102. client, mopidy_podcast_request_data, valid_auth_token
  103. ):
  104. url = reverse("scrobbles:mopidy-webhook")
  105. headers = {"Authorization": f"Token {valid_auth_token}"}
  106. response = client.post(
  107. url,
  108. mopidy_podcast_request_data,
  109. content_type="application/json",
  110. headers=headers,
  111. )
  112. assert response.status_code == 200
  113. assert response.data == {"scrobble_id": 1}
  114. scrobble = Scrobble.objects.get(id=1)
  115. assert scrobble.media_obj.__class__ == PodcastEpisode
  116. assert scrobble.media_obj.title == "Up First"
  117. @pytest.mark.django_db
  118. @patch("music.utils.lookup_artist_from_mb", return_value={})
  119. @patch(
  120. "music.utils.lookup_album_dict_from_mb",
  121. return_value={"year": "1999", "mb_group_id": 1},
  122. )
  123. @patch("music.utils.lookup_track_from_mb", return_value={})
  124. @patch("music.models.lookup_artist_from_tadb", return_value={})
  125. @patch("music.models.lookup_album_from_tadb", return_value={"year": "1999"})
  126. @patch("music.models.Album.fetch_artwork", return_value=None)
  127. @patch("music.models.Album.scrape_allmusic", return_value=None)
  128. def test_scrobble_jellyfin_track(
  129. mock_lookup_artist,
  130. mock_lookup_album,
  131. mock_lookup_track,
  132. mock_lookup_artist_tadb,
  133. mock_lookup_album_tadb,
  134. mock_fetch_artwork,
  135. mock_scrape_allmusic,
  136. client,
  137. jellyfin_track,
  138. valid_auth_token,
  139. ):
  140. url = reverse("scrobbles:jellyfin-webhook")
  141. headers = {"Authorization": f"Token {valid_auth_token}"}
  142. with time_machine.travel(datetime(2024, 1, 14, 12, 00, 1)):
  143. jellyfin_track.request_data["UtcTimestamp"] = timezone.now().strftime(
  144. "%Y-%m-%d %H:%M:%S"
  145. )
  146. response = client.post(
  147. url,
  148. jellyfin_track.request_json,
  149. content_type="application/json",
  150. headers=headers,
  151. )
  152. assert response.status_code == 200
  153. assert response.data == {"scrobble_id": 1}
  154. scrobble = Scrobble.objects.get(id=1)
  155. assert scrobble.media_obj.__class__ == Track
  156. assert scrobble.media_obj.title == "Emotion"
  157. with time_machine.travel(datetime(2024, 1, 14, 12, 0, 58)):
  158. jellyfin_track.request_data["UtcTimestamp"] = timezone.now().strftime(
  159. "%Y-%m-%d %H:%M:%S"
  160. )
  161. response = client.post(
  162. url,
  163. jellyfin_track.request_json,
  164. content_type="application/json",
  165. headers=headers,
  166. )
  167. assert response.status_code == 200
  168. assert response.data == {"scrobble_id": 1}
  169. scrobble = Scrobble.objects.get(id=1)
  170. assert scrobble.media_obj.__class__ == Track
  171. assert scrobble.media_obj.title == "Emotion"
  172. with time_machine.travel(datetime(2024, 1, 14, 12, 1, 1)):
  173. jellyfin_track.request_data["UtcTimestamp"] = timezone.now().strftime(
  174. "%Y-%m-%d %H:%M:%S"
  175. )
  176. response = client.post(
  177. url,
  178. jellyfin_track.request_json,
  179. content_type="application/json",
  180. headers=headers,
  181. )
  182. assert response.status_code == 200
  183. assert response.data == {"scrobble_id": 2}
  184. scrobble = Scrobble.objects.get(id=1)
  185. assert scrobble.media_obj.__class__ == Track
  186. assert scrobble.media_obj.title == "Emotion"