Skip to content

Commit e87c178

Browse files
committed
Add test coverage to ensure the cache flush bit is properly handled
1 parent 3ee9b65 commit e87c178

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

tests/test_handlers.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
""" Unit tests for zeroconf._handlers """
66

7+
import asyncio
78
import logging
89
import pytest
910
import socket
@@ -834,3 +835,83 @@ async def test_qu_response_only_sends_additionals_if_sends_answer():
834835
# unregister
835836
zc.registry.remove(info)
836837
await aiozc.async_close()
838+
839+
840+
# This test uses asyncio because it needs to access the cache directly
841+
# which is not threadsafe
842+
@pytest.mark.asyncio
843+
async def test_cache_flush_bit():
844+
"""Test that the cache flush bit sets the TTL to one for matching records."""
845+
# instantiate a zeroconf instance
846+
aiozc = AsyncZeroconf(interfaces=['127.0.0.1'])
847+
zc = aiozc.zeroconf
848+
849+
type_ = "_cacheflush._tcp.local."
850+
name = "knownname"
851+
registration_name = "%s.%s" % (name, type_)
852+
desc = {'path': '/~paulsm/'}
853+
server_name = "server-uu1.local."
854+
info = ServiceInfo(
855+
type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")]
856+
)
857+
a_record = info.dns_addresses()[0]
858+
zc.cache.async_add_records([info.dns_pointer(), a_record, info.dns_text(), info.dns_service()])
859+
860+
info.addresses = [socket.inet_aton("10.0.1.5"), socket.inet_aton("10.0.1.6")]
861+
new_records = info.dns_addresses()
862+
for new_record in new_records:
863+
assert new_record.unique is True
864+
865+
original_a_record = zc.cache.get(a_record)
866+
# Do the run within 1s to verify the original record is not going to be expired
867+
out = r.DNSOutgoing(const._FLAGS_QR_RESPONSE | const._FLAGS_AA, multicast=True)
868+
for answer in new_records:
869+
out.add_answer_at_time(answer, 0)
870+
for packet in out.packets():
871+
zc.record_manager.async_updates_from_response(r.DNSIncoming(packet))
872+
assert zc.cache.get(a_record) is original_a_record
873+
assert original_a_record.ttl != 1
874+
for record in new_records:
875+
assert zc.cache.get(record) is not None
876+
877+
original_a_record.created = current_time_millis() - 1001
878+
879+
# Do the run within 1s to verify the original record is not going to be expired
880+
out = r.DNSOutgoing(const._FLAGS_QR_RESPONSE | const._FLAGS_AA, multicast=True)
881+
for answer in new_records:
882+
out.add_answer_at_time(answer, 0)
883+
for packet in out.packets():
884+
zc.record_manager.async_updates_from_response(r.DNSIncoming(packet))
885+
assert original_a_record.ttl == 1
886+
for record in new_records:
887+
assert zc.cache.get(record) is not None
888+
889+
cached_records = [zc.cache.get(record) for record in new_records]
890+
for record in cached_records:
891+
record.created = current_time_millis() - 1001
892+
893+
fresh_address = socket.inet_aton("4.4.4.4")
894+
info.addresses = [fresh_address]
895+
# Do the run within 1s to verify the two new records get marked as expired
896+
out = r.DNSOutgoing(const._FLAGS_QR_RESPONSE | const._FLAGS_AA, multicast=True)
897+
for answer in info.dns_addresses():
898+
out.add_answer_at_time(answer, 0)
899+
for packet in out.packets():
900+
zc.record_manager.async_updates_from_response(r.DNSIncoming(packet))
901+
for record in cached_records:
902+
assert record.ttl == 1
903+
904+
for entry in zc.cache.get_all_by_details(server_name, const._TYPE_A, const._CLASS_IN):
905+
if entry.address == fresh_address:
906+
assert entry.ttl > 1
907+
else:
908+
assert entry.ttl == 1
909+
910+
# Wait for the ttl 1 records to expire
911+
await asyncio.sleep(1.01)
912+
913+
loaded_info = r.ServiceInfo(type_, registration_name)
914+
loaded_info.load_from_cache(zc)
915+
assert loaded_info.addresses == info.addresses
916+
917+
await aiozc.async_close()

0 commit comments

Comments
 (0)