@@ -1085,6 +1085,73 @@ def update_service(self, zc, type_, name) -> None: # type: ignore[no-untyped-de
10851085 zc .close ()
10861086
10871087
1088+ def test_service_browser_nsec_record_does_not_trigger_update ():
1089+ """NSEC records assert non-existence and must not fire ServiceStateChange.Updated."""
1090+ zc = Zeroconf (interfaces = ["127.0.0.1" ])
1091+ type_ = "_hap._tcp.local."
1092+ registration_name = f"xxxyyy.{ type_ } "
1093+ callbacks : list [tuple [str , str , str ]] = []
1094+ service_added = Event ()
1095+
1096+ class MyServiceListener (r .ServiceListener ):
1097+ def add_service (self , zc , type_ , name ) -> None : # type: ignore[no-untyped-def]
1098+ if name == registration_name :
1099+ callbacks .append (("add" , type_ , name ))
1100+ service_added .set ()
1101+
1102+ def remove_service (self , zc , type_ , name ) -> None : # type: ignore[no-untyped-def]
1103+ if name == registration_name :
1104+ callbacks .append (("remove" , type_ , name ))
1105+
1106+ def update_service (self , zc , type_ , name ) -> None : # type: ignore[no-untyped-def]
1107+ if name == registration_name :
1108+ callbacks .append (("update" , type_ , name ))
1109+
1110+ listener = MyServiceListener ()
1111+ browser = r .ServiceBrowser (zc , type_ , None , listener )
1112+ try :
1113+ desc = {"path" : "/~paulsm/" }
1114+ address = socket .inet_aton ("10.0.1.2" )
1115+ info = ServiceInfo (type_ , registration_name , 80 , 0 , 0 , desc , "ash-2.local." , addresses = [address ])
1116+
1117+ _inject_response (
1118+ zc ,
1119+ mock_incoming_msg (
1120+ [
1121+ info .dns_pointer (),
1122+ info .dns_service (),
1123+ info .dns_text (),
1124+ * info .dns_addresses (),
1125+ ]
1126+ ),
1127+ )
1128+ assert service_added .wait (timeout = 5 ), "add_service callback never fired"
1129+
1130+ # NSEC inject runs synchronously through the event loop; once
1131+ # _inject_response returns, async_update_records has already
1132+ # decided not to enqueue a callback for the NSEC record.
1133+ _inject_response (
1134+ zc ,
1135+ mock_incoming_msg (
1136+ [
1137+ r .DNSNsec (
1138+ registration_name ,
1139+ const ._TYPE_NSEC ,
1140+ const ._CLASS_IN | const ._CLASS_UNIQUE ,
1141+ const ._DNS_OTHER_TTL ,
1142+ registration_name ,
1143+ [const ._TYPE_AAAA ],
1144+ ),
1145+ ]
1146+ ),
1147+ )
1148+
1149+ assert callbacks == [("add" , type_ , registration_name )]
1150+ finally :
1151+ browser .cancel ()
1152+ zc .close ()
1153+
1154+
10881155def test_service_browser_uses_non_strict_names ():
10891156 """Verify we can look for technically invalid names as we cannot change what others do."""
10901157
0 commit comments