mcp-api-test 0.1.4__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcp_api_test-0.1.4/src/mcp_api_test.egg-info → mcp_api_test-0.2.0}/PKG-INFO +5 -2
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/README.md +2 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/pyproject.toml +18 -6
- mcp_api_test-0.2.0/src/mcp_api_test/__init__.py +3 -0
- {mcp_api_test-0.1.4/src → mcp_api_test-0.2.0/src/mcp_api_test}/__main__.py +1 -1
- {mcp_api_test-0.1.4/src → mcp_api_test-0.2.0/src/mcp_api_test}/flags.py +1 -1
- {mcp_api_test-0.1.4/src → mcp_api_test-0.2.0/src/mcp_api_test}/logger.py +4 -3
- mcp_api_test-0.2.0/src/mcp_api_test/py.typed +0 -0
- {mcp_api_test-0.1.4/src → mcp_api_test-0.2.0/src/mcp_api_test}/server.py +27 -19
- {mcp_api_test-0.1.4/src → mcp_api_test-0.2.0/src/mcp_api_test}/utils.py +1 -1
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0/src/mcp_api_test.egg-info}/PKG-INFO +5 -2
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/src/mcp_api_test.egg-info/SOURCES.txt +9 -6
- mcp_api_test-0.2.0/src/mcp_api_test.egg-info/entry_points.txt +2 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/src/mcp_api_test.egg-info/requires.txt +2 -1
- mcp_api_test-0.2.0/src/mcp_api_test.egg-info/top_level.txt +1 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/tests/test_mcp_api_tools.py +1 -1
- mcp_api_test-0.2.0/tests/test_mcp_server_compat.py +30 -0
- mcp_api_test-0.2.0/tests/test_packaging.py +49 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/tests/test_parse_curl_command.py +1 -2
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/tests/test_parse_status_code.py +1 -1
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/tests/test_utils.py +1 -1
- mcp_api_test-0.1.4/src/mcp_api_test.egg-info/entry_points.txt +0 -2
- mcp_api_test-0.1.4/src/mcp_api_test.egg-info/top_level.txt +0 -6
- mcp_api_test-0.1.4/tests/test_packaging.py +0 -46
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/LICENSE +0 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/MANIFEST.in +0 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/setup.cfg +0 -0
- {mcp_api_test-0.1.4/src → mcp_api_test-0.2.0/src/mcp_api_test}/models.py +0 -0
- {mcp_api_test-0.1.4 → mcp_api_test-0.2.0}/src/mcp_api_test.egg-info/dependency_links.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-api-test
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: MCP server for testing REST API with built-in assertions
|
|
5
5
|
Author: Ryan Febriansyah
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,13 +18,14 @@ Classifier: Topic :: Software Development :: Testing
|
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist: mcp<
|
|
21
|
+
Requires-Dist: mcp<3.0.0,>=2.0.0
|
|
22
22
|
Requires-Dist: httpx
|
|
23
23
|
Requires-Dist: python-dotenv
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
27
27
|
Requires-Dist: respx; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy; extra == "dev"
|
|
28
29
|
Provides-Extra: docs
|
|
29
30
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
30
31
|
Dynamic: license-file
|
|
@@ -39,6 +40,8 @@ Unlike traditional testing frameworks, `mcp-api-test` exposes API testing as a *
|
|
|
39
40
|
|
|
40
41
|
It complements (rather than replaces) tools like pytest, Postman, and Playwright.
|
|
41
42
|
|
|
43
|
+
> **Breaking changes in v0.2.0.** The server now runs on `mcp` 2.x, and the package layout moved from flat top-level modules to a proper `mcp_api_test` package. If you import internals directly, you may update to `from mcp_api_test.server import ...`. MCP client configuration (OpenCode, Claude Desktop, Claude Code) is unaffected. See the [changelog](docs/changelog.md) for details.
|
|
44
|
+
|
|
42
45
|
## Use cases
|
|
43
46
|
|
|
44
47
|
- **QA & test engineering**: prompt an agent with natural language instead of crafting Postman requests
|
|
@@ -8,6 +8,8 @@ Unlike traditional testing frameworks, `mcp-api-test` exposes API testing as a *
|
|
|
8
8
|
|
|
9
9
|
It complements (rather than replaces) tools like pytest, Postman, and Playwright.
|
|
10
10
|
|
|
11
|
+
> **Breaking changes in v0.2.0.** The server now runs on `mcp` 2.x, and the package layout moved from flat top-level modules to a proper `mcp_api_test` package. If you import internals directly, you may update to `from mcp_api_test.server import ...`. MCP client configuration (OpenCode, Claude Desktop, Claude Code) is unaffected. See the [changelog](docs/changelog.md) for details.
|
|
12
|
+
|
|
11
13
|
## Use cases
|
|
12
14
|
|
|
13
15
|
- **QA & test engineering**: prompt an agent with natural language instead of crafting Postman requests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mcp-api-test"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "MCP server for testing REST API with built-in assertions"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -10,7 +10,7 @@ authors = [
|
|
|
10
10
|
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
dependencies = [
|
|
13
|
-
"mcp>=
|
|
13
|
+
"mcp>=2.0.0,<3.0.0",
|
|
14
14
|
"httpx",
|
|
15
15
|
"python-dotenv"
|
|
16
16
|
]
|
|
@@ -31,7 +31,7 @@ classifiers = [
|
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[project.scripts]
|
|
34
|
-
mcp-api-test = "server:main"
|
|
34
|
+
mcp-api-test = "mcp_api_test.server:main"
|
|
35
35
|
|
|
36
36
|
[build-system]
|
|
37
37
|
requires = ["setuptools>=61"]
|
|
@@ -39,8 +39,20 @@ build-backend = "setuptools.build_meta"
|
|
|
39
39
|
|
|
40
40
|
[tool.setuptools]
|
|
41
41
|
package-dir = {"" = "src"}
|
|
42
|
-
|
|
42
|
+
packages = ["mcp_api_test"]
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.package-data]
|
|
45
|
+
mcp_api_test = ["py.typed"]
|
|
43
46
|
|
|
44
47
|
[project.optional-dependencies]
|
|
45
|
-
dev = ["pytest", "pytest-asyncio", "respx"]
|
|
46
|
-
docs = ["mkdocs-material"]
|
|
48
|
+
dev = ["pytest", "pytest-asyncio", "respx", "mypy"]
|
|
49
|
+
docs = ["mkdocs-material"]
|
|
50
|
+
|
|
51
|
+
[tool.mypy]
|
|
52
|
+
python_version = "3.10"
|
|
53
|
+
packages = ["mcp_api_test"]
|
|
54
|
+
check_untyped_defs = true
|
|
55
|
+
disallow_untyped_defs = true
|
|
56
|
+
no_implicit_optional = true
|
|
57
|
+
warn_redundant_casts = true
|
|
58
|
+
warn_unused_ignores = true
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class Logger:
|
|
@@ -31,7 +32,7 @@ class Logger:
|
|
|
31
32
|
|
|
32
33
|
self.logger.addHandler(file_handler)
|
|
33
34
|
|
|
34
|
-
def info(self, message: str, *args, **kwargs) -> None:
|
|
35
|
+
def info(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
35
36
|
"""
|
|
36
37
|
Log an info message with particular arguments
|
|
37
38
|
"""
|
|
@@ -39,7 +40,7 @@ class Logger:
|
|
|
39
40
|
message = message % args
|
|
40
41
|
self.logger.info(message, **kwargs)
|
|
41
42
|
|
|
42
|
-
def warning(self, message: str, *args, **kwargs) -> None:
|
|
43
|
+
def warning(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
43
44
|
"""
|
|
44
45
|
Log a warning message with particular arguments
|
|
45
46
|
"""
|
|
@@ -47,7 +48,7 @@ class Logger:
|
|
|
47
48
|
message = message % args
|
|
48
49
|
self.logger.warning(message, **kwargs)
|
|
49
50
|
|
|
50
|
-
def error(self, message: str, *args, **kwargs) -> None:
|
|
51
|
+
def error(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
51
52
|
"""
|
|
52
53
|
Log an error message with particular arguments
|
|
53
54
|
"""
|
|
File without changes
|
|
@@ -2,26 +2,26 @@ import json
|
|
|
2
2
|
import shlex
|
|
3
3
|
import time
|
|
4
4
|
import httpx
|
|
5
|
-
from logger import get_logger
|
|
6
5
|
from dotenv import load_dotenv
|
|
7
6
|
from urllib.parse import urlparse
|
|
8
7
|
from typing import Optional, Dict, Any, List
|
|
9
|
-
from mcp.server.
|
|
10
|
-
from
|
|
11
|
-
from
|
|
8
|
+
from mcp.server.mcpserver import MCPServer
|
|
9
|
+
from .logger import get_logger
|
|
10
|
+
from .models import APITestResult, AssertionResult
|
|
11
|
+
from .utils import (
|
|
12
12
|
render_console_template_result,
|
|
13
13
|
render_error_result,
|
|
14
14
|
build_fullpath_url,
|
|
15
15
|
get_nested_fields,
|
|
16
16
|
)
|
|
17
|
-
from flags import CURL_BOOLEAN_FLAGS, CURL_VALUE_FLAGS, CURL_WARN_FLAGS
|
|
17
|
+
from .flags import CURL_BOOLEAN_FLAGS, CURL_VALUE_FLAGS, CURL_WARN_FLAGS
|
|
18
18
|
|
|
19
19
|
load_dotenv()
|
|
20
20
|
|
|
21
21
|
logger = get_logger(name="mcp-api-testing")
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
mcp =
|
|
24
|
+
mcp = MCPServer("mcp-api-test")
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def parse_status_code_range(range_str: str) -> tuple[int, int]:
|
|
@@ -162,7 +162,7 @@ async def run_test_api(
|
|
|
162
162
|
required_status_code_range: Optional[str] = None,
|
|
163
163
|
required_response_fields: Optional[List[str]] = None,
|
|
164
164
|
required_response_contains: Optional[str] = None,
|
|
165
|
-
):
|
|
165
|
+
) -> str:
|
|
166
166
|
"""
|
|
167
167
|
Test a given API services, it will supports either:
|
|
168
168
|
|
|
@@ -192,19 +192,27 @@ async def run_test_api(
|
|
|
192
192
|
full_path_url = build_fullpath_url(base_url=base_url, endpoint=endpoint)
|
|
193
193
|
method = method.upper()
|
|
194
194
|
|
|
195
|
+
content: Optional[str] = None
|
|
196
|
+
json_payload: Optional[Dict[str, Any]] = None
|
|
197
|
+
if payload is not None:
|
|
198
|
+
# cURL parser may produce a raw string for non-JSON -d payloads
|
|
199
|
+
# (example: form-encoded data). Use content= to send as-is rather
|
|
200
|
+
# than json= which would double-encode the string.
|
|
201
|
+
if isinstance(payload, str):
|
|
202
|
+
content = payload
|
|
203
|
+
else:
|
|
204
|
+
json_payload = payload
|
|
205
|
+
|
|
195
206
|
start_time = time.perf_counter()
|
|
196
207
|
try:
|
|
197
208
|
async with httpx.AsyncClient(timeout=30) as client:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
else:
|
|
206
|
-
kwargs["json"] = payload
|
|
207
|
-
response = await client.request(**kwargs)
|
|
209
|
+
response = await client.request(
|
|
210
|
+
method=method,
|
|
211
|
+
url=full_path_url,
|
|
212
|
+
headers=headers,
|
|
213
|
+
content=content,
|
|
214
|
+
json=json_payload,
|
|
215
|
+
)
|
|
208
216
|
end_time = time.perf_counter()
|
|
209
217
|
response_time_ms = (end_time - start_time) * 1000
|
|
210
218
|
except Exception as e:
|
|
@@ -223,7 +231,7 @@ async def run_test_api(
|
|
|
223
231
|
)
|
|
224
232
|
resp_body = response.text
|
|
225
233
|
|
|
226
|
-
assertions = []
|
|
234
|
+
assertions: List[Dict[str, Any]] = []
|
|
227
235
|
|
|
228
236
|
if required_status_code is not None:
|
|
229
237
|
passed = response.status_code == required_status_code
|
|
@@ -321,7 +329,7 @@ async def run_test_api(
|
|
|
321
329
|
|
|
322
330
|
|
|
323
331
|
# expose this function so it can be called in toml files
|
|
324
|
-
def main():
|
|
332
|
+
def main() -> None:
|
|
325
333
|
mcp.run()
|
|
326
334
|
|
|
327
335
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-api-test
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: MCP server for testing REST API with built-in assertions
|
|
5
5
|
Author: Ryan Febriansyah
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,13 +18,14 @@ Classifier: Topic :: Software Development :: Testing
|
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
-
Requires-Dist: mcp<
|
|
21
|
+
Requires-Dist: mcp<3.0.0,>=2.0.0
|
|
22
22
|
Requires-Dist: httpx
|
|
23
23
|
Requires-Dist: python-dotenv
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
27
27
|
Requires-Dist: respx; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy; extra == "dev"
|
|
28
29
|
Provides-Extra: docs
|
|
29
30
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
30
31
|
Dynamic: license-file
|
|
@@ -39,6 +40,8 @@ Unlike traditional testing frameworks, `mcp-api-test` exposes API testing as a *
|
|
|
39
40
|
|
|
40
41
|
It complements (rather than replaces) tools like pytest, Postman, and Playwright.
|
|
41
42
|
|
|
43
|
+
> **Breaking changes in v0.2.0.** The server now runs on `mcp` 2.x, and the package layout moved from flat top-level modules to a proper `mcp_api_test` package. If you import internals directly, you may update to `from mcp_api_test.server import ...`. MCP client configuration (OpenCode, Claude Desktop, Claude Code) is unaffected. See the [changelog](docs/changelog.md) for details.
|
|
44
|
+
|
|
42
45
|
## Use cases
|
|
43
46
|
|
|
44
47
|
- **QA & test engineering**: prompt an agent with natural language instead of crafting Postman requests
|
|
@@ -2,12 +2,14 @@ LICENSE
|
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.md
|
|
4
4
|
pyproject.toml
|
|
5
|
-
src/
|
|
6
|
-
src/
|
|
7
|
-
src/
|
|
8
|
-
src/
|
|
9
|
-
src/
|
|
10
|
-
src/
|
|
5
|
+
src/mcp_api_test/__init__.py
|
|
6
|
+
src/mcp_api_test/__main__.py
|
|
7
|
+
src/mcp_api_test/flags.py
|
|
8
|
+
src/mcp_api_test/logger.py
|
|
9
|
+
src/mcp_api_test/models.py
|
|
10
|
+
src/mcp_api_test/py.typed
|
|
11
|
+
src/mcp_api_test/server.py
|
|
12
|
+
src/mcp_api_test/utils.py
|
|
11
13
|
src/mcp_api_test.egg-info/PKG-INFO
|
|
12
14
|
src/mcp_api_test.egg-info/SOURCES.txt
|
|
13
15
|
src/mcp_api_test.egg-info/dependency_links.txt
|
|
@@ -15,6 +17,7 @@ src/mcp_api_test.egg-info/entry_points.txt
|
|
|
15
17
|
src/mcp_api_test.egg-info/requires.txt
|
|
16
18
|
src/mcp_api_test.egg-info/top_level.txt
|
|
17
19
|
tests/test_mcp_api_tools.py
|
|
20
|
+
tests/test_mcp_server_compat.py
|
|
18
21
|
tests/test_packaging.py
|
|
19
22
|
tests/test_parse_curl_command.py
|
|
20
23
|
tests/test_parse_status_code.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp_api_test
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Regression tests for the mcp 2.x migration (FastMCP -> MCPServer)"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from mcp_api_test.server import mcp
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_server_is_built_on_mcpserver():
|
|
8
|
+
# regression: mcp 2.x removed mcp.server.fastmcp.FastMCP in favor of
|
|
9
|
+
# mcp.server.mcpserver.MCPServer; importing the old path now raises
|
|
10
|
+
# ModuleNotFoundError instead of silently breaking at a later call site
|
|
11
|
+
from mcp.server.mcpserver import MCPServer
|
|
12
|
+
|
|
13
|
+
assert isinstance(mcp, MCPServer)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_old_fastmcp_import_path_is_gone():
|
|
17
|
+
# confirms we're actually running against the mcp 2.x line pinned in
|
|
18
|
+
# pyproject.toml, not an mcp 1.x install that would mask this migration
|
|
19
|
+
with pytest.raises(ModuleNotFoundError):
|
|
20
|
+
import mcp.server.fastmcp # noqa: F401
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_run_test_api_is_registered_as_a_tool():
|
|
24
|
+
# @mcp.tool() kept the same decorator signature across the migration;
|
|
25
|
+
# this would fail if MCPServer's tool manager changed shape
|
|
26
|
+
import asyncio
|
|
27
|
+
|
|
28
|
+
tools = asyncio.run(mcp.list_tools())
|
|
29
|
+
tool_names = {tool.name for tool in tools}
|
|
30
|
+
assert "run_test_api" in tool_names
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Static checks that the source layout matches the wheel packaging config"""
|
|
2
|
+
|
|
3
|
+
import tomllib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
7
|
+
PACKAGE_DIR = PROJECT_ROOT / "src" / "mcp_api_test"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _pyproject() -> dict:
|
|
11
|
+
with open(PROJECT_ROOT / "pyproject.toml", "rb") as f:
|
|
12
|
+
return tomllib.load(f)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _project_version() -> str:
|
|
16
|
+
return _pyproject()["project"]["version"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_declared_package_matches_src_directory():
|
|
20
|
+
packages = _pyproject()["tool"]["setuptools"]["packages"]
|
|
21
|
+
assert packages == ["mcp_api_test"]
|
|
22
|
+
assert PACKAGE_DIR.is_dir()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_all_modules_exist_in_package_dir():
|
|
26
|
+
# regression: flat py-modules layout drifting from the package dir
|
|
27
|
+
expected = {"server", "utils", "models", "logger", "flags", "__init__", "__main__"}
|
|
28
|
+
actual = {p.stem for p in PACKAGE_DIR.glob("*.py")}
|
|
29
|
+
assert expected <= actual
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_py_typed_marker_exists_and_is_packaged():
|
|
33
|
+
# PEP 561: inline-typed packages must ship a py.typed marker
|
|
34
|
+
assert (PACKAGE_DIR / "py.typed").exists()
|
|
35
|
+
|
|
36
|
+
package_data = _pyproject()["tool"]["setuptools"]["package-data"]
|
|
37
|
+
assert "py.typed" in package_data.get("mcp_api_test", [])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_entry_point_targets_the_package():
|
|
41
|
+
scripts = _pyproject()["project"]["scripts"]
|
|
42
|
+
assert scripts["mcp-api-test"] == "mcp_api_test.server:main"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_version_matches_pyproject():
|
|
46
|
+
# regression: src/mcp_api_test/__init__.py __version__ drifted from pyproject.toml
|
|
47
|
+
import mcp_api_test
|
|
48
|
+
|
|
49
|
+
assert mcp_api_test.__version__ == _project_version()
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"""Static checks that the source layout matches the wheel packaging config"""
|
|
2
|
-
|
|
3
|
-
import tomllib
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
7
|
-
SRC_DIR = PROJECT_ROOT / "src"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _packaged_modules() -> list[str]:
|
|
11
|
-
with open(PROJECT_ROOT / "pyproject.toml", "rb") as f:
|
|
12
|
-
config = tomllib.load(f)
|
|
13
|
-
return config["tool"]["setuptools"]["py-modules"]
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _project_version() -> str:
|
|
17
|
-
with open(PROJECT_ROOT / "pyproject.toml", "rb") as f:
|
|
18
|
-
config = tomllib.load(f)
|
|
19
|
-
return config["project"]["version"]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_all_src_modules_are_packaged():
|
|
23
|
-
src_modules = {
|
|
24
|
-
p.stem
|
|
25
|
-
for p in SRC_DIR.glob("*.py")
|
|
26
|
-
if p.name != "__init__.py" and not p.name.startswith("__")
|
|
27
|
-
}
|
|
28
|
-
missing = src_modules - set(_packaged_modules())
|
|
29
|
-
assert not missing, (
|
|
30
|
-
f"Modules in src/ that are missing from pyproject.toml "
|
|
31
|
-
f"[tool.setuptools] py-modules: {sorted(missing)}"
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def test_packaged_modules_exist_in_src():
|
|
36
|
-
for module in _packaged_modules():
|
|
37
|
-
assert (
|
|
38
|
-
SRC_DIR / f"{module}.py"
|
|
39
|
-
).exists(), f"'{module}' is listed in py-modules but has no src/{module}.py"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_version_matches_pyproject():
|
|
43
|
-
# regression: src/__init__.py __version__ drifted from pyproject.toml
|
|
44
|
-
import src
|
|
45
|
-
|
|
46
|
-
assert src.__version__ == _project_version()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|