File: test_client.py

package info (click to toggle)
aiocoap 0.4.14-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,756 kB
  • sloc: python: 16,846; makefile: 23; sh: 9
file content (218 lines) | stat: -rw-r--r-- 8,686 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
#
# SPDX-License-Identifier: MIT

import asyncio
import aiocoap
import errno

from .test_server import WithTestServer, WithClient, no_warnings, asynctest


class TestClientWithSetHost(WithTestServer, WithClient):
    set_uri_host = True

    @no_warnings
    @asynctest
    async def test_uri_parser(self):
        request = aiocoap.Message(code=aiocoap.GET)
        request_uri = "coap://" + self.servernetloc + "/empty?query=a&query=b"
        request.set_request_uri(request_uri, set_uri_host=self.set_uri_host)
        self.assertEqual(
            request.get_request_uri(),
            request_uri,
            "Request URL does not round-trip in request",
        )
        response = await self.client.request(request).response
        self.assertEqual(
            response.get_request_uri(),
            request_uri,
            "Request URL does not round-trip in response",
        )
        self.assertEqual(response.code, aiocoap.CONTENT, "Request URL building failed")

        request = aiocoap.Message(code=aiocoap.GET)
        request.set_request_uri(
            "coap://" + self.servernamealias + "/empty", set_uri_host=self.set_uri_host
        )
        self.assertEqual(
            request.get_request_uri(), "coap://" + self.servernamealias + "/empty"
        )
        response = await self.client.request(request).response
        self.assertEqual(
            response.code,
            aiocoap.CONTENT,
            "Resolving WithTestServer.servernamealias failed",
        )
        if self.set_uri_host:
            self.assertEqual(
                response.get_request_uri(),
                "coap://" + self.servernamealias + "/empty",
                "Host name did not get round-tripped",
            )
        else:
            # The simple6 transport misreports remotes to which a socket was
            # opened with a name.
            if "simple6" not in list(
                aiocoap.defaults.get_default_clienttransports(loop=self.loop)
            ):
                self.assertEqual(
                    response.get_request_uri(),
                    "coap://" + self.servernetloc + "/empty",
                    "Response's request URI is not numeric in hostname-less query",
                )

    @no_warnings
    @asynctest
    async def test_uri_parser2(self):
        """A difficult test because it is prone to keeping the transport
        around, bothering later tests"""

        request = aiocoap.Message(code=aiocoap.GET)
        request.set_request_uri(
            "coap://" + self.servernetloc + ":9999/empty",
            set_uri_host=self.set_uri_host,
        )
        resp = self.client.request(request).response
        try:
            # give the request some time to finish getaddrinfo
            result = await asyncio.as_completed([resp], timeout=0.1).__next__()
        except aiocoap.error.NetworkError as e:
            # This is a bit stricter than what the API indicates, but hey, we
            # can still relax the tests.
            self.assertTrue(isinstance(e.__cause__, OSError))
            # ECONNREFUSED: linux; ECONNRESET: win32
            self.assertTrue(e.__cause__.errno in (errno.ECONNREFUSED, errno.ECONNRESET))
        except asyncio.TimeoutError:
            self.fail(
                "Request to non-opened port did not come back with 'Connection Refused' immediately"
            )
        else:
            self.fail(
                "Request to non-opened port did not come back with 'Connection Refused', but another result: %s"
                % (result,)
            )
        self.assertTrue(
            request.remote.hostinfo.endswith(":9999"), "Remote port was not parsed"
        )
        resp.cancel()


class TestClientWithHostlessMessages(TestClientWithSetHost):
    set_uri_host = False


