Example #1
0
    def test_concurrent_401_requests_performs_refresh_once(self, monkeypatch):
        """Several requests are concurrent and receive all the 401 error.

        Two requests are running when the token timeout occurs. The two
        requests will receive a 401 error.

        Only one refresh should be made. After the refreshment, both requests
        should be retried.
        """
        network = MockSessionFunctions(monkeypatch)
        session = Session()

        defer_req1 = network.async('/req1')
        defer_req2 = network.async('/req2')

        req1 = session.download_storage_file('GET', '/req1')
        req2 = session.download_storage_file('GET', '/req2')
        assert network.history == ['/req1', '/req2']
        # both requests are waiting the response.

        # returning the 401 on /req1 should trigger the token refreshment.
        defer_refresh = network.async('/token')
        defer_req1.reject(MockTokenExpiredError())

        # /req2 receive the 401, but the refreshment is ongoing.
        defer_req2.reject(MockTokenExpiredError())

        with pytest.raises(TimeoutError):
            req1.result(0.01)
        with pytest.raises(TimeoutError):
            req2.result(0)
        assert network.history == ['/req1', '/req2', '/token']

        # as soon as the token is refreshed, both req1 and req2 should be
        # retried.
        network.on('/req1', network.ok_response())
        network.on('/req2', network.ok_response())
        defer_refresh.resolve(network.auth_response('NEW_TOKEN'))

        assert req1.result(0.01)
        assert req2.result(0.01)

        assert len(network.history) == 5
        assert network.history.count('/token') == 1
Example #2
0
    def test_token_is_refreshed_when_it_expires(self, monkeypatch):
        """Test that the token is automatically refreshed if needed.

        The first DL request will fail with a '401' error.
        Then, the session should make a re-auth request, then eventually
        retry the DL.
        """
        network = MockSessionFunctions(monkeypatch)
        session = Session()
        session.access_token = 'EXPIRED_TOKEN'
        session.refresh_token = 'REFRESH_TOKEN'

        network.on('/path', MockTokenExpiredError())
        network.on('/token', network.auth_response())
        network.on('/path', network.ok_response('CONTENT'))

        p = session.download_storage_file('GET', '/path')
        assert p.result(0.001).get('content') == 'CONTENT'
        assert network.history == ['/path', '/token', '/path']
Example #3
0
    def test_slow_request_fails_as_token_has_changed(self, monkeypatch):
        """Requests should retry if the token has changed during its execution.

        Two requests are executed concurrently: /req1 and /slow. Both will
        receive the 401 error. /req1 will refresh the token as usual, then
        retry.

        The slow request will "catch" the 401 once the refresh as already
        happened. In this cas, the request should detect that the token has
        changed, then retry immediately without doing a new token refreshment.
        """
        network = MockSessionFunctions(monkeypatch)
        session = Session()

        defer_req1 = network.async('/req1')
        defer_slow_req = network.async('/slow')

        req1 = session.download_storage_file('GET', '/req1')
        slow_req = session.download_storage_file('GET', '/slow')
        assert network.history == ['/req1', '/slow']
        # both requests are waiting the response.

        # returning the 401 on /req1 should trigger the token refreshment.
        network.on('/token', network.auth_response('NEW_ACCESS'))
        network.on('/req1', network.ok_response())
        defer_req1.reject(MockTokenExpiredError())

        assert req1.result(0.01)
        with pytest.raises(TimeoutError):
            slow_req.result(0)

        # slow_req receives a 401, detect the token has changed, then retry
        # immediately.
        network.on('/slow', network.ok_response())
        defer_slow_req.reject(MockTokenExpiredError())

        assert slow_req.result(0.01)
        assert network.history == ['/req1', '/slow', '/token',
                                   '/req1', '/slow']
Example #4
0
    def test_new_requests_wait_ongoing_token_refreshment(self, monkeypatch):
        """A request /req2 is started while an ongoing token refreshment.

        The second request must wait the result of the token refreshment,
        before being sent on the network.
        """
        network = MockSessionFunctions(monkeypatch)
        session = Session()

        network.on('/req1', MockTokenExpiredError())
        deferred_auth = network.async('/token')

        req1 = session.download_storage_file('GET', '/req1')

        # At this point, req1 is waiting for the /auth request to returns.
        assert network.history == ['/req1', '/token']

        req2 = session.download_storage_file('GET', '/req2')

        with pytest.raises(TimeoutError):
            req1.result(0.01)
        with pytest.raises(TimeoutError):
            req2.result(0)

        # /req2 hasn't been sent because it waits the token refreshment
        # initiated by /req1
        assert len(network.history) == 2

        network.on('/req2', network.ok_response())
        network.on('/req1', network.ok_response())

        # unlock the /auth response
        deferred_auth.resolve(network.auth_response())

        req1.result(0.01)
        req2.result(0.01)

        assert len(network.history) is 4
        assert sorted(network.history[-2:]) == ['/req1', '/req2']