Skip to content

Commit ebc4913

Browse files
Tests for orm/utils/node.py + remove dead code (#6983)
Most of the changes in this PR are new tests for src/aiida/orm/utils/node.py, now we have 100% coverage for this file. --- Co-authored-by: Muhammad Rebaal <[email protected]>
1 parent 3bae272 commit ebc4913

File tree

2 files changed

+90
-7
lines changed

2 files changed

+90
-7
lines changed

src/aiida/orm/utils/node.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ def load_node_class(type_string):
4040
if not type_string.endswith('.'):
4141
raise exceptions.DbContentError(f'The type string `{type_string}` is invalid')
4242

43-
try:
44-
base_path = type_string.rsplit('.', 2)[0]
45-
except ValueError as exc:
46-
raise exceptions.EntryPointError from exc
43+
base_path = type_string.rsplit('.', 2)[0]
4744

4845
# This exception needs to be there to make migrations work that rely on the old type string starting with `node.`
4946
# Since now the type strings no longer have that prefix, we simply strip it and continue with the normal logic.
@@ -60,8 +57,7 @@ def load_node_class(type_string):
6057
return Data
6158

6259
if base_path.startswith('process'):
63-
entry_point_name = base_path.removeprefix('data.')
64-
return load_entry_point('aiida.node', entry_point_name)
60+
return load_entry_point('aiida.node', base_path)
6561

6662
# At this point we really have an anomalous type string. At some point, storing nodes with unresolvable type strings
6763
# was allowed, for example by creating a sub class in a shell and then storing an instance. Attempting to load the

tests/orm/utils/test_node.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@
1010

1111
import pytest
1212

13+
from aiida.common import exceptions
1314
from aiida.orm import Data
14-
from aiida.orm.utils.node import load_node_class
15+
from aiida.orm.utils.node import (
16+
get_type_string_from_class,
17+
is_valid_node_type_string,
18+
load_node_class,
19+
)
1520

1621

1722
def test_load_node_class_fallback():
@@ -23,3 +28,85 @@ def test_load_node_class_fallback():
2328
with pytest.warns(UserWarning):
2429
loaded_class = load_node_class('__main__.SubData.')
2530
assert loaded_class == Data
31+
32+
# Test invalid type string without trailing dot.
33+
with pytest.raises(exceptions.DbContentError, match='invalid'):
34+
load_node_class('data.dict')
35+
36+
37+
def test_load_node_class_with_node_prefix():
38+
"""Test the behavior of load_node_class with node prefix."""
39+
# Test node prefix removal
40+
with pytest.warns(UserWarning):
41+
loaded_class = load_node_class('node.data.dict.')
42+
assert loaded_class == Data
43+
44+
# Test node prefix with invalid subtype
45+
with pytest.warns(UserWarning):
46+
loaded_class = load_node_class('node.invalid.type.')
47+
assert loaded_class == Data
48+
49+
50+
def test_load_node_class_with_process_prefix():
51+
"""Test the behavior of load_node_class with process prefix."""
52+
from aiida.orm.nodes.process.calculation.calculation import CalculationNode
53+
from aiida.orm.nodes.process.process import ProcessNode
54+
55+
# Test process prefix with invalid type - should return CalculationNode
56+
loaded_class = load_node_class('process.calculation.invalid.')
57+
assert loaded_class == CalculationNode
58+
59+
# Test process prefix with empty subtype - should return ProcessNode
60+
loaded_class = load_node_class('process.')
61+
assert loaded_class == ProcessNode
62+
63+
64+
def test_load_node_class_with_data_prefix():
65+
"""Test the behavior of load_node_class with data prefix."""
66+
# Test data prefix with removeprefix
67+
with pytest.warns(UserWarning):
68+
loaded_class = load_node_class('data.dict.')
69+
assert loaded_class == Data
70+
71+
# Test data prefix with empty subtype
72+
with pytest.warns(UserWarning, match='unknown type string `data.`'):
73+
loaded_class = load_node_class('data.')
74+
assert loaded_class == Data
75+
76+
77+
def test_load_node_class_empty_string():
78+
"""Test that an empty string returns the base `Node` class."""
79+
from aiida.orm import Node
80+
81+
loaded_class = load_node_class('')
82+
assert loaded_class == Node
83+
84+
85+
def test_get_type_string_from_class():
86+
"""Test conversion from class module/name to type string."""
87+
# Test with internal data class
88+
type_string = get_type_string_from_class('aiida.orm.nodes.data.dict', 'Dict')
89+
assert type_string == 'data.core.dict.Dict.' # Changed to include 'core'
90+
91+
# Test with Node class (should return empty string)
92+
type_string = get_type_string_from_class('aiida.orm.nodes.node', 'Node')
93+
assert type_string == ''
94+
95+
# Test with process class
96+
type_string = get_type_string_from_class('aiida.orm.nodes.process.process', 'ProcessNode')
97+
assert type_string == 'process.ProcessNode.'
98+
99+
100+
def test_is_valid_node_type_string():
101+
"""Test validation of node type strings."""
102+
# Test valid cases
103+
assert is_valid_node_type_string('')
104+
assert is_valid_node_type_string('data.dict.Dict.')
105+
106+
# Test invalid cases
107+
assert not is_valid_node_type_string('data.dict') # No trailing dot
108+
assert not is_valid_node_type_string('data.') # Just one dot
109+
110+
# Test raise_on_false parameter
111+
with pytest.raises(exceptions.DbContentError, match='invalid'):
112+
is_valid_node_type_string('data.dict', raise_on_false=True)

0 commit comments

Comments
 (0)