@@ -1006,4 +1006,87 @@ def test_process_client_fingerprint_skips_ucs_detect_for_mud(monkeypatch, tmp_pa
10061006 fpd ._process_client_fingerprint (filepath , data )
10071007 except (ImportError , AttributeError , TypeError ):
10081008 pass
1009+ capsys .readouterr ()
10091010 assert not ucs_called
1011+
1012+
1013+ def test_protocol_fingerprint_hash_stability ():
1014+ """Hash must not change across releases for the same probe data."""
1015+ w = MockWriter (
1016+ extra = {"TERM" : "xterm" , "HOME" : "/home/user" , "USER" : "alice" , "SHELL" : "/bin/bash" }
1017+ )
1018+ w ._protocol = MockProtocol ({"HOME" : "/home/user" , "USER" : "alice" , "SHELL" : "/bin/bash" })
1019+ probe = {
1020+ "BINARY" : {"status" : "WILL" , "opt" : fps .BINARY },
1021+ "TTYPE" : {"status" : "WILL" , "opt" : fps .TTYPE },
1022+ "SGA" : {"status" : "WONT" , "opt" : fps .SGA },
1023+ }
1024+ fp = fps ._create_protocol_fingerprint (w , probe )
1025+ assert fps ._hash_fingerprint (fp ) == "426327fe80f38c2c"
1026+
1027+
1028+ def test_fingerprinting_server_on_request_environ ():
1029+ """FingerprintingServer includes HOME and SHELL in environ request."""
1030+ srv = fps .FingerprintingServer .__new__ (fps .FingerprintingServer )
1031+ env = srv .on_request_environ ()
1032+ assert "HOME" in env
1033+ assert "SHELL" in env
1034+ assert "USER" in env
1035+
1036+
1037+ def test_fingerprint_server_shell_has_no_protocol_factory ():
1038+ """Shell is a plain callback, not annotated with protocol_factory."""
1039+ assert not hasattr (fps .fingerprinting_server_shell , "protocol_factory" )
1040+
1041+
1042+ def test_fingerprint_server_main_exists ():
1043+ """Entry point function is importable."""
1044+ assert callable (fps .fingerprint_server_main )
1045+
1046+
1047+ def _noop_asyncio_run (coro ):
1048+ """Discard a coroutine without running it (avoids RuntimeWarning)."""
1049+ coro .close ()
1050+
1051+
1052+ def test_fingerprint_server_main_data_dir_flag (tmp_path , monkeypatch ):
1053+ """--data-dir sets DATA_DIR and passes remaining args through."""
1054+ data_dir = str (tmp_path / "fp-data" )
1055+ monkeypatch .setattr (sys , "argv" , ["prog" , "--data-dir" , data_dir , "127.0.0.1" , "9999" ])
1056+ monkeypatch .setattr ("telnetlib3.fingerprinting.asyncio.run" , _noop_asyncio_run )
1057+
1058+ captured : dict = {}
1059+ # local
1060+ from telnetlib3 .server import parse_server_args
1061+
1062+ original_parse = parse_server_args
1063+
1064+ def patched_parse () -> dict :
1065+ result = original_parse ()
1066+ captured .update (result )
1067+ return result
1068+
1069+ monkeypatch .setattr ("telnetlib3.server.parse_server_args" , patched_parse )
1070+
1071+ old_data_dir = fps .DATA_DIR
1072+ try :
1073+ fps .fingerprint_server_main ()
1074+ assert fps .DATA_DIR == data_dir
1075+ assert captured ["host" ] == "127.0.0.1"
1076+ assert captured ["port" ] == 9999
1077+ finally :
1078+ fps .DATA_DIR = old_data_dir
1079+
1080+
1081+ def test_fingerprint_server_main_env_fallback (monkeypatch ):
1082+ """DATA_DIR unchanged when --data-dir is not provided."""
1083+ monkeypatch .setattr (sys , "argv" , ["prog" ])
1084+ monkeypatch .setattr ("telnetlib3.fingerprinting.asyncio.run" , _noop_asyncio_run )
1085+
1086+ old_data_dir = fps .DATA_DIR
1087+ try :
1088+ fps .DATA_DIR = "/original"
1089+ fps .fingerprint_server_main ()
1090+ assert fps .DATA_DIR == "/original"
1091+ finally :
1092+ fps .DATA_DIR = old_data_dir
0 commit comments