/
/
/
1"""Tests that one-off items never reach the playlog, whichever provider owns them."""
2
3from __future__ import annotations
4
5from collections.abc import Mapping
6from typing import Any
7from uuid import uuid4
8
9from music_assistant_models.enums import MediaType
10from music_assistant_models.media_items import AudioSource, ProviderMapping, SoundEffect, Track
11
12from music_assistant.constants import DB_TABLE_PLAYLOG
13from music_assistant.mass import MusicAssistant
14
15
16def _provider_mapping(provider: str, item_id: str) -> set[ProviderMapping]:
17 """Create a single provider mapping for the given provider and item id."""
18 return {
19 ProviderMapping(
20 item_id=item_id,
21 provider_domain=provider,
22 provider_instance=provider,
23 )
24 }
25
26
27async def _playlog_rows(
28 mass: MusicAssistant, media_type: MediaType, item_id: str
29) -> list[Mapping[str, Any]]:
30 """Return every playlog row for the given media type and item id."""
31 return await mass.music.database.get_rows(
32 DB_TABLE_PLAYLOG,
33 {"media_type": media_type.value, "item_id": item_id},
34 )
35
36
37async def test_sound_effect_from_other_provider_gets_no_playlog_row(
38 mass: MusicAssistant,
39) -> None:
40 """A sound effect is a one-off clip and stays out of the playlog whoever owns it."""
41 user = await mass.webserver.auth.create_user("effectother")
42 effect = SoundEffect(
43 item_id="doorbell",
44 provider="test_effects--instance",
45 name="Doorbell",
46 provider_mappings=_provider_mapping("test_effects--instance", "doorbell"),
47 )
48
49 await mass.music.mark_item_played(effect, fully_played=True, userid=user.user_id)
50
51 assert not await _playlog_rows(mass, MediaType.SOUND_EFFECT, effect.item_id)
52
53
54async def test_sound_effect_from_builtin_provider_gets_no_playlog_row(
55 mass: MusicAssistant,
56) -> None:
57 """The pre-existing builtin behaviour is unchanged."""
58 user = await mass.webserver.auth.create_user("effectbuiltin")
59 effect = SoundEffect(
60 item_id="http://example.com/intro.mp3",
61 provider="builtin",
62 name="Intro",
63 provider_mappings=_provider_mapping("builtin", "http://example.com/intro.mp3"),
64 )
65
66 await mass.music.mark_item_played(effect, fully_played=True, userid=user.user_id)
67
68 assert not await _playlog_rows(mass, MediaType.SOUND_EFFECT, effect.item_id)
69
70
71async def test_audio_source_gets_no_playlog_row(mass: MusicAssistant) -> None:
72 """A live input is not a catalogue item, so it never lands in the playlog either."""
73 user = await mass.webserver.auth.create_user("audiosourceplugin")
74 source = AudioSource(
75 item_id="main",
76 provider="test_receiver--instance",
77 name="Test Receiver",
78 provider_mappings=_provider_mapping("test_receiver--instance", "main"),
79 )
80
81 await mass.music.mark_item_played(source, fully_played=True, userid=user.user_id)
82
83 assert not await _playlog_rows(mass, MediaType.AUDIO_SOURCE, source.item_id)
84
85
86async def test_regular_provider_track_still_gets_playlog_row(mass: MusicAssistant) -> None:
87 """Control case: the media-type guard must not suppress ordinary provider items."""
88 user = await mass.webserver.auth.create_user("regulartrack")
89 item_id = uuid4().hex
90 track = Track(
91 item_id=item_id,
92 provider="test_music_prov",
93 name="Some Track",
94 provider_mappings=_provider_mapping("test_music_prov", item_id),
95 )
96
97 await mass.music.mark_item_played(track, fully_played=True, userid=user.user_id)
98
99 assert await _playlog_rows(mass, MediaType.TRACK, item_id)
100