Skip to content

Commit 01aec5c

Browse files
committed
start adding tests
1 parent 02f14d5 commit 01aec5c

File tree

10 files changed

+417
-6
lines changed

10 files changed

+417
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/.coverage
12
/.env
23
/output/
34
dist

justfile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
black *args="":
2-
uv run black --check src {{ args }}
2+
uv run black --check src tests {{ args }}
33

44
ruff *args="":
55
uv run ruff check {{ args }}
@@ -14,8 +14,8 @@ check: black ruff type-check
1414
{{ just_executable() }} toml-sort --check
1515

1616
fix:
17-
uv run black src
18-
uv run ruff check --fix src
17+
uv run black src tests
18+
uv run ruff check --fix src tests
1919
{{ just_executable() }} toml-sort --in-place
2020

2121
release:
@@ -30,3 +30,8 @@ run *args="":
3030

3131
@html:
3232
just run django.views.generic.FormView --renderer html --output output
33+
34+
test *args="":
35+
uv run -m coverage run --module pytest tests {{ args }}
36+
-uv run -m coverage report
37+
uv run -m coverage html

pyproject.toml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,49 @@ Changelog = "https://github.com/ghickman/classify/blob/main/CHANGELOG.md"
3838
[dependency-groups]
3939
dev = [
4040
"black>=25.9.0,<26",
41+
"coverage>=7.11.0",
4142
"django>=5.2.7",
43+
"httpx>=0.28.1",
4244
"ipdb>=0.13.13",
4345
"nox>=2025.10.16",
4446
"nox-uv>=0.6.3",
47+
"pytest>=8.4.2",
48+
"pytest-env>=1.2.0",
4549
"ruff>=0.14.2",
4650
"toml-sort>=0.24.3",
4751
"ty>=0.0.1a24",
4852
]
4953

54+
[tool.coverage.html]
55+
show_contexts = true
56+
57+
[tool.coverage.report]
58+
# fail_under = 100
59+
show_missing = true
60+
skip_covered = true
61+
62+
[tool.coverage.run]
63+
branch = true
64+
relative_files = true
65+
dynamic_context = "test_function"
66+
omit = [
67+
"src/classify/contrib/*",
68+
]
69+
source = ["src", "tests"]
70+
71+
[tool.pytest.ini_options]
72+
addopts = "--tb=native"
73+
env = [
74+
"TEST_MODE=1",
75+
]
76+
5077
[tool.ruff]
5178
line-length = 88
5279
extend-exclude = [
5380
"src/classify/contrib/django/",
5481
"src/classify/templates",
5582
]
56-
src = ["src"]
83+
src = ["src", "tests"]
5784

5885
[tool.ruff.lint]
5986
select = ["ALL"]

src/classify/renderers/html.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import contextlib
22
import functools
3+
import os
34
import socketserver
45
import tempfile
56
import webbrowser
@@ -36,8 +37,10 @@ def _(empty: None) -> Generator: # noqa: ARG001
3637
def serve_output(port: int) -> None:
3738
httpd = socketserver.TCPServer(("", port), Handler)
3839

39-
print(f"Serving on port: {port}")
40-
webbrowser.open_new_tab(f"http://localhost:{port}/")
40+
if not os.environ.get("TEST_MODE", None):
41+
print(f"Serving on port: {port}")
42+
webbrowser.open_new_tab(f"http://localhost:{port}/")
43+
4144
httpd.serve_forever()
4245

