mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 21:50:47 +08:00
Fix WebCRT command audit loss on merged stdin flush and early input.
Audit every completed stdin line per flush, keep a list of audit_line hints from the router, restore prompt enrichment and stdout fallback, and record actual bytes when hints disagree with what was sent. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
302ebfac23
commit
fcef3b59ac
4 changed files with 260 additions and 35 deletions
|
|
@ -16,6 +16,8 @@ from netx_api.webcrt_channel import (
|
|||
is_auditable_command_line,
|
||||
_is_device_output_line,
|
||||
_is_prompt_only_line,
|
||||
resolve_audit_commands,
|
||||
pick_audit_command,
|
||||
)
|
||||
from netx_api.webcrt_session_model import WebcrtSession
|
||||
|
||||
|
|
@ -141,6 +143,48 @@ class AuditableCommandLineTests(unittest.TestCase):
|
|||
|
||||
def test_plain_stdin_without_prompt_not_auditable(self) -> None:
|
||||
self.assertFalse(is_auditable_command_line("display version"))
|
||||
out = resolve_audit_commands(
|
||||
["display version"],
|
||||
prompt_hint="6150#",
|
||||
source="stdin",
|
||||
)
|
||||
self.assertEqual(out, ["6150#display version"])
|
||||
|
||||
def test_plain_stdin_records_actual_without_hint(self) -> None:
|
||||
out = resolve_audit_commands(["display version"], source="stdin")
|
||||
self.assertEqual(out, ["display version"])
|
||||
|
||||
|
||||
class ResolveAuditCommandsTests(unittest.TestCase):
|
||||
def test_merged_flush_audits_all_buf_lines(self) -> None:
|
||||
"""Interleave flush: multiple Enter in one write_stdin must not drop middle commands."""
|
||||
out = resolve_audit_commands(
|
||||
["show version", "show ll n b", "show intf"],
|
||||
audit_lines=["AL5458#show intf"],
|
||||
prompt_hint="AL5458#show version",
|
||||
source="stdin",
|
||||
)
|
||||
self.assertEqual(len(out), 3)
|
||||
self.assertIn("show version", out[0])
|
||||
self.assertIn("show ll n b", out[1])
|
||||
self.assertIn("show intf", out[2])
|
||||
|
||||
def test_hint_disagreement_records_actual_stdin(self) -> None:
|
||||
cmd = pick_audit_command(
|
||||
"how ll n b",
|
||||
"AL5458#show ll n b",
|
||||
prompt_hint="AL5458#",
|
||||
source="stdin",
|
||||
)
|
||||
self.assertEqual(cmd, "how ll n b")
|
||||
|
||||
def test_early_stdin_plain_command(self) -> None:
|
||||
out = resolve_audit_commands(
|
||||
["show version"],
|
||||
source="early_stdin",
|
||||
prompt_hint="AL5458#",
|
||||
)
|
||||
self.assertEqual(out, ["show version"])
|
||||
|
||||
|
||||
class PasswordPromptTests(unittest.TestCase):
|
||||
|
|
@ -339,6 +383,36 @@ class SessionCommandAuditTests(unittest.TestCase):
|
|||
sess.write_stdin("\r", audit_source="prompt_sync")
|
||||
mock_audit.assert_not_called()
|
||||
|
||||
@patch("netx_api.webcrt_session_model._audit")
|
||||
def test_merged_write_stdin_audits_every_command(self, mock_audit: MagicMock) -> None:
|
||||
conn = MagicMock()
|
||||
conn.RETURN = "\n"
|
||||
conn.remote_conn = MagicMock(spec=["recv_ready", "recv", "exit_status_ready", "resize_pty"])
|
||||
del conn.remote_conn.send
|
||||
conn.write_channel = MagicMock()
|
||||
|
||||
sess = WebcrtSession(
|
||||
session_id="s-merge",
|
||||
ne_id="ne1",
|
||||
ne_name="lab",
|
||||
ne_ip="1.2.3.4",
|
||||
protocol="ssh",
|
||||
cols=80,
|
||||
rows=24,
|
||||
cli_keymap=False,
|
||||
conn=conn,
|
||||
)
|
||||
sess._last_prompt_line = "AL5458#"
|
||||
sess.write_stdin(
|
||||
"show version\rshow ll n b\rshow intf\r",
|
||||
audit_lines=["AL5458#show intf"],
|
||||
)
|
||||
self.assertEqual(mock_audit.call_count, 3)
|
||||
recorded = [c.kwargs["command"] for c in mock_audit.call_args_list]
|
||||
self.assertTrue(any("show version" in x for x in recorded))
|
||||
self.assertTrue(any("show ll n b" in x for x in recorded))
|
||||
self.assertTrue(any("show intf" in x for x in recorded))
|
||||
|
||||
@patch("netx_api.webcrt_session_model._audit")
|
||||
def test_device_error_audit_line_rejected(self, mock_audit: MagicMock) -> None:
|
||||
conn = MagicMock()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue