ec2b666284
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
# Copyright 2026 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from __future__ import annotations
|
|
|
|
from google.adk.events._node_path_builder import _NodePathBuilder
|
|
import pytest
|
|
|
|
|
|
def test_from_string_returns_empty_path_when_string_is_empty():
|
|
"""Parsing an empty string returns a path with no segments."""
|
|
path = _NodePathBuilder.from_string('')
|
|
assert str(path) == ''
|
|
|
|
|
|
def test_from_string_parses_single_segment():
|
|
"""Parsing a single segment string returns a path with that segment."""
|
|
path = _NodePathBuilder.from_string('wf@1')
|
|
assert str(path) == 'wf@1'
|
|
|
|
|
|
def test_from_string_parses_multiple_segments():
|
|
"""Parsing a slash-separated string returns a path with all segments."""
|
|
path = _NodePathBuilder.from_string('wf@1/node@2')
|
|
assert str(path) == 'wf@1/node@2'
|
|
|
|
|
|
def test_str_joins_segments_with_slash():
|
|
"""String representation joins segments with slashes."""
|
|
path = _NodePathBuilder(['wf@1', 'node@2'])
|
|
assert str(path) == 'wf@1/node@2'
|
|
|
|
|
|
def test_eq_returns_true_for_same_segments():
|
|
"""Equality returns True if both paths have identical segments."""
|
|
path1 = _NodePathBuilder(['wf@1', 'node@2'])
|
|
path2 = _NodePathBuilder(['wf@1', 'node@2'])
|
|
assert path1 == path2
|
|
|
|
|
|
def test_eq_returns_false_for_different_segments():
|
|
"""Equality returns False if segments differ."""
|
|
path1 = _NodePathBuilder(['wf@1', 'node@1'])
|
|
path2 = _NodePathBuilder(['wf@1', 'node@2'])
|
|
assert path1 != path2
|
|
|
|
|
|
def test_eq_returns_not_implemented_for_other_types():
|
|
"""Equality returns False when comparing with non-_NodePathBuilder objects."""
|
|
path = _NodePathBuilder(['wf@1'])
|
|
assert path != 'wf@1'
|
|
|
|
|
|
def test_node_name_returns_name_without_run_id():
|
|
"""node_name returns the name part of the leaf segment, removing @id."""
|
|
path = _NodePathBuilder.from_string('wf@1/node@2')
|
|
assert path.node_name == 'node'
|
|
|
|
|
|
def test_node_name_returns_full_segment_when_no_id_present():
|
|
"""node_name returns the full segment if no @id is present."""
|
|
path = _NodePathBuilder.from_string('wf@1/node')
|
|
assert path.node_name == 'node'
|
|
|
|
|
|
def test_run_id_returns_id_when_present():
|
|
"""run_id returns the ID part after @ in the leaf segment."""
|
|
path = _NodePathBuilder.from_string('wf@1/node@2')
|
|
assert path.run_id == '2'
|
|
|
|
|
|
def test_run_id_returns_none_when_no_id_present():
|
|
"""run_id returns None if no @ is present in the leaf segment."""
|
|
path = _NodePathBuilder.from_string('wf@1/node')
|
|
assert path.run_id is None
|
|
|
|
|
|
def test_parent_returns_prefix_path():
|
|
"""parent returns a new path with the last segment removed."""
|
|
path = _NodePathBuilder.from_string('wf@1/node@2')
|
|
parent = path.parent
|
|
assert parent is not None
|
|
assert str(parent) == 'wf@1'
|
|
|
|
|
|
def test_parent_returns_none_for_root_path():
|
|
"""parent returns None for a path with a single segment."""
|
|
path = _NodePathBuilder.from_string('wf@1')
|
|
assert path.parent is None
|
|
|
|
|
|
def test_append_adds_segment_with_id():
|
|
"""append adds a new segment with the specified name and run ID."""
|
|
path = _NodePathBuilder.from_string('wf@1')
|
|
child = path.append('node', '2')
|
|
assert str(child) == 'wf@1/node@2'
|
|
|
|
|
|
def test_append_adds_segment_without_id():
|
|
"""append adds a new segment without run ID if not provided."""
|
|
path = _NodePathBuilder.from_string('wf@1')
|
|
child = path.append('node')
|
|
assert str(child) == 'wf@1/node'
|
|
|
|
|
|
def test_is_descendant_of_returns_true_for_deep_child():
|
|
"""is_descendant_of returns True if the path starts with the ancestor segments."""
|
|
ancestor = _NodePathBuilder.from_string('wf@1')
|
|
descendant = _NodePathBuilder.from_string('wf@1/node@2')
|
|
assert descendant.is_descendant_of(ancestor)
|
|
|
|
|
|
def test_is_descendant_of_returns_false_for_unrelated_path():
|
|
"""is_descendant_of returns False if the path does not start with ancestor segments."""
|
|
ancestor = _NodePathBuilder.from_string('wf@1')
|
|
other = _NodePathBuilder.from_string('other@1/node@2')
|
|
assert not other.is_descendant_of(ancestor)
|
|
|
|
|
|
def test_is_direct_child_of_returns_true_for_direct_child():
|
|
"""is_direct_child_of returns True if the path is exactly one segment longer than parent."""
|
|
parent = _NodePathBuilder.from_string('wf@1')
|
|
child = _NodePathBuilder.from_string('wf@1/node@2')
|
|
assert child.is_direct_child_of(parent)
|
|
|
|
|
|
def test_is_direct_child_of_returns_false_for_grandchild():
|
|
"""is_direct_child_of returns False if the path is more than one segment longer."""
|
|
parent = _NodePathBuilder.from_string('wf@1')
|
|
descendant = _NodePathBuilder.from_string('wf@1/inner@1/node@2')
|
|
assert not descendant.is_direct_child_of(parent)
|
|
|
|
|
|
def test_get_direct_child_returns_path_object():
|
|
"""get_direct_child returns a new path object for the direct child."""
|
|
parent = _NodePathBuilder.from_string('wf@1')
|
|
descendant = _NodePathBuilder.from_string('wf@1/inner@1/node@2')
|
|
child = parent.get_direct_child(descendant)
|
|
assert isinstance(child, _NodePathBuilder)
|
|
assert str(child) == 'wf@1/inner@1'
|
|
|
|
|
|
def test_get_direct_child_raises_value_error_for_unrelated_path():
|
|
"""get_direct_child raises ValueError if descendant does not start with self."""
|
|
parent = _NodePathBuilder.from_string('wf@1')
|
|
other = _NodePathBuilder.from_string('other@1/node@2')
|
|
with pytest.raises(ValueError):
|
|
parent.get_direct_child(other)
|