class TestClientOther(WithTestServer, WithClient):
    @no_warnings
    # can't do @asynctest because of assertRaises
    def test_raising(self):
        """This test obtains results via the response_raising property of a
        Request."""
        yieldfrom = self.loop.run_until_complete

        request = aiocoap.Message(
            code=aiocoap.GET, uri="coap://" + self.servernetloc + "/empty"
        )
        response = yieldfrom(self.client.request(request).response_raising)
        self.assertEqual(
            response.code,
            aiocoap.CONTENT,
            "Response access via response_raising failed",
        )

        request = aiocoap.Message(
            code=aiocoap.GET, uri="coap://" + self.servernetloc + "/nonexistent"
        )
        ## @FIXME i'd like to assertRaises(NotFound), see docstring of
        # :class:`ResponseWrappingError`
        self.assertRaises(
            aiocoap.error.ResponseWrappingError,
            yieldfrom,
            self.client.request(request).response_raising,
        )

    @no_warnings
    @asynctest
    async def test_nonraising(self):
        """This test obtains results via the response_nonraising property of a
        Request."""
        request = aiocoap.Message(
            code=aiocoap.GET, uri="coap://" + self.servernetloc + "/empty"
        )
        response = await self.client.request(request).response_nonraising
        self.assertEqual(
            response.code,
            aiocoap.CONTENT,
            "Response access via response_nonraising failed",
        )

        request = aiocoap.Message(
            code=aiocoap.GET, uri="coap://cant.resolve.this.example./empty"
        )
        response = await self.client.request(request).response_nonraising
        self.assertEqual(response.code, aiocoap.INTERNAL_SERVER_ERROR)

    @no_warnings
    @asynctest
    async def test_freeoncancel(self):
        # As there's no programmatic feedback about what actually gets sent,
        # looking at the logs is the easiest option, even though it will
        # require occasional adjustment when logged messages change.
        #
        # FIXME Currently, this *only* checks for whether later responses are
        # rejected, it does *not* check for whether the response runner is
        # freed as well (primarily because that'd need _del_to_be_sure to be
        # useable in an async context).

        # With immediate cancellation, nothing is sent. Note that we don't
        # ensure this per documentation, but still it's good to see when this
        # changes.
        loglength = len(self.handler.list)
        request = aiocoap.Message(
            code=aiocoap.GET, uri="coap://" + self.servernetloc + "/empty"
        )
        self.resp = self.client.request(request).response
        self.resp.cancel()
        self.assertEqual(
            loglength,
            len(self.handler.list),
            "Something was logged during request creation and immediate cancellation: %r"
            % (self.handler.list[loglength:],),
        )

        # FIXME: What is slightly weird here is that on uvloop, something
        # *does* get sent -- but it appears that that happens even after the
        # cancellation. That behavior oly gets apparent when test_freeoncancel
        # and test_freeoncancel_non were run in a single test, because then the
        # ACK Content from the prior test also got counted.

    @no_warnings
    @asynctest
    async def test_freeoncancel_non(self):
        # With a NON, the response should take long. (Not trying to race the
        # "I'm taking too long"-ACK by making the sleep short enough).
        # Note that the resource implementation deliberately sends responses as CON,
        # as to allow us to peek into the internals of aiocoap by
        # looking at wehter it returns a RST or an ACK.
        request = aiocoap.Message(
            code=aiocoap.GET,
            uri="coap://" + self.servernetloc + "/slow",
            mtype=aiocoap.NON,
        )
        self.resp = self.client.request(request).response
        # Wait for the request to actually be sent
        while not any("Sending request" in l.getMessage() for l in self.handler.list):
            await asyncio.sleep(0.001)
        # Now the request was sent, let's look at what happens during and after the cancellation
        loglength = len(self.handler.list)
        self.resp.cancel()
        await asyncio.sleep(0.4)  # server takes 0.2 to respond
        logmsgs = self.handler.list[loglength:]
        unmatched_msgs = [
            l
            for l in logmsgs
            if "could not be matched to any request" in l.getMessage()
        ]
        self.assertEqual(
            len(unmatched_msgs), 1, "The incoming response was not treated as unmatched"
        )