|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import os |
| 4 | + |
| 5 | +import pytest |
| 6 | +from zeep.loader import parse_xml |
| 7 | +import datetime |
| 8 | +from onvif.client import ONVIFCamera |
| 9 | +from onvif.settings import DEFAULT_SETTINGS |
| 10 | +from onvif.transport import ASYNC_TRANSPORT |
| 11 | +from onvif.types import FastDateTime, ForgivingTime |
| 12 | + |
| 13 | +INVALID_TERM_TIME = b'<?xml version="1.0" encoding="UTF-8"?>\r\n<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:chan="http://schemas.microsoft.com/ws/2005/02/duplex" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:tns1="http://www.onvif.org/ver10/topics">\r\n<SOAP-ENV:Header>\r\n<wsa5:Action>http://www.onvif.org/ver10/events/wsdl/PullPointSubscription/PullMessagesResponse</wsa5:Action>\r\n</SOAP-ENV:Header>\r\n<SOAP-ENV:Body>\r\n<tev:PullMessagesResponse>\r\n<tev:CurrentTime>2024-08-17T00:56:16Z</tev:CurrentTime>\r\n<tev:TerminationTime>2024-08-17T00:61:16Z</tev:TerminationTime>\r\n</tev:PullMessagesResponse>\r\n</SOAP-ENV:Body>\r\n</SOAP-ENV:Envelope>\r\n' |
| 14 | +_WSDL_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "onvif", "wsdl") |
| 15 | + |
| 16 | + |
| 17 | +@pytest.mark.asyncio |
| 18 | +async def test_parse_invalid_dt(caplog: pytest.LogCaptureFixture) -> None: |
| 19 | + device = ONVIFCamera("127.0.0.1", 80, "user", "pass", wsdl_dir=_WSDL_PATH) |
| 20 | + device.xaddrs = { |
| 21 | + "http://www.onvif.org/ver10/events/wsdl": "http://192.168.210.102:6688/onvif/event_service" |
| 22 | + } |
| 23 | + # Create subscription manager |
| 24 | + subscription = await device.create_notification_service() |
| 25 | + operation = subscription.document.bindings[subscription.binding_name].get( |
| 26 | + "Subscribe" |
| 27 | + ) |
| 28 | + envelope = parse_xml( |
| 29 | + INVALID_TERM_TIME, # type: ignore[arg-type] |
| 30 | + ASYNC_TRANSPORT, |
| 31 | + settings=DEFAULT_SETTINGS, |
| 32 | + ) |
| 33 | + result = operation.process_reply(envelope) |
| 34 | + assert result.CurrentTime == datetime.datetime( |
| 35 | + 2024, 8, 17, 0, 56, 16, tzinfo=datetime.timezone.utc |
| 36 | + ) |
| 37 | + assert result.TerminationTime == datetime.datetime( |
| 38 | + 2024, 8, 17, 1, 1, 16, tzinfo=datetime.timezone.utc |
| 39 | + ) |
| 40 | + assert "ValueError" not in caplog.text |
| 41 | + |
| 42 | + |
| 43 | +def test_parse_invalid_datetime() -> None: |
| 44 | + with pytest.raises(ValueError, match="Invalid character while parsing year"): |
| 45 | + FastDateTime().pythonvalue("aaaa-aa-aaTaa:aa:aaZ") |
| 46 | + |
| 47 | + |
| 48 | +def test_parse_invalid_time() -> None: |
| 49 | + with pytest.raises(ValueError, match="Unrecognised ISO 8601 time format"): |
| 50 | + ForgivingTime().pythonvalue("aa:aa:aa") |
| 51 | + |
| 52 | + |
| 53 | +def test_fix_datetime_missing_time() -> None: |
| 54 | + assert FastDateTime().pythonvalue("2024-08-17") == datetime.datetime( |
| 55 | + 2024, 8, 17, 0, 0, 0 |
| 56 | + ) |
| 57 | + |
| 58 | + |
| 59 | +def test_fix_datetime_missing_t() -> None: |
| 60 | + assert FastDateTime().pythonvalue("2024-08-17 00:61:16Z") == datetime.datetime( |
| 61 | + 2024, 8, 17, 1, 1, 16, tzinfo=datetime.timezone.utc |
| 62 | + ) |
| 63 | + assert FastDateTime().pythonvalue("2024-08-17 00:61:16") == datetime.datetime( |
| 64 | + 2024, 8, 17, 1, 1, 16 |
| 65 | + ) |
| 66 | + |
| 67 | + |
| 68 | +def test_fix_datetime_overflow() -> None: |
| 69 | + assert FastDateTime().pythonvalue("2024-08-17T00:61:16Z") == datetime.datetime( |
| 70 | + 2024, 8, 17, 1, 1, 16, tzinfo=datetime.timezone.utc |
| 71 | + ) |
| 72 | + assert FastDateTime().pythonvalue("2024-08-17T00:60:16Z") == datetime.datetime( |
| 73 | + 2024, 8, 17, 1, 0, 16, tzinfo=datetime.timezone.utc |
| 74 | + ) |
| 75 | + assert FastDateTime().pythonvalue("2024-08-17T00:59:16Z") == datetime.datetime( |
| 76 | + 2024, 8, 17, 0, 59, 16, tzinfo=datetime.timezone.utc |
| 77 | + ) |
| 78 | + assert FastDateTime().pythonvalue("2024-08-17T23:59:59Z") == datetime.datetime( |
| 79 | + 2024, 8, 17, 23, 59, 59, tzinfo=datetime.timezone.utc |
| 80 | + ) |
| 81 | + assert FastDateTime().pythonvalue("2024-08-17T24:00:00Z") == datetime.datetime( |
| 82 | + 2024, 8, 18, 0, 0, 0, tzinfo=datetime.timezone.utc |
| 83 | + ) |
| 84 | + |
| 85 | + |
| 86 | +def test_unfixable_datetime_overflow() -> None: |
| 87 | + with pytest.raises(ValueError, match="Invalid character while parsing minute"): |
| 88 | + FastDateTime().pythonvalue("2024-08-17T999:00:00Z") |
| 89 | + |
| 90 | + |
| 91 | +def test_fix_time_overflow() -> None: |
| 92 | + assert ForgivingTime().pythonvalue("24:00:00") == datetime.time(0, 0, 0) |
| 93 | + assert ForgivingTime().pythonvalue("23:59:59") == datetime.time(23, 59, 59) |
| 94 | + assert ForgivingTime().pythonvalue("23:59:60") == datetime.time(0, 0, 0) |
| 95 | + assert ForgivingTime().pythonvalue("23:59:61") == datetime.time(0, 0, 1) |
| 96 | + assert ForgivingTime().pythonvalue("23:60:00") == datetime.time(0, 0, 0) |
| 97 | + assert ForgivingTime().pythonvalue("23:61:00") == datetime.time(0, 1, 0) |
| 98 | + |
| 99 | + |
| 100 | +def test_unfixable_time_overflow() -> None: |
| 101 | + with pytest.raises(ValueError, match="Unrecognised ISO 8601 time format"): |
| 102 | + assert ForgivingTime().pythonvalue("999:00:00") |
0 commit comments