def setUp(self):
        reactor.suggestThreadPoolSize(1)
        connection_string = os.environ.get("SHORTENER_TEST_CONNECTION_STRING",
                                           "sqlite://")

        self.account = 'test-account'
        cfg = {
            'host_domain': 'http://wtxt.io',
            'account': self.account,
            'connection_string': connection_string,
            'graphite_endpoint': 'tcp:www.example.com:80',
            'handlers': [
                {
                    'dump': 'shortener.handlers.dump.Dump'
                },
            ],
        }
        self.pool = HTTPConnectionPool(reactor, persistent=False)
        self.service = ShortenerServiceApp(reactor=reactor, config=cfg)

        self.tr = DisconnectingStringTransport()
        endpoint = StringTransportClientEndpoint(reactor, self.tr)
        self.service.metrics.carbon_client = CarbonClientService(endpoint)
        self.service.metrics.carbon_client.startService()
        yield self.service.metrics.carbon_client.connect_d

        site = Site(self.service.app.resource())
        self.listener = reactor.listenTCP(0, site, interface='localhost')
        self.listener_port = self.listener.getHost().port
        self._drop_tables()
        self.conn = yield self.service.engine.connect()
        self.addCleanup(self.listener.loseConnection)
        self.addCleanup(self.pool.closeCachedConnections)
    def setUp(self):
        reactor.suggestThreadPoolSize(1)
        connection_string = os.environ.get("SHORTENER_TEST_CONNECTION_STRING", "sqlite://")

        self.account = "test-account"
        cfg = {
            "host_domain": "http://wtxt.io",
            "account": self.account,
            "connection_string": connection_string,
            "graphite_endpoint": "tcp:www.example.com:80",
            "handlers": [{"dump": "shortener.handlers.dump.Dump"}],
        }
        self.pool = HTTPConnectionPool(reactor, persistent=False)
        self.service = ShortenerServiceApp(reactor=reactor, config=cfg)

        self.tr = DisconnectingStringTransport()
        endpoint = StringTransportClientEndpoint(reactor, self.tr)
        self.service.metrics.carbon_client = CarbonClientService(endpoint)
        self.service.metrics.carbon_client.startService()
        yield self.service.metrics.carbon_client.connect_d

        site = Site(self.service.app.resource())
        self.listener = reactor.listenTCP(0, site, interface="localhost")
        self.listener_port = self.listener.getHost().port
        self._drop_tables()
        self.conn = yield self.service.engine.connect()
        self.addCleanup(self.listener.loseConnection)
        self.addCleanup(self.pool.closeCachedConnections)
Esempio n. 3
0
def makeService(options):
    config_file = options['config']
    with open(config_file, 'r') as fp:
        config = dict(yaml.safe_load(fp))

    app = ShortenerServiceApp(reactor=reactor, config=config)

    site = server.Site(app.app.resource())

    main_service = service.MultiService()

    app_service = strports.service(config.get('port', DEFAULT_PORT), site)
    app_service.setServiceParent(main_service)

    app.metrics.carbon_client.setServiceParent(main_service)

    return main_service
