From 2cfcb43f2d5a65e315a159029f8da192e00dffc2 Mon Sep 17 00:00:00 2001 From: Sergey Mishchenko Date: Wed, 15 Apr 2026 08:37:04 +0300 Subject: [PATCH] ignore command nesting in deploy rulebook --- annet/rulebook/deploying.py | 4 --- tests/annet/test_deploying.py | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/annet/rulebook/deploying.py b/annet/rulebook/deploying.py index 04c1cc19..ef778919 100644 --- a/annet/rulebook/deploying.py +++ b/annet/rulebook/deploying.py @@ -76,10 +76,6 @@ def match_deploy_rule(rules, cmd_path, context): if syntax.match_context(ifcontext, context): if depth == len(cmd_path) - 1: return rule - else: - rules = rule["children"] - if len(rules) == 0: - break # default match return { "attrs": { diff --git a/tests/annet/test_deploying.py b/tests/annet/test_deploying.py index bbc91034..e08bb496 100644 --- a/tests/annet/test_deploying.py +++ b/tests/annet/test_deploying.py @@ -2,8 +2,13 @@ from collections import OrderedDict from unittest import mock +import annet.vendors +from annet.annlib.command import Command, Question from annet.annlib.rbparser.deploying import Answer, MakeMessageMatcher +from annet.deploy import apply_deploy_rulebook +from annet.patching import PatchTree from annet.rulebook.deploying import compile_deploying_text +from tests import make_hw_stub def test_compile_deploying_text_cisco_2_dialogs(ann_connectors): @@ -44,3 +49,63 @@ def test_compile_deploying_text_cisco_2_dialogs(ann_connectors): ) assert res == expected + + +def test_deploying_rulebook_ignores_nesting(): + """Test that apply_deploy_rulebook correctly processes block and command structure.""" + # Mock HardwareView + text = """ +block + dialog: Question? ::: Y +command + dialog: Question? ::: Y + """ + rules = compile_deploying_text(text, "huawei") + hw = make_hw_stub("huawei") + + # Create the PatchTree with block and command structure + p = PatchTree() + p.add("block", {}) + p.itms[-1].child = PatchTree() + p.itms[-1].child.add("command", {}) + p.itms[-1].child.add("quit", {}) + p.add("command", {}) + p.itms[-1].child = PatchTree() + p.itms[-1].child.add("subcommand", {}) + p.itms[-1].child.add("quit", {}) + + # Expected result from apply_deploy_rulebook + expected = [ + Command(cmd="system-view", questions=[], timeout=30, read_timeout=30), + Command(cmd="block", questions=[Question(question="Question?", answer="Y")], timeout=30, read_timeout=None), + Command(cmd="command", questions=[Question(question="Question?", answer="Y")], timeout=30, read_timeout=None), + Command(cmd="quit", questions=[], timeout=30, read_timeout=None), + Command(cmd="command", questions=[Question(question="Question?", answer="Y")], timeout=30, read_timeout=None), + Command(cmd="subcommand", questions=[], timeout=30, read_timeout=None), + Command(cmd="quit", questions=[], timeout=30, read_timeout=None), + Command(cmd="q", questions=[], timeout=30, read_timeout=30), + ] + + # Mock get_rulebook to return our compiled rules + with mock.patch("annet.deploy.get_rulebook") as mock_get_rulebook: + mock_get_rulebook.return_value = { + "deploying": rules, + "patching": {}, + "ordering": None, + "texts": { + "patching": "", + "ordering": "", + "deploying": text, + }, + } + + # Apply deploy rulebook + formatter = annet.vendors.registry_connector.get().match(hw).make_formatter(indent="") + result = apply_deploy_rulebook(hw, formatter.cmd_paths(p), do_finalize=False, do_commit=False) + + assert len(result) == len(expected) + for actual, exp in zip(result, expected): + assert actual.cmd == exp.cmd + assert actual.questions == exp.questions + assert actual.timeout == exp.timeout + assert actual.read_timeout == exp.read_timeout