1- """Tests for the server-info resource."""
1+ """Tests for the server-info resource.
2+
3+ Identity resolution (metadata-first, foreign-pyproject guard, fallback) lives
4+ in the package root now and is tested in ``test_version_resolution.py``. These
5+ tests cover the resource contract and that the resource *consumes* that single
6+ source of truth rather than re-deriving identity on its own.
7+ """
28
39import json
410import sys
511import tomllib
612from pathlib import Path
713
14+ import my_mcp_server
815from my_mcp_server .resources .server_info import (
916 DESCRIPTION ,
1017 MIME_TYPE ,
1118 NAME ,
19+ PKG_NAME ,
1220 URI ,
1321 server_info ,
1422)
@@ -41,15 +49,40 @@ async def test_returns_json_with_expected_shape() -> None:
4149 assert isinstance (payload ["version" ], str ) and payload ["version" ]
4250
4351
44- async def test_version_matches_pyproject () -> None :
45- """Version field reflects pyproject.toml, not a hardcoded constant."""
52+ async def test_name_and_version_come_from_package_single_source () -> None :
53+ """The resource reports the package's own identity, not a re-derivation.
54+
55+ ``name`` is the package's ``DIST_NAME`` and ``version`` is whatever
56+ ``resolve_version()`` returns — so the value the server reports can never
57+ drift from the value the package is.
58+ """
59+ payload = json .loads (await server_info ())
60+ assert payload ["name" ] == my_mcp_server .DIST_NAME
61+ assert payload ["version" ] == my_mcp_server .resolve_version ()
62+
63+
64+ async def test_version_matches_pyproject_for_editable_install () -> None :
65+ """For this source checkout (installed editable in CI), the metadata-first
66+ version coincides with pyproject's — a regression guard that the reported
67+ version is the real one, not a hardcoded constant."""
4668 project = _pyproject_project ()
4769 payload = json .loads (await server_info ())
4870
4971 assert payload ["name" ] == project ["name" ]
5072 assert payload ["version" ] == project ["version" ]
5173
5274
75+ async def test_resource_tracks_resolve_version (monkeypatch ) -> None :
76+ """If ``resolve_version()`` changes, the resource changes with it — proves
77+ the resource routes through the single source instead of reading metadata
78+ or pyproject on its own."""
79+ from my_mcp_server .resources import server_info as mod
80+
81+ monkeypatch .setattr (mod , "resolve_version" , lambda : "9.9.9-sentinel" )
82+ payload = json .loads (await mod .server_info ())
83+ assert payload ["version" ] == "9.9.9-sentinel"
84+
85+
5386async def test_runtime_reflects_current_interpreter () -> None :
5487 """Python version in runtime matches sys.version."""
5588 payload = json .loads (await server_info ())
@@ -65,115 +98,9 @@ async def test_registered_on_server() -> None:
6598 assert URI in uris
6699
67100
68- # --- wheel-install fallback path ---------------------------------------------
69- # When the package is installed from a wheel, pyproject.toml is NOT shipped,
70- # so `_read_pyproject()` returns None and `_server_metadata()` must fall back
71- # to `importlib.metadata.version()`. The tests above all exercise the source
72- # tree (where pyproject.toml IS reachable).
73-
74-
75- async def test_falls_back_to_importlib_metadata_when_pyproject_missing (
76- monkeypatch ,
77- ) -> None :
78- """Wheel install: _read_pyproject() returns None, both name and version
79- come from importlib.metadata (both sides of the equality below resolve
80- against the SAME source, not pyproject-on-disk — so a maintainer who
81- bumps pyproject.toml's version without re-running ``pip install -e .``
82- won't see a misleading failure here)."""
83- from importlib .metadata import version as imp_version
84-
85- from my_mcp_server .resources import server_info as mod
86-
87- monkeypatch .setattr (mod , "_read_pyproject" , lambda : None )
88- payload = json .loads (await mod .server_info ())
89-
90- # `mod.PKG_NAME` is derived from `__package__` — survives a clone rename
91- # without re-hardcoding the literal in the test.
92- assert payload ["name" ] == mod .PKG_NAME
93- assert payload ["version" ] == imp_version (mod .PKG_NAME )
94-
95-
96- async def test_falls_back_to_zero_version_when_package_not_installed (
97- monkeypatch ,
98- ) -> None :
99- """Edge of the edge: pyproject missing AND importlib.metadata can't find
100- the dist. Should return FALLBACK_VERSION instead of raising
101- PackageNotFoundError."""
102- from my_mcp_server .resources import server_info as mod
103-
104- def raise_not_found (* _args : object , ** _kw : object ) -> str :
105- # Tolerant signature in case `version()` ever gets called with kwargs.
106- raise mod .PackageNotFoundError ("simulated wheel-install-without-metadata" )
107-
108- monkeypatch .setattr (mod , "_read_pyproject" , lambda : None )
109- monkeypatch .setattr (mod , "version" , raise_not_found )
110-
111- payload = json .loads (await mod .server_info ())
112- assert payload ["version" ] == mod .FALLBACK_VERSION
113-
114-
115- def test_read_pyproject_returns_none_when_project_table_missing (tmp_path , monkeypatch ) -> None :
116- """Walk-up finds a pyproject.toml with no [project] table — must exit at
117- the inner `return None` (NOT continue walking — that's the contract).
118-
119- Strengthened with a tomllib.load spy so a future refactor that drops the
120- explicit inner return and falls through to keep walking gets caught here
121- (the spy would see >1 load) instead of silently passing because no other
122- pyproject.toml is reachable on the way to /.
123- """
124- from my_mcp_server .resources import server_info as mod
125-
126- pkg = tmp_path / "pkg"
127- pkg .mkdir ()
128- fake_pyproject = tmp_path / "pyproject.toml"
129- fake_pyproject .write_text ("[build-system]\n requires = []\n " )
130- fake_file = pkg / "server_info.py"
131- fake_file .write_text ("" )
132-
133- loaded_paths : list [str ] = []
134- original_load = mod .tomllib .load
135-
136- def spy_load (fh ): # type: ignore[no-untyped-def]
137- loaded_paths .append (getattr (fh , "name" , "" ))
138- return original_load (fh )
139-
140- monkeypatch .setattr (mod .tomllib , "load" , spy_load )
141- monkeypatch .setattr (mod , "__file__" , str (fake_file ))
142-
143- assert mod ._read_pyproject () is None
144- assert loaded_paths == [str (fake_pyproject )], (
145- f"_read_pyproject must stop at the first pyproject.toml encountered; "
146- f"saw { len (loaded_paths )} load(s): { loaded_paths } "
147- )
148-
149-
150- def test_read_pyproject_returns_none_when_no_pyproject_anywhere (tmp_path , monkeypatch ) -> None :
151- """Walk-up exhausts the parent chain without finding any pyproject.toml —
152- exercises the outer `return None` after the loop (server_info.py:42).
153-
154- Without this test the loop-exhaustion path is 0% covered: the
155- "fallback" tests above bypass the walk entirely by monkeypatching
156- `_read_pyproject` itself.
157- """
158- from my_mcp_server .resources import server_info as mod
159-
160- pkg = tmp_path / "pkg"
161- pkg .mkdir ()
162- fake_file = pkg / "server_info.py"
163- fake_file .write_text ("" )
164- # Deliberately no pyproject.toml anywhere in tmp_path or its ancestors
165- # (system tmp dirs don't contain one), so the walk reaches root.
166-
167- monkeypatch .setattr (mod , "__file__" , str (fake_file ))
168- assert mod ._read_pyproject () is None
169-
170-
171- def test_pkg_name_derived_from_package () -> None :
172- """PKG_NAME is the dist-name form (hyphens) of the import package name —
173- pins the rename-safety contract documented in server_info.py."""
174- from my_mcp_server .resources import server_info as mod
175-
176- assert mod .PKG_NAME == "my-mcp-server"
177- # Sanity: derivation tracks __package__, not a stray literal somewhere.
178- root_import = (mod .__package__ or "" ).split ("." )[0 ]
179- assert root_import .replace ("_" , "-" ) == mod .PKG_NAME
101+ def test_pkg_name_is_back_compat_alias_of_dist_name () -> None :
102+ """``PKG_NAME`` is retained as a back-compat alias of the package's
103+ ``DIST_NAME`` (the dist-name form, hyphens) so external importers of
104+ ``server_info.PKG_NAME`` keep working after identity moved to the root."""
105+ assert PKG_NAME == "my-mcp-server"
106+ assert PKG_NAME == my_mcp_server .DIST_NAME
0 commit comments