Skip to content

Commit a232ec9

Browse files
committed
chore: additional tests
1 parent 887d390 commit a232ec9

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed

linkd/conditions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class BaseCondition(abc.ABC):
4848

4949
def __init__(self, inner: type[t.Any] | types.UnionType | tuple[t.Any, ...] | None) -> None:
5050
if isinstance(inner, tuple) or inner is None or t.get_origin(inner) in (types.UnionType, t.Union):
51-
raise SyntaxError(f"{self.__class__.__name__!r} can only be parameterized by concrete types")
51+
raise ValueError(f"{self.__class__.__name__!r} can only be parameterized by concrete types")
5252

5353
if compose._is_compose_class(inner):
5454
raise ValueError(f"{self.__class__.__name__!r} cannot be parameterized by composed types")

tests/test_compose.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2025-present tandemdude
3+
#
4+
# Permission is hereby granted, free of charge, to any person obtaining a copy
5+
# of this software and associated documentation files (the "Software"), to deal
6+
# in the Software without restriction, including without limitation the rights
7+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
# copies of the Software, and to permit persons to whom the Software is
9+
# furnished to do so, subject to the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included in all
12+
# copies or substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
# SOFTWARE.
21+
import linkd
22+
23+
24+
class TestCompose:
25+
def test_compose_generates_working_class(self) -> None:
26+
class Deps(linkd.Compose):
27+
foo: str
28+
bar: int
29+
30+
d = Deps("foo", 1234)
31+
assert d.foo == "foo"
32+
assert d.bar == 1234

tests/test_conditions.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2025-present tandemdude
3+
#
4+
# Permission is hereby granted, free of charge, to any person obtaining a copy
5+
# of this software and associated documentation files (the "Software"), to deal
6+
# in the Software without restriction, including without limitation the rights
7+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
# copies of the Software, and to permit persons to whom the Software is
9+
# furnished to do so, subject to the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included in all
12+
# copies or substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
# SOFTWARE.
21+
import pytest
22+
23+
import linkd
24+
25+
26+
class Deps(linkd.Compose):
27+
foo: str
28+
bar: int
29+
30+
31+
class TestBaseCondition:
32+
def test_cannot_instantiate_with_composite_type(self) -> None:
33+
with pytest.raises(ValueError):
34+
linkd.If(int | str) # type: ignore[reportCallIssue]
35+
36+
def test_cannot_instantiate_with_composed_type(self) -> None:
37+
with pytest.raises(ValueError):
38+
linkd.If(Deps) # type: ignore[reportCallIssue]
39+
40+
41+
class TestDependencyExpression:
42+
def test_cannot_create_from_composed_type(self) -> None:
43+
with pytest.raises(ValueError):
44+
linkd.DependencyExpression.create(Deps)
45+
46+
def test_cannot_create_from_composed_union(self) -> None:
47+
with pytest.raises(ValueError):
48+
linkd.DependencyExpression.create(Deps | None)

tests/test_solver.py

+35
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import linkd
2727
from linkd import solver
2828
from linkd.solver import CANNOT_INJECT
29+
from linkd.solver import _parse_composed_dependencies
2930
from linkd.solver import _parse_injectable_params
3031

3132
COMMAND_CONTEXT = linkd.global_context_registry.register("linkd.contexts.command")
@@ -81,6 +82,22 @@ def m(foo: str, bar: int = linkd.INJECTED, *, baz: float, bork: bool = linkd.INJ
8182
assert kw["bork"]._order[0].inner is bool and kw["bork"]._required
8283

8384

85+
class TestComposedTypeParsing:
86+
def test_parses_composed_type_correctly(self) -> None:
87+
class Deps(linkd.Compose):
88+
foo: str
89+
bar: int
90+
91+
deps = _parse_composed_dependencies(Deps)
92+
93+
assert len(deps) == 2
94+
assert "foo" in deps and "bar" in deps
95+
96+
def test_errors_for_non_composed_type(self) -> None:
97+
with pytest.raises(TypeError):
98+
_parse_composed_dependencies(int) # type: ignore[reportArgumentType]
99+
100+
84101
class TestMethodInjection:
85102
@pytest.mark.asyncio
86103
async def test_exception_raised_when_no_context_available(self) -> None:
@@ -248,6 +265,24 @@ def m(foo, obj: object = linkd.INJECTED) -> None: # type: ignore[reportUnknownP
248265
async with manager.enter_context(linkd.Contexts.ROOT):
249266
await m("bar")
250267

268+
@pytest.mark.asyncio
269+
async def test_composed_dependency_provided_correctly(self) -> None:
270+
class Deps(linkd.Compose):
271+
foo: str
272+
bar: int
273+
274+
manager = linkd.DependencyInjectionManager()
275+
manager.registry_for(linkd.Contexts.ROOT).register_value(str, "foo")
276+
manager.registry_for(linkd.Contexts.ROOT).register_value(int, 1234)
277+
278+
@linkd.inject
279+
async def m(d: Deps = linkd.INJECTED) -> None:
280+
assert d.foo == "foo"
281+
assert d.bar == 1234
282+
283+
async with manager.enter_context(linkd.Contexts.ROOT):
284+
await m()
285+
251286

252287
class TestDependencyInjectionManager:
253288
@pytest.mark.asyncio

0 commit comments

Comments
 (0)