test_views.py 7.3 KB

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