This repository has been archived by the owner on Feb 8, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 308
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial reimplementation based on change stream
- Loading branch information
1 parent
8f6dac5
commit f7a148d
Showing
7 changed files
with
152 additions
and
170 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
import sys | ||
|
||
from gratipay import wireup | ||
|
||
|
||
class teller(object): | ||
"""This is a context manager to log to Sentry. You have to pass in an | ||
``Environment`` object with a ``sentry_dsn`` attribute. | ||
""" | ||
|
||
def __init__(self, env, noop=None): | ||
try: | ||
sys.stdout = sys.stderr # work around aspen.log_dammit limitation; sigh | ||
self.tell_sentry = wireup.make_sentry_teller(env, noop) | ||
finally: | ||
sys.stdout = sys.__stdout__ | ||
|
||
def __enter__(self): | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_value, traceback): | ||
self.tell_sentry(exc_type, {}) | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
BEGIN; | ||
CREATE TABLE worker_coordination (npm_last_seq bigint not null default -1); | ||
INSERT INTO worker_coordination DEFAULT VALUES; | ||
END; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1,60 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import absolute_import, division, print_function, unicode_literals | ||
|
||
from subprocess import Popen, PIPE | ||
from gratipay.testing import Harness | ||
|
||
import pytest | ||
from gratipay.cli import sync_npm | ||
|
||
from gratipay import sync_npm | ||
from gratipay.testing import Harness | ||
|
||
class ProcessDocTests(Harness): | ||
|
||
def load(raw): | ||
serialized = Popen( ('env/bin/sync-npm', 'serialize', '/dev/stdin') | ||
, stdin=PIPE, stdout=PIPE | ||
).communicate(raw)[0] | ||
Popen( ('env/bin/sync-npm', 'upsert', '/dev/stdin') | ||
, stdin=PIPE, stdout=PIPE | ||
).communicate(serialized)[0] | ||
def test_returns_None_if_no_name(self): | ||
assert sync_npm.process_doc({}) is None | ||
|
||
def test_backfills_missing_keys(self): | ||
actual = sync_npm.process_doc({'name': 'foo'}) | ||
assert actual == {'name': 'foo', 'description': '', 'emails': []} | ||
|
||
class FailCollector: | ||
def test_extracts_maintainer_emails(self): | ||
doc = {'name': 'foo', 'maintainers': [{'email': '[email protected]'}]} | ||
assert sync_npm.process_doc(doc)['emails'] == ['[email protected]'] | ||
|
||
def __init__(self): | ||
self.fails = [] | ||
def test_skips_empty_emails(self): | ||
doc = {'name': 'foo', 'maintainers': [{'email': ''}, {'email': ' '}]} | ||
assert sync_npm.process_doc(doc)['emails'] == [] | ||
|
||
def __call__(self, fail, whatever): | ||
self.fails.append(fail) | ||
def test_sorts_emails(self): | ||
doc = {'name': 'foo', 'maintainers': [{'email': 'bob'}, {'email': 'alice'}]} | ||
assert sync_npm.process_doc(doc)['emails'] == ['alice', 'bob'] | ||
|
||
def test_dedupes_emails(self): | ||
doc = {'name': 'foo', 'maintainers': [{'email': 'alice'}, {'email': 'alice'}]} | ||
assert sync_npm.process_doc(doc)['emails'] == ['alice'] | ||
|
||
class Heck(Exception): | ||
pass | ||
|
||
class ConsumeChangeStreamTests(Harness): | ||
|
||
def change_stream(self, docs): | ||
def change_stream(seq): | ||
for i, doc in enumerate(docs): | ||
if i < seq: continue | ||
yield {'seq': i, 'doc': doc} | ||
return change_stream | ||
|
||
class Tests(Harness): | ||
|
||
def test_packages_starts_empty(self): | ||
assert self.db.all('select * from packages') == [] | ||
|
||
|
||
# sn - sync-npm | ||
|
||
def test_sn_inserts_packages(self): | ||
load(br''' | ||
{ "_updated": 1234567890 | ||
, "testing-package": | ||
{ "name":"testing-package" | ||
, "description":"A package for testing" | ||
, "maintainers":[{"email":"[email protected]"}] | ||
, "author": {"email":"[email protected]"} | ||
, "time":{"modified":"2015-09-12T03:03:03.135Z"} | ||
} | ||
} | ||
''') | ||
|
||
package = self.db.one('select * from packages') | ||
assert package.package_manager == 'npm' | ||
assert package.name == 'testing-package' | ||
assert package.description == 'A package for testing' | ||
assert package.name == 'testing-package' | ||
|
||
|
||
def test_sn_handles_quoting(self): | ||
load(br''' | ||
{ "_updated": 1234567890 | ||
, "testi\\\"ng-pa\\\"ckage": | ||
{ "name":"testi\\\"ng-pa\\\"ckage" | ||
, "description":"A package for \"testing\"" | ||
, "maintainers":[{"email":"alice@\"example\".com"}] | ||
, "author": {"email":"\\\\\"bob\\\\\"@example.com"} | ||
, "time":{"modified":"2015-09-12T03:03:03.135Z"} | ||
} | ||
} | ||
''') | ||
def test_consumes_change_stream(self): | ||
docs = [ {'name': 'foo', 'description': 'Foo.'} | ||
, {'name': 'foo', 'description': 'Foo?'} | ||
, {'name': 'foo', 'description': 'Foo!'} | ||
] | ||
sync_npm.consume_change_stream(self.change_stream(docs), self.db) | ||
|
||
package = self.db.one('select * from packages') | ||
assert package.package_manager == 'npm' | ||
assert package.name == r'testi\"ng-pa\"ckage' | ||
assert package.description == 'A package for "testing"' | ||
assert package.emails == ['alice@"example".com', r'\\"bob\\"@example.com'] | ||
|
||
|
||
def test_sn_handles_empty_description_and_emails(self): | ||
load(br''' | ||
{ "_updated": 1234567890 | ||
, "empty-description": | ||
{ "name":"empty-description" | ||
, "description":"" | ||
, "time":{"modified":"2015-09-12T03:03:03.135Z"} | ||
} | ||
} | ||
''') | ||
|
||
package = self.db.one('select * from packages') | ||
assert package.package_manager == 'npm' | ||
assert package.name == 'empty-description' | ||
assert package.description == '' | ||
assert package.name == 'foo' | ||
assert package.description == 'Foo!' | ||
assert package.emails == [] | ||
|
||
|
||
# with sentry(env) | ||
|
||
def test_with_sentry_logs_to_sentry_and_raises(self): | ||
class env: sentry_dsn = '' | ||
noop = FailCollector() | ||
with pytest.raises(Heck): | ||
with sync_npm.sentry(env, noop): | ||
raise Heck | ||
assert noop.fails == [Heck] |
Oops, something went wrong.