4346

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import pytest
2+
3+
from classify.library import Attribute, Class, Line, Method
4+
5+
6+
def attribute(name, **kwargs):
7+
return Attribute(
8+
name=name,
9+
object=kwargs.get("object", ""),
10+
defining_class=kwargs.get("defining_class", ""),
11+
value=kwargs.get("value", ""),
12+
)
13+
14+
15+
def method(name, **kwargs):
16+
return Method(
17+
name=name,
18+
docstring=kwargs.get("docstring", ""),
19+
defining_class=kwargs.get("defining_class", ""),
20+
arguments=kwargs.get("arguments", ""),
21+
code=kwargs.get("code", ""),
22+
lines=Line(start=42, total=7),
23+
)
24+
25+
26+
@pytest.fixture
27+
def dummy_class():
28+
return Class(
29+
name="MyClass",
30+
methods={
31+
"one": [
32+
method("one", defining_class="ParentClass"),
33+
method("one", defining_class="MyClass"),
34+
]
35+
},
36+
docstring="",
37+
ancestors=[],
38+
parents=["ParentClass"],
39+
attributes={},
40+
)

tests/renderers/__init__.py

Whitespace-only changes.

tests/renderers/test_html.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import subprocess
2+
import tempfile
3+
import time
4+
from pathlib import Path
5+
6+
import httpx
7+
import pytest
8+
9+
from classify.renderers.html import resolve_path, to_html
10+
11+
12+
class DummyClass:
13+
some_attr = 7
14+
15+
def one(self):
16+
pass
17+
18+
19+
@pytest.fixture(scope="session")
20+
def classify_server():
21+
ds_proc = subprocess.Popen(
22+
[
23+
"classify",
24+
"tests.renderers.test_html.DummyClass",
25+
"--renderer",
26+
"html",
27+
"--serve",
28+
"--port",
29+
"8008",
30+
],
31+
stdout=subprocess.PIPE,
32+
stderr=subprocess.STDOUT,
33+
)
34+
35+
# Give the server time to start
36+
time.sleep(1)
37+
38+
# Check it started successfully
39+
assert not ds_proc.poll(), ds_proc.stdout.read().decode("utf-8")
40+
41+
yield ds_proc
42+
43+
# Shut it down at the end of the pytest session
44+
ds_proc.terminate()
45+
46+
47+
def test_to_html(dummy_class):
48+
with tempfile.TemporaryDirectory() as path:
49+
path = Path(path) # noqa: PLW2901
50+
to_html(dummy_class, output_path=path, serve=False, port=8000)
51+
52+
output = path / "classify.html"
53+
assert output.exists()
54+
55+
content = output.read_text()
56+
assert dummy_class.name in content
57+
58+
59+
def test_to_html_and_serve(classify_server): # noqa: ARG001
60+
response = httpx.get("http://127.0.0.1:8008/")
61+
assert response.status_code == 200 # noqa: PLR2004
62+
63+
64+
def test_resolve_path_with_path():
65+
with tempfile.TemporaryDirectory() as parent:
66+
path = Path(parent) / "testing"
67+
assert not path.exists()
68+
69+
with resolve_path(path) as made_path:
70+
assert made_path == path
71+
assert made_path.exists()
72+
73+
74+
def test_resolve_path_without_path():
75+
with resolve_path(None) as path:
76+
assert path.exists()
77+
78+
assert not path.exists()

tests/test_library.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import pytest
2+
3+
from classify.library import get_members
4+
5+
6+
class DummyParent:
7+
def one(self):
8+
print("one parent")
9+
10+
def three(self):
11+
pass
12+
13+
14+
class DummyClass(DummyParent):
15+
def __init__(self):
16+
super().__init__()
17+
18+
def _internal(self):
19+
pass
20+
21+
def one(self):
22+
super().one()
23+
print("one child")
24+
25+
def two(self):
26+
pass
27+
28+
29+
@pytest.mark.parametrize(
30+
("cls", "expected"),
31+
[
32+
(DummyParent, ["__dict__", "__weakref__", "one", "three"]),
33+
(DummyClass, ["__init__", "one", "two"]),
34+
],
35+
ids=["parent", "child"],
36+
)
37+
def test_get_members(cls, expected):
38+
members = get_members(cls)
39+
40+
names = [m[0] for m in members]
41+
42+
assert names == expected, names

0 commit comments

Comments
 (0)