/
/
/
1"""
2Invariant: no builtin or default-auto-created provider ships a ``setup_flow.py``.
3
4Builtin providers and the providers in ``DEFAULT_PROVIDERS`` are created automatically at
5startup with empty values (``create_builtin_provider_config`` / the default-providers pass)
6and loaded without ever running an interactive setup flow. A ``setup_flow.py`` on such a
7provider would therefore never run, so it must not exist. Enforced here so that adding a
8provider to ``DEFAULT_PROVIDERS`` (or marking it ``builtin``) can't silently ship a setup
9flow that the auto-create path bypasses.
10"""
11
12from __future__ import annotations
13
14import json
15from pathlib import Path
16
17from music_assistant.constants import DEFAULT_PROVIDERS
18from music_assistant.mass import PROVIDERS_PATH
19
20
21def _auto_created_provider_domains() -> set[str]:
22 """Return the domains of all providers that are auto-created without a setup flow."""
23 domains = {domain for domain, _ in DEFAULT_PROVIDERS}
24 for manifest_path in Path(PROVIDERS_PATH).glob("*/manifest.json"):
25 data = json.loads(manifest_path.read_text())
26 if data.get("builtin"):
27 # the directory name is the provider domain
28 domains.add(manifest_path.parent.name)
29 return domains
30
31
32def test_builtin_and_default_providers_have_no_setup_flow() -> None:
33 """Builtin/default (auto-created) providers must not ship a setup_flow.py."""
34 offenders = [
35 domain
36 for domain in sorted(_auto_created_provider_domains())
37 if (Path(PROVIDERS_PATH) / domain / "setup_flow.py").exists()
38 ]
39 assert not offenders, (
40 "builtin/default providers are auto-created without an interactive setup, so they "
41 f"must not ship a setup_flow.py (it would never run): {offenders}"
42 )
43