Skip to content

Commit e0ad6f0

Browse files
committed
feat: dev
1 parent cd2ee55 commit e0ad6f0

File tree

7 files changed

+342
-4
lines changed

7 files changed

+342
-4
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ lint:
3939

4040

4141
test:
42-
poetry run pytest leetcode/ \
42+
poetry run pytest leetcode/ tests/ \
4343
-v --cov=leetcode --cov=leetcode_py \
4444
--cov-report=term-missing \
4545
--cov-report=xml \

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![tests](https://img.shields.io/github/actions/workflow/status/wisarootl/leetcode-py/ci-test.yml?branch=main&label=tests&logo=github)](https://github.com/wisarootl/zerv/actions/workflows/ci-test.yml)
88
[![release](https://img.shields.io/github/actions/workflow/status/wisarootl/leetcode-py/cd.yml?branch=main&label=release&logo=github)](https://github.com/wisarootl/zerv/actions/workflows/cd.yml)
99

10-
Premium LeetCode practice environment with modern Python tooling, beautiful tree visualizations, and comprehensive testing.
10+
Premium LeetCode practice repository with Python solutions, algorithm templates, data structure visualizations, and automated testing. Perfect for coding interview preparation, competitive programming, and mastering algorithms with Blind 75, Grind 75, and NeetCode 150 problems.
1111

1212
## 📋 Prerequisites
1313

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ disable_error_code = ["return", "no-redef"]
6969
exclude = [".templates"]
7070

7171
[tool.pytest.ini_options]
72-
testpaths = ["leetcode"]
73-
python_files = ["tests.py"]
72+
testpaths = ["leetcode", "tests"]
73+
python_files = ["tests.py", "test_*.py"]
7474
python_classes = ["Test*"]
7575
python_functions = ["test_*"]
7676
addopts = "-v --tb=short"

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Tests package

tests/test_list_node.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import pytest
2+
3+
from leetcode_py.list_node import ListNode
4+
5+
6+
class TestListNode:
7+
@pytest.mark.parametrize(
8+
"val,expected_val,expected_next",
9+
[
10+
(None, 0, None), # default
11+
(5, 5, None), # with value
12+
],
13+
)
14+
def test_init(self, val, expected_val, expected_next):
15+
node = ListNode() if val is None else ListNode(val)
16+
assert node.val == expected_val
17+
assert node.next == expected_next
18+
19+
def test_init_with_next(self):
20+
next_node = ListNode(2)
21+
node = ListNode(1, next_node)
22+
assert node.val == 1
23+
assert node.next == next_node
24+
25+
@pytest.mark.parametrize(
26+
"input_list,expected_result",
27+
[
28+
([], None),
29+
([1], "single_node"),
30+
([1, 2, 3], "multiple_nodes"),
31+
],
32+
)
33+
def test_from_list(self, input_list, expected_result):
34+
result = ListNode.from_list(input_list)
35+
36+
if expected_result is None:
37+
assert result is None
38+
elif expected_result == "single_node":
39+
assert result is not None
40+
assert result.val == 1
41+
assert result.next is None
42+
elif expected_result == "multiple_nodes":
43+
assert result is not None
44+
assert result.val == 1
45+
assert result.next is not None
46+
assert result.next.val == 2
47+
assert result.next.next is not None
48+
assert result.next.next.val == 3
49+
assert result.next.next.next is None
50+
51+
@pytest.mark.parametrize(
52+
"input_list,expected_output",
53+
[
54+
([1], [1]),
55+
([1, 2, 3], [1, 2, 3]),
56+
],
57+
)
58+
def test_to_list(self, input_list, expected_output):
59+
node = ListNode.from_list(input_list)
60+
assert node is not None
61+
assert node.to_list() == expected_output
62+
63+
@pytest.mark.parametrize(
64+
"input_list,expected_str,expected_repr",
65+
[
66+
([1, 2, 3], "1 -> 2 -> 3", "ListNode([1, 2, 3])"),
67+
],
68+
)
69+
def test_string_representations(self, input_list, expected_str, expected_repr):
70+
node = ListNode.from_list(input_list)
71+
assert node is not None
72+
assert str(node) == expected_str
73+
assert repr(node) == expected_repr
74+
assert node._repr_html_() == expected_str
75+
76+
@pytest.mark.parametrize(
77+
"list1,list2,should_equal",
78+
[
79+
([1, 2, 3], [1, 2, 3], True),
80+
([1, 2, 3], [1, 2, 4], False),
81+
],
82+
)
83+
def test_equality(self, list1, list2, should_equal):
84+
node1 = ListNode.from_list(list1)
85+
node2 = ListNode.from_list(list2)
86+
assert (node1 == node2) == should_equal
87+
88+
@pytest.mark.parametrize("other_value", [[1], "1"])
89+
def test_equality_different_types(self, other_value):
90+
node = ListNode(1)
91+
assert node != other_value
92+
93+
@pytest.mark.parametrize(
94+
"test_list",
95+
[
96+
[1, 2, 3, 4, 5],
97+
[1],
98+
[10, 20, 30],
99+
],
100+
)
101+
def test_roundtrip_conversion(self, test_list):
102+
node = ListNode.from_list(test_list)
103+
assert node is not None
104+
result = node.to_list()
105+
assert result == test_list

tests/test_test_utils.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from unittest.mock import patch
2+
3+
import pytest
4+
5+
from leetcode_py.test_utils import logged_test
6+
7+
8+
class TestLoggedTest:
9+
def test_logged_test_decorator_success(self):
10+
@logged_test
11+
def sample_test():
12+
return "success"
13+
14+
result = sample_test()
15+
assert result == "success"
16+
17+
def test_logged_test_decorator_failure(self):
18+
@logged_test
19+
def failing_test():
20+
raise ValueError("test error")
21+
22+
with pytest.raises(ValueError, match="test error"):
23+
failing_test()
24+
25+
def test_logged_test_preserves_function_metadata(self):
26+
@logged_test
27+
def sample_function():
28+
"""Sample docstring"""
29+
pass
30+
31+
assert sample_function.__name__ == "sample_function"
32+
assert sample_function.__doc__ == "Sample docstring"
33+
34+
@pytest.mark.parametrize(
35+
"args,kwargs,expected",
36+
[
37+
((1, 2), {"c": 3}, 6),
38+
((5, 10), {}, 15),
39+
],
40+
)
41+
def test_logged_test_with_arguments(self, args, kwargs, expected):
42+
@logged_test
43+
def function_with_args(a, b, c=None):
44+
return a + b + (c or 0)
45+
46+
result = function_with_args(*args, **kwargs)
47+
assert result == expected
48+
49+
@pytest.mark.parametrize(
50+
"kwargs,expected",
51+
[
52+
({"a": 1, "b": 2, "c": 3}, 6),
53+
({"x": 10, "y": 20}, 30),
54+
],
55+
)
56+
def test_logged_test_with_kwargs(self, kwargs, expected):
57+
@logged_test
58+
def function_with_kwargs(**kwargs):
59+
return sum(kwargs.values())
60+
61+
result = function_with_kwargs(**kwargs)
62+
assert result == expected
63+
64+
@patch("builtins.print")
65+
def test_logged_test_prints_empty_line(self, mock_print):
66+
@logged_test
67+
def sample_test():
68+
return "success"
69+
70+
sample_test()
71+
mock_print.assert_called_once_with("")

tests/test_tree_node.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import pytest
2+
3+
from leetcode_py.tree_node import TreeNode, build_anytree
4+
5+
6+
class TestTreeNode:
7+
@pytest.mark.parametrize(
8+
"val,expected_val",
9+
[
10+
(None, 0), # default
11+
(5, 5), # with value
12+
],
13+
)
14+
def test_init(self, val, expected_val):
15+
node = TreeNode() if val is None else TreeNode(val)
16+
assert node.val == expected_val
17+
assert node.left is None
18+
assert node.right is None
19+
20+
def test_init_with_children(self):
21+
left = TreeNode(1)
22+
right = TreeNode(2)
23+
node = TreeNode(0, left, right)
24+
assert node.val == 0
25+
assert node.left == left
26+
assert node.right == right
27+
28+
def test_from_list_empty(self):
29+
result = TreeNode.from_list([])
30+
assert result is None
31+
32+
def test_from_list_none_root(self):
33+
result = TreeNode.from_list([None])
34+
assert result is None
35+
36+
def test_from_list_single(self):
37+
result = TreeNode.from_list([1])
38+
assert result is not None
39+
assert result.val == 1
40+
assert result.left is None
41+
assert result.right is None
42+
43+
def test_from_list_complete_tree(self):
44+
result = TreeNode.from_list([1, 2, 3, 4, 5, 6, 7])
45+
assert result is not None
46+
assert result.val == 1
47+
assert result.left is not None
48+
assert result.left.val == 2
49+
assert result.right is not None
50+
assert result.right.val == 3
51+
assert result.left.left is not None
52+
assert result.left.left.val == 4
53+
assert result.left.right is not None
54+
assert result.left.right.val == 5
55+
assert result.right.left is not None
56+
assert result.right.left.val == 6
57+
assert result.right.right is not None
58+
assert result.right.right.val == 7
59+
60+
def test_from_list_sparse_tree(self):
61+
result = TreeNode.from_list([1, None, 2])
62+
assert result is not None
63+
assert result.val == 1
64+
assert result.left is None
65+
assert result.right is not None
66+
assert result.right.val == 2
67+
assert result.right.left is None
68+
assert result.right.right is None
69+
70+
@pytest.mark.parametrize(
71+
"input_list,expected_output",
72+
[
73+
([1], [1]),
74+
([1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7]),
75+
([1, None, 2], [1, None, 2]),
76+
],
77+
)
78+
def test_to_list(self, input_list, expected_output):
79+
node = TreeNode.from_list(input_list)
80+
assert node is not None
81+
assert node.to_list() == expected_output
82+
83+
@pytest.mark.parametrize(
84+
"input_list,expected_values",
85+
[
86+
([1], ["1"]),
87+
([1, 2, 3], ["1", "2", "3"]),
88+
],
89+
)
90+
def test_str_representation(self, input_list, expected_values):
91+
node = TreeNode.from_list(input_list)
92+
assert node is not None
93+
result = str(node)
94+
for val in expected_values:
95+
assert val in result
96+
97+
def test_repr_representation(self):
98+
node = TreeNode.from_list([1, 2, 3])
99+
assert node is not None
100+
assert repr(node) == "TreeNode([1, 2, 3])"
101+
102+
def test_repr_html_generates_svg(self):
103+
node = TreeNode.from_list([1, 2, 3])
104+
assert node is not None
105+
result = node._repr_html_()
106+
assert isinstance(result, str)
107+
assert "svg" in result.lower()
108+
109+
@pytest.mark.parametrize(
110+
"list1,list2,should_equal",
111+
[
112+
([1, 2, 3], [1, 2, 3], True),
113+
([1, 2, 3], [1, 3, 2], False),
114+
],
115+
)
116+
def test_equality(self, list1, list2, should_equal):
117+
node1 = TreeNode.from_list(list1)
118+
node2 = TreeNode.from_list(list2)
119+
assert node1 is not None
120+
assert node2 is not None
121+
assert (node1 == node2) == should_equal
122+
123+
@pytest.mark.parametrize("other_value", [[1], "1"])
124+
def test_equality_different_types(self, other_value):
125+
node = TreeNode(1)
126+
assert node != other_value
127+
128+
@pytest.mark.parametrize(
129+
"test_list",
130+
[
131+
[1, 2, 3, 4, 5, None, 6],
132+
[1],
133+
[1, None, 2],
134+
],
135+
)
136+
def test_roundtrip_conversion(self, test_list):
137+
node = TreeNode.from_list(test_list)
138+
assert node is not None
139+
result = node.to_list()
140+
assert result == test_list
141+
142+
def test_build_anytree_none(self):
143+
result = build_anytree(None)
144+
assert result is None
145+
146+
def test_build_anytree_single_node(self):
147+
node = TreeNode(1)
148+
result = build_anytree(node)
149+
assert result is not None
150+
assert result.name == "1"
151+
assert len(result.children) == 0
152+
153+
def test_str_with_none_tree(self):
154+
# Create a scenario where build_anytree returns None
155+
# This happens when we have a node but build_anytree fails
156+
import unittest.mock
157+
158+
with unittest.mock.patch("leetcode_py.tree_node.build_anytree", return_value=None):
159+
node = TreeNode(1)
160+
result = str(node)
161+
assert result == "None"

0 commit comments

Comments
 (0)