Skip to content

Commit ca4c296

Browse files
hmgaudeckerclaude
andauthored
Bring test coverage of src/dags to 100% (#76)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1cfab77 commit ca4c296

6 files changed

Lines changed: 117 additions & 3 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
- uses: actions/checkout@v6
5858
- uses: prefix-dev/setup-pixi@v0.9.4
5959
with:
60-
pixi-version: v0.64.0
60+
pixi-version: v0.65.0
6161
cache: true
6262
cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }}
6363
frozen: true

CHANGES.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ releases are available on [conda-forge](https://anaconda.org/conda-forge/dags).
66

77
## 0.5.0
88

9+
- :gh:`76` Add a couple of tests to bring coverage to 100% (:ghuser:`hmgaudecker`).
10+
911
- :gh:`75` Streamline public API (:ghuser:`hmgaudecker`).
1012

1113
- Deprecate `one_function_without_tree_logic`,
1214
`functions_without_tree_logic`, and `fail_if_paths_are_invalid`
1315
in favor of `get_one_function_without_tree_logic`,
14-
`get_functions_without_tree_logic`, and
15-
`dags.tree.validation.fail_if_paths_are_invalid`.
16+
`get_functions_without_tree_logic`
17+
- Deprecate `dags.tree.fail_if_paths_are_invalid` (call from `dags.tree.validation`)
1618

1719
- :gh:`65` Update docs and use Jupyter Book for documentation (:ghuser:`hmgaudecker`).
1820

tests/test_dag.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,3 +513,66 @@ def f2():
513513

514514
assert aggregated() is False
515515
assert "return" not in inspect.get_annotations(aggregated)
516+
517+
518+
def test_concatenate_functions_invalid_return_type_raises() -> None:
519+
with pytest.raises(DagsError, match="Invalid return type"):
520+
concatenate_functions(
521+
functions=[_leisure, _consumption],
522+
targets=["_leisure", "_consumption"],
523+
return_type="set", # type: ignore[arg-type]
524+
)
525+
526+
527+
def test_get_ancestors_include_targets() -> None:
528+
calculated = get_ancestors(
529+
functions=[_utility, _unrelated, _leisure, _consumption],
530+
targets="_utility",
531+
include_targets=True,
532+
)
533+
expected = {
534+
"_utility",
535+
"_consumption",
536+
"_leisure",
537+
"working_hours",
538+
"wage",
539+
"leisure_weight",
540+
}
541+
assert calculated == expected
542+
543+
544+
def test_concatenate_functions_non_string_targets() -> None:
545+
with pytest.raises(DagsError, match="Targets must be strings"):
546+
concatenate_functions(
547+
functions={"f": lambda: 1},
548+
targets=[1], # type: ignore[list-item]
549+
)
550+
551+
552+
def test_aggregator_exception_in_get_annotations() -> None:
553+
"""Test that an aggregator whose annotations cause an exception is handled."""
554+
555+
def f1() -> int:
556+
return 1
557+
558+
def f2() -> int:
559+
return 2
560+
561+
# Create an object that is callable but raises on get_annotations
562+
class BadAggregator:
563+
def __call__(self, a: int, b: int) -> int:
564+
return a + b
565+
566+
@property
567+
def __annotations__(self) -> dict[str, Any]:
568+
msg = "bad annotations"
569+
raise TypeError(msg)
570+
571+
aggregator = BadAggregator()
572+
result = concatenate_functions(
573+
functions={"f1": f1, "f2": f2},
574+
targets=["f1", "f2"],
575+
aggregator=aggregator,
576+
set_annotations=True,
577+
)
578+
assert result() == 3

tests/test_dag_tree/test_dag_tree.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66

77
from dags.tree import concatenate_functions_tree
8+
from dags.tree.dag_tree import create_dag_tree
89
from dags.tree.typing import (
910
NestedFunctionDict,
1011
NestedInputDict,
@@ -176,3 +177,16 @@ def f(a, b):
176177
enforce_signature=True,
177178
)
178179
assert concatenated_func({"a": 1}) == {"f": 2}
180+
181+
182+
def test_create_dag_tree(functions_simple: NestedFunctionDict) -> None:
183+
inputs: NestedInputDict = {
184+
"n1": {"a": 1, "b": 2},
185+
"n2": {"a": 3, "b": {"g": 4}},
186+
}
187+
targets: NestedTargetDict = {"n1": {"f": None}}
188+
189+
dag = create_dag_tree(functions=functions_simple, inputs=inputs, targets=targets)
190+
191+
assert "n1__f" in dag.nodes
192+
assert "n1__g" in dag.nodes

tests/test_process_output.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66

7+
from dags.exceptions import DagsError
78
from dags.output import (
89
aggregated_output,
910
dict_output,
@@ -88,3 +89,29 @@ def f():
8889
return (1, 2)
8990

9091
assert f() == [1, 2]
92+
93+
94+
def test_dict_output_keys_none() -> None:
95+
with pytest.raises(DagsError, match="'keys' parameter is required"):
96+
dict_output(keys=None) # ty: ignore[invalid-argument-type]
97+
98+
99+
def test_aggregated_output_aggregator_none() -> None:
100+
with pytest.raises(DagsError, match="'aggregator' parameter is required"):
101+
aggregated_output(aggregator=None) # ty: ignore[invalid-argument-type]
102+
103+
104+
def test_aggregated_output_direct_call() -> None:
105+
def f():
106+
return (10, 20)
107+
108+
g = aggregated_output(f, aggregator=lambda x, y: x + y)
109+
assert g() == 30
110+
111+
112+
def test_aggregated_output_decorator_usage() -> None:
113+
@aggregated_output(aggregator=lambda x, y: x + y)
114+
def f():
115+
return (10, 20)
116+
117+
assert f() == 30

tests/test_signature.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,11 @@ def test_with_signature_invalid_args_type() -> None:
237237
@with_signature(args="invalid")
238238
def f(*args, **kwargs):
239239
pass
240+
241+
242+
def test_with_signature_invalid_args_type_int() -> None:
243+
with pytest.raises(DagsError, match="Invalid type for arg"):
244+
245+
@with_signature(args=42) # type: ignore[arg-type]
246+
def f(*args, **kwargs):
247+
pass

0 commit comments

Comments
 (0)