def testGetUserWithNewUserAndMixedCaseTwitterScreenName(self): """ A new L{User} is created if L{Delegator.getUser} verifies the L{TwitterUser} but can't find a user matching the Twitter screen name. The Twitter user's screen name should be lowercased to make the new Fluidinfo username. """ UserAPI().create([ (u'consumer', 'secret', u'Consumer', u'*****@*****.**')]) consumer = getUser(u'consumer') OAuthConsumerAPI().register(consumer) self.store.commit() self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') delegator = Delegator(self.transact) deferred = delegator.getUser(u'consumer', provider, authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), dumps({'id': 1984245, 'screen_name': u'MixedCaseName', 'name': u'John Doe'})) responseDeferred.callback(response) result = yield deferred self.assertTrue(result['new-user']) user = TwitterUserAPI().get(1984245) self.assertEqual(u'mixedcasename', user.username)
def testGetUserWithNoPassword(self): """ If a L{User} returned by L{Delegator.getUser} doesn't have a password, a C{missing-password} value is added to the result. """ UserAPI().create([ (u'consumer', 'secret', u'Consumer', u'*****@*****.**'), (u'user', None, u'User', u'*****@*****.**')]) TwitterUserAPI().create(u'user', 1984245) consumer = getUser(u'consumer') OAuthConsumerAPI().register(consumer) self.store.commit() self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') delegator = Delegator(self.transact) deferred = delegator.getUser(u'consumer', provider, authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), dumps({'id': 1984245})) responseDeferred.callback(response) result = yield deferred self.assertTrue(result['access-token']) self.assertTrue(result['renewal-token']) del result['access-token'] del result['renewal-token'] self.assertEqual({'username': u'user', 'new-user': False, 'missing-password': True, 'uid': 1984245, 'data': {u'id': 1984245}}, result)
def testGetUserWithUserConflict(self): """ A L{DuplicateUserError} exception is raised if a L{User} with the same username as the Twitter user's screen name already exists, but is not associated with the Twitter UID. """ UserAPI().create([ (u'consumer', 'secret', u'Consumer', u'*****@*****.**'), (u'john', 'secret', u'John', u'*****@*****.**')]) self.store.commit() self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' consumer = getUser(u'consumer') provider = ServiceProvider(self.agent, 'https://example.com/verify') delegator = Delegator(self.transact) deferred = delegator.getUser(consumer, provider, authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), dumps({'id': 1984245, 'screen_name': u'john', 'name': u'John Doe'})) responseDeferred.callback(response) error = yield self.assertFailure(deferred, DuplicateUserError) self.assertEqual([u'john'], list(error.usernames))
def testGetUser(self): """ L{Delegator.getUser} returns a C{(User, data)} 2-tuple when the service provider successfully verifies credentials and a mapping between a Fluidinfo L{User} and the L{TwitterUser} being verified exists. """ UserAPI().create([ (u'consumer', 'secret', u'Consumer', u'*****@*****.**'), (u'user', 'secret', u'User', u'*****@*****.**')]) TwitterUserAPI().create(u'user', 1984245) consumer = getUser(u'consumer') OAuthConsumerAPI().register(consumer) self.store.commit() self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') delegator = Delegator(self.transact) deferred = delegator.getUser(u'consumer', provider, authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), dumps({'id': 1984245})) responseDeferred.callback(response) result = yield deferred self.assertTrue(result['access-token']) self.assertTrue(result['renewal-token']) del result['access-token'] del result['renewal-token'] self.assertEqual({'username': u'user', 'new-user': False, 'missing-password': False, 'uid': 1984245, 'data': {u'id': 1984245}}, result)
def testRenderWithUserConflict(self): """ A C{CONFLICT} HTTP status code is returned if the authorization is successfully verified by the service provider, but the username clashes with an existing L{User} that isn't linked to the Twitter UID. The offending username is returned UTF-8 and base64 encoded. """ username = u'john\N{HIRAGANA LETTER A}' UserAPI().create([(username, 'secret', u'John', u'*****@*****.**')]) self.store.commit() self.agent._connect = self._connect headers = { 'X-Verify-Credentials-Authorization': ['OAuth ...'], 'X-Auth-Service-Provider': [TWITTER_URL] } request = FakeRequest(headers=Headers(headers)) with login(u'anon', self.anonymous.objectID, self.transact) as session: resource = OAuthEchoResource(session, self.agent) self.assertEqual(NOT_DONE_YET, resource.render_GET(request)) [(_, responseDeferred)] = self.protocol.requests data = {'id': 1984245, 'screen_name': username, 'name': u'John'} response = FakeResponse(ResponseDone(), dumps(data)) responseDeferred.callback(response) yield resource.deferred self.assertEqual(CONFLICT, request.code) headers = dict(request.responseHeaders.getAllRawHeaders()) encodedUsername = b64encode(username.encode('utf-8')) self.assertEqual( { 'X-Fluiddb-Error-Class': ['UsernameConflict'], 'X-Fluiddb-Username': [encodedUsername], 'X-Fluiddb-Request-Id': [session.id] }, headers)
def test_json_content(self): d = json_content(self.response) self.protocol.dataReceived('{"msg":"hello!"}') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), {"msg": "hello!"})
def test_connectionLostDoneAfterError(self): """ Reconnect with initial interval after succesful reconnect. """ self.setUpState('connecting') # First connect fails. self.api.connectFail(ConnectError()) self.flushLoggedErrors(ConnectError) # A reconnect is attempted self.clock.advance(0.25) self.assertEqual(2, len(self.api.filterCalls)) # Reconnect succeeds. self.api.connected() # Connection closed by other party. self.api.protocol.connectionLost(failure.Failure(ResponseDone())) self.clock.advance(0) # A reconnect is attempted, but not before the back off delay. self.assertEqual(2, len(self.api.filterCalls)) self.clock.advance(DELAY_INITIAL) self.assertEqual(3, len(self.api.filterCalls)) # Second reconnect fails. self.api.connectFail(ConnectError()) self.flushLoggedErrors(ConnectError) # A reconnect is attempted, but not before the same back off delay. self.assertEqual(3, len(self.api.filterCalls)) self.clock.advance(0.25) self.assertEqual(4, len(self.api.filterCalls))
def deliverBody(self, protocol): """ Immediately deliver the entire response body to C{protocol}. """ protocol.makeConnection(_StubProducer()) protocol.dataReceived(self._responseBody) protocol.connectionLost(Failure(ResponseDone()))
def test_connectConnectingReconnect(self): """ Don't connect while connecting. """ self.setUpState('connecting') # Try to connect. self.monitor.connect(forceReconnect=True) # As we haven't connected yet, we cannot drop the connection yet, # and no reconnect should have taken place. self.clock.advance(DELAY_INITIAL) self.assertEqual(1, len(self.api.filterCalls)) # The initial connection is now established. self.api.connected() # A disconnect occurs right away. self.clock.advance(0) self.assertTrue(self.api.protocol.stopCalled) self.api.protocol.connectionLost(failure.Failure(ResponseDone())) self.clock.advance(0) # Now the reconnect occurs, wait for delayed calls. self.clock.advance(DELAY_INITIAL) self.assertEqual(2, len(self.api.filterCalls))
def test_closedNoTimeout(self): """ When the connection is done, there is no timeout. """ self.protocol.connectionLost(failure.Failure(ResponseDone())) self.assertEquals(None, self.protocol.timeOut) return self.protocol.deferred
def connection_lost(self, reason): self._stream_response = None self._stream_protocol = None if reason.check(PotentialDataLoss): reason = Failure(ResponseDone()) if self.disconnect_callback is not None: self.disconnect_callback(self, reason) self._reconnect()
def test_content(self): d = content(self.response) self.protocol.dataReceived(b'foo') self.protocol.dataReceived(b'bar') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), b'foobar')
def test_text_content_default_encoding_no_param(self): self.response.headers = Headers({'Content-Type': ['text/plain']}) d = text_content(self.response) self.protocol.dataReceived('\xa1') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), u'\xa1')
def test_text_content_default_encoding_no_header(self): self.response.headers = Headers() d = text_content(self.response) self.protocol.dataReceived(b'\xa1') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), u'\xa1')
def feed_response(protocol): config = { "relays": [ { "fingerprint": "boom", }, ] } protocol.dataReceived(json.dumps(config).encode()) protocol.connectionLost(Failure(ResponseDone()))
def feed_response(protocol): config = { "relays": [ { "fingerprint": "00786E43CCC5409753F25E36031C5CEA6EA43702", }, ] } protocol.dataReceived(json.dumps(config).encode()) protocol.connectionLost(Failure(ResponseDone()))
def test_no_error(self): """A response that is NOT too large.""" # Start sending data. self.protocol.dataReceived(b"12345") # Close the connection. self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.result.getvalue(), b"12345") self.assertEqual(self.deferred.result, 5)
def test_content_application_json_default_encoding(self): self.response.headers = Headers( {b'Content-Type': [b'application/json']}) d = text_content(self.response) self.protocol.dataReceived(b'gr\xc3\xbcn') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), u'grün')
def test_text_content(self): self.response.headers = Headers( {b'Content-Type': [b'text/plain; charset=utf-8']}) d = text_content(self.response) self.protocol.dataReceived(b'\xe2\x98\x83') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), u'\u2603')
def test_content_multiple_waiters(self): d1 = content(self.response) d2 = content(self.response) self.protocol.dataReceived(b'foo') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d1), b'foo') self.assertEqual(self.successResultOf(d2), b'foo') self.assertNotIdentical(d1, d2)
def test_multiple_packets(self): """Data should be accummulated through mutliple packets.""" # Start sending data. self.protocol.dataReceived(b"12") self.protocol.dataReceived(b"34") # Close the connection. self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.result.getvalue(), b"1234") self.assertEqual(self.deferred.result, 4)
def test_too_large(self): """A response which is too large raises an exception.""" # Start sending data. self.protocol.dataReceived(b"1234567890") # Close the connection. self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.result.getvalue(), b"1234567890") self.assertIsInstance(self.deferred.result, Failure) self.assertIsInstance(self.deferred.result.value, BodyExceededMaxSize) self._cleanup_error()
def testRenderWithNewUser(self): """ Missing L{User}s are created automatically and linked to L{TwitterUser}s for authorized UIDs. """ self.assertNotIn(u'john', UserAPI().get([u'john'])) self.store.commit() self.agent._connect = self._connect headers = { 'X-Verify-Credentials-Authorization': ['OAuth ...'], 'X-Auth-Service-Provider': [TWITTER_URL] } request = FakeRequest(headers=Headers(headers)) with login(u'anon', self.anonymous.objectID, self.transact) as session: resource = OAuthEchoResource(session, self.agent) self.assertEqual(NOT_DONE_YET, resource.render_GET(request)) [(_, responseDeferred)] = self.protocol.requests data = {'id': 1984245, 'screen_name': u'john', 'name': u'John'} response = FakeResponse(ResponseDone(), dumps(data)) responseDeferred.callback(response) result = yield resource.deferred self.assertTrue(result['access-token']) self.assertTrue(result['renewal-token']) del result['access-token'] del result['renewal-token'] self.assertEqual( { 'username': u'john', 'new-user': True, 'missing-password': True, 'data': data, 'uid': 1984245 }, result) self.assertEqual(OK, request.code) self.assertEqual(data, loads(request.written.getvalue())) headers = dict(request.responseHeaders.getAllRawHeaders()) self.assertTrue(headers['X-Fluiddb-Access-Token']) self.assertTrue(headers['X-Fluiddb-Renewal-Token']) del headers['X-Fluiddb-Access-Token'] del headers['X-Fluiddb-Renewal-Token'] self.assertEqual( { 'X-Fluiddb-New-User': ['true'], 'X-Fluiddb-Missing-Password': ['true'], 'X-Fluiddb-Username': ['am9obg=='] }, # username is in base64. headers) self.store.rollback() self.assertIn(u'john', UserAPI().get([u'john']))
def test_collect(self): data = [] d = collect(self.response, data.append) self.protocol.dataReceived(b'{') self.protocol.dataReceived(b'"msg": "hell') self.protocol.dataReceived(b'o"}') self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), None) self.assertEqual(data, [b'{', b'"msg": "hell', b'o"}'])
def test_json_content_utf16(self): """ JSON received is decoded according to the charset given in the Content-Type header. """ self.response.headers = Headers({ b'Content-Type': [b"application/json; charset='UTF-16LE'"], }) d = json_content(self.response) self.protocol.dataReceived(u'{"msg":"hëlló!"}'.encode('UTF-16LE')) self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), {u'msg': u'hëlló!'})
def test_json_content_unicode(self): """ When Unicode JSON content is received, the JSON text should be correctly decoded. RFC7159 (8.1): "JSON text SHALL be encoded in UTF-8, UTF-16, or UTF-32. The default encoding is UTF-8" """ self.response.headers = Headers() d = json_content(self.response) self.protocol.dataReceived(u'{"msg":"hëlló!"}'.encode('utf-8')) self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), {u'msg': u'hëlló!'})
def test_stopServiceConnected(self): """ Stopping the service while waiting to reconnect should abort. """ self.setUpState('connected') # Stop the service. self.monitor.stopService() # Actually lose the connection self.api.protocol.connectionLost(failure.Failure(ResponseDone())) # No reconnect should be attempted. self.clock.advance(DELAY_INITIAL) self.assertEqual(1, len(self.api.filterCalls))
def testVerifyCredentialsUnpacksSuccessfulResponse(self): """ L{ServiceProvider.verifyCredentials} returns a C{Deferred} that fires with the user object returned by Twitter, as a C{dict}, when credentials are successfully verified. """ self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') deferred = provider.verifyCredentials(authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), dumps({'id': 1984245})) responseDeferred.callback(response) user = yield deferred self.assertEqual(1984245, user['id'])
def test_connectionLostDone(self): """ When the connection is closed while connected, attempt reconnect. """ self.setUpState('connected') # Connection closed by other party. self.api.protocol.connectionLost(failure.Failure(ResponseDone())) self.clock.advance(0) # A reconnect is attempted, but not before the back off delay. self.assertEqual(1, len(self.api.filterCalls)) self.clock.advance(1) self.assertEqual(1, len(self.api.filterCalls)) self.clock.advance(DELAY_INITIAL - 1) self.assertEqual(2, len(self.api.filterCalls))
def test_text_content_unicode_headers(self): """ Header parsing is robust against unicode header names and values. """ self.response.headers = Headers({ b'Content-Type': [u'text/plain; charset="UTF-16BE"; u=ᛃ'.encode('utf-8')], u'Coördination'.encode('iso-8859-1'): [u'koʊˌɔrdɪˈneɪʃən'.encode('utf-8')], }) d = text_content(self.response) self.protocol.dataReceived(u'ᚠᚡ'.encode('UTF-16BE')) self.protocol.connectionLost(Failure(ResponseDone())) self.assertEqual(self.successResultOf(d), u'ᚠᚡ')