class TestShortenerServiceApp(TestCase):
    timeout = 5

    def _drop_tables(self):
        # NOTE: This is a blocking operation!
        md = MetaData(bind=self.service.engine._engine)
        md.reflect()
        md.drop_all()
        assert self.service.engine._engine.table_names() == []

    @inlineCallbacks
    def setUp(self):
        reactor.suggestThreadPoolSize(1)
        connection_string = os.environ.get("SHORTENER_TEST_CONNECTION_STRING",
                                           "sqlite://")

        self.account = 'test-account'
        cfg = {
            'host_domain': 'http://wtxt.io',
            'account': self.account,
            'connection_string': connection_string,
            'graphite_endpoint': 'tcp:www.example.com:80',
            'handlers': [
                {
                    'dump': 'shortener.handlers.dump.Dump'
                },
            ],
        }
        self.pool = HTTPConnectionPool(reactor, persistent=False)
        self.service = ShortenerServiceApp(reactor=reactor, config=cfg)

        self.tr = DisconnectingStringTransport()
        endpoint = StringTransportClientEndpoint(reactor, self.tr)
        self.service.metrics.carbon_client = CarbonClientService(endpoint)
        self.service.metrics.carbon_client.startService()
        yield self.service.metrics.carbon_client.connect_d

        site = Site(self.service.app.resource())
        self.listener = reactor.listenTCP(0, site, interface='localhost')
        self.listener_port = self.listener.getHost().port
        self._drop_tables()
        self.conn = yield self.service.engine.connect()
        self.addCleanup(self.listener.loseConnection)
        self.addCleanup(self.pool.closeCachedConnections)

    @inlineCallbacks
    def tearDown(self):
        yield self.conn.close()
        self._drop_tables()
        yield self.listener.loseConnection()

    def make_url(self, path):
        return 'http://localhost:%s%s' % (self.listener_port, path)

    @inlineCallbacks
    def test_create_url_simple(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        payload = {
            'long_url': 'foo',
            'user_token': 'bar',
        }

        self.assertEqual(self.tr.value(), "")

        resp = yield treq.put(self.make_url('/api/create'),
                              data=json.dumps(payload),
                              allow_redirects=False,
                              pool=self.pool)

        result = yield treq.json_content(resp)
        self.assertEqual(result['short_url'], 'http://wtxt.io/qr0')
        self.assertTrue(
            self.tr.value().startswith("test-account.wtxtio.created.count 1"))

    @inlineCallbacks
    def test_create_url_no_user_token(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        payload = {'long_url': 'foo'}
        resp = yield treq.put(self.make_url('/api/create'),
                              data=json.dumps(payload),
                              allow_redirects=False,
                              pool=self.pool)

        result = yield treq.json_content(resp)
        self.assertEqual(result['short_url'], 'http://wtxt.io/qr0')
        self.assertTrue(
            self.tr.value().startswith("test-account.wtxtio.created.count 1"))

    @inlineCallbacks
    def test_resolve_url_simple(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        yield self.service.shorten_url(url)

        self.assertTrue(
            self.tr.value().startswith("test-account.wtxtio.created.count 1"))

        resp = yield treq.get(self.make_url('/qr0'),
                              allow_redirects=False,
                              pool=self.pool)

        self.assertEqual(resp.code, 301)
        [location] = resp.headers.getRawHeaders('location')
        self.assertEqual(location, url)

        conn_queue = self.tr.value().splitlines()
        self.assertTrue(
            conn_queue[1].startswith("test-account.wtxtio.expanded.count 1"))

    @inlineCallbacks
    def test_resolve_url_404(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        yield self.service.shorten_url(url)

        self.assertTrue(
            self.tr.value().startswith("test-account.wtxtio.created.count 1"))

        resp = yield treq.get(self.make_url('/1Tx'),
                              allow_redirects=False,
                              pool=self.pool)

        self.assertEqual(resp.code, 404)
        conn_queue = self.tr.value().splitlines()
        self.assertTrue(
            conn_queue[1].startswith("test-account.wtxtio.invalid.count 1"))

    @inlineCallbacks
    def test_url_shortening(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        long_url = 'http://en.wikipedia.org/wiki/Cthulhu'
        short_url = yield self.service.shorten_url(long_url)
        self.assertEqual(short_url, 'http://wtxt.io/qr0')
        self.assertTrue(
            self.tr.value().startswith("test-account.wtxtio.created.count 1"))

    @inlineCallbacks
    def test_short_url_generation(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        url1 = yield self.service.shorten_url(url + '1')
        url2 = yield self.service.shorten_url(url + '2')
        url3 = yield self.service.shorten_url(url + '3')
        url4 = yield self.service.shorten_url(url + '4')
        urls = [url1, url2, url3, url4]
        self.assertEqual(len(set(urls)), 4)
        conn_queue = self.tr.value().splitlines()
        self.assertEqual(len(conn_queue), 4)

    @inlineCallbacks
    def test_repeat_url_generation(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        url1 = yield self.service.shorten_url(url + '1')
        url2 = yield self.service.shorten_url(url + '2')
        url3 = yield self.service.shorten_url(url + '2')
        url4 = yield self.service.shorten_url(url + '1')
        urls = [url1, url2, url3, url4]
        self.assertEqual(len(set(urls)), 2)
        conn_queue = self.tr.value().splitlines()
        self.assertEqual(len(conn_queue), 2)

    @inlineCallbacks
    def test_resolve_url(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        yield self.service.shorten_url(url + '1')
        yield self.service.shorten_url(url + '2')
        yield self.service.shorten_url(url + '3')
        yield self.service.shorten_url(url + '4')

        result = yield self.service.get_row_by_short_url('qH0')
        self.assertEqual(result['long_url'], url + '4')

    @inlineCallbacks
    def test_resolve_url_hits_counter(self):
        tables = ShortenerTables(self.account, self.conn)
        yield tables.create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        yield self.service.shorten_url(url)

        yield self.service.get_row_by_short_url('qr0')
        yield self.service.get_row_by_short_url('qr0')
        result = yield self.service.get_row_by_short_url('qr0')

        audit = yield tables.get_audit_row(result['id'])
        self.assertEqual(audit['hits'], 3)

    @inlineCallbacks
    def test_short_url_sequencing(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        urls = [''.join([url, str(a)]) for a in range(1, 10)]
        for u in urls:
            yield self.service.shorten_url(u)

        result = yield self.service.get_row_by_short_url('qs0')
        self.assertEqual(result['long_url'], url + '5')

        result = yield self.service.get_row_by_short_url('qp0')
        self.assertEqual(result['long_url'], url + '6')

        conn_queue = self.tr.value().splitlines()
        self.assertEqual(len(conn_queue), 9)

    @inlineCallbacks
    def test_account_init(self):
        resp = yield treq.get(self.make_url('/api/init'),
                              allow_redirects=False,
                              pool=self.pool)
        result = yield treq.json_content(resp)
        self.assertTrue(result['created'])
Esempio n. 5
0
class TestHandlers(TestCase):
    timeout = 5

    def _drop_tables(self):
        # NOTE: This is a blocking operation!
        md = MetaData(bind=self.service.engine._engine)
        md.reflect()
        md.drop_all()
        assert self.service.engine._engine.table_names() == []

    @inlineCallbacks
    def setUp(self):
        reactor.suggestThreadPoolSize(1)
        connection_string = os.environ.get("SHORTENER_TEST_CONNECTION_STRING",
                                           "sqlite://")

        self.account = 'test-account'
        cfg = {
            'host_domain': 'http://wtxt.io',
            'account': self.account,
            'connection_string': connection_string,
            'graphite_endpoint': 'tcp:www.example.com:80',
            'handlers': [
                {
                    'dump': 'shortener.handlers.dump.Dump'
                },
            ],
        }
        self.pool = HTTPConnectionPool(reactor, persistent=False)
        self.service = ShortenerServiceApp(reactor=reactor, config=cfg)

        self.tr = DisconnectingStringTransport()
        endpoint = StringTransportClientEndpoint(reactor, self.tr)
        self.service.metrics.carbon_client = CarbonClientService(endpoint)
        self.service.metrics.carbon_client.startService()
        yield self.service.metrics.carbon_client.connect_d

        site = Site(self.service.app.resource())
        self.listener = reactor.listenTCP(0, site, interface='localhost')
        self.listener_port = self.listener.getHost().port
        self._drop_tables()
        self.conn = yield self.service.engine.connect()
        self.addCleanup(self.listener.loseConnection)
        self.addCleanup(self.pool.closeCachedConnections)

    def make_url(self, path):
        return 'http://localhost:%s%s' % (self.listener_port, path)

    @inlineCallbacks
    def test_api_dump(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        yield self.service.shorten_url(url, 'test-user')
        yield treq.get(self.make_url('/qr0'),
                       allow_redirects=False,
                       pool=self.pool)

        resp = yield treq.get(self.make_url('/api/handler/dump?url=qr0'),
                              allow_redirects=False,
                              pool=self.pool)

        self.assertEqual(resp.code, 200)
        result = yield treq.json_content(resp)
        self.assertEqual(result['user_token'], 'test-user')
        self.assertEqual(result['short_url'], 'qr0')
        self.assertEqual(result['long_url'], url)
        self.assertEqual(result['hits'], 1)
        self.assertEqual(result['domain'], 'en.wikipedia.org')

    @inlineCallbacks
    def test_api_dump_invalid_querystring(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = 'http://en.wikipedia.org/wiki/Cthulhu'
        yield self.service.shorten_url(url, 'test-user')
        yield treq.get(self.make_url('/qr0'),
                       allow_redirects=False,
                       pool=self.pool)

        resp = yield treq.get(self.make_url('/api/handler/dump'),
                              allow_redirects=False,
                              pool=self.pool)

        self.assertEqual(resp.code, 400)
class TestHandlers(TestCase):
    timeout = 5

    def _drop_tables(self):
        # NOTE: This is a blocking operation!
        md = MetaData(bind=self.service.engine._engine)
        md.reflect()
        md.drop_all()
        assert self.service.engine._engine.table_names() == []

    @inlineCallbacks
    def setUp(self):
        reactor.suggestThreadPoolSize(1)
        connection_string = os.environ.get("SHORTENER_TEST_CONNECTION_STRING", "sqlite://")

        self.account = "test-account"
        cfg = {
            "host_domain": "http://wtxt.io",
            "account": self.account,
            "connection_string": connection_string,
            "graphite_endpoint": "tcp:www.example.com:80",
            "handlers": [{"dump": "shortener.handlers.dump.Dump"}],
        }
        self.pool = HTTPConnectionPool(reactor, persistent=False)
        self.service = ShortenerServiceApp(reactor=reactor, config=cfg)

        self.tr = DisconnectingStringTransport()
        endpoint = StringTransportClientEndpoint(reactor, self.tr)
        self.service.metrics.carbon_client = CarbonClientService(endpoint)
        self.service.metrics.carbon_client.startService()
        yield self.service.metrics.carbon_client.connect_d

        site = Site(self.service.app.resource())
        self.listener = reactor.listenTCP(0, site, interface="localhost")
        self.listener_port = self.listener.getHost().port
        self._drop_tables()
        self.conn = yield self.service.engine.connect()
        self.addCleanup(self.listener.loseConnection)
        self.addCleanup(self.pool.closeCachedConnections)

    def make_url(self, path):
        return "http://localhost:%s%s" % (self.listener_port, path)

    @inlineCallbacks
    def test_api_dump(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = "http://en.wikipedia.org/wiki/Cthulhu"
        yield self.service.shorten_url(url, "test-user")
        yield treq.get(self.make_url("/qr0"), allow_redirects=False, pool=self.pool)

        resp = yield treq.get(self.make_url("/api/handler/dump?url=qr0"), allow_redirects=False, pool=self.pool)

        self.assertEqual(resp.code, 200)
        result = yield treq.json_content(resp)
        self.assertEqual(result["user_token"], "test-user")
        self.assertEqual(result["short_url"], "qr0")
        self.assertEqual(result["long_url"], url)
        self.assertEqual(result["hits"], 1)
        self.assertEqual(result["domain"], "en.wikipedia.org")

    @inlineCallbacks
    def test_api_dump_invalid_querystring(self):
        yield ShortenerTables(self.account, self.conn).create_tables()

        url = "http://en.wikipedia.org/wiki/Cthulhu"
        yield self.service.shorten_url(url, "test-user")
        yield treq.get(self.make_url("/qr0"), allow_redirects=False, pool=self.pool)

        resp = yield treq.get(self.make_url("/api/handler/dump"), allow_redirects=False, pool=self.pool)

        self.assertEqual(resp.code, 400)