aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--bootstrap.py11
-rw-r--r--pyaggr3g470r/controllers/abstract.py17
-rw-r--r--pyaggr3g470r/lib/crawler.py6
-rw-r--r--pyaggr3g470r/models/__init__.py3
-rw-r--r--pyaggr3g470r/models/article.py11
-rw-r--r--pyaggr3g470r/views/views.py15
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/base.py41
-rw-r--r--tests/controllers/__init__.py5
-rw-r--r--tests/controllers/article.py23
-rw-r--r--tests/controllers/feed.py25
-rw-r--r--tests/fixtures.py31
13 files changed, 164 insertions, 25 deletions
diff --git a/.gitignore b/.gitignore
index 62dbbec8..a9beb28d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ build
conf/conf.cfg
+.coverage
diff --git a/bootstrap.py b/bootstrap.py
index 7a5a9b6e..9cab7417 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -22,7 +22,15 @@ from flask.ext.sqlalchemy import SQLAlchemy
# Create Flask application
application = Flask('pyaggr3g470r')
-application.debug = conf.LOG_LEVEL <= logging.DEBUG
+if os.environ.get('PYAGG_TESTING', False) == 'true':
+ application.debug = logging.DEBUG
+ application.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
+ application.config['TESTING'] = True
+else:
+ application.debug = conf.LOG_LEVEL <= logging.DEBUG
+ application.config['SQLALCHEMY_DATABASE_URI'] \
+ = conf.SQLALCHEMY_DATABASE_URI
+
scheme, domain, _, _, _ = urlsplit(conf.PLATFORM_URL)
application.config['SERVER_NAME'] = domain
application.config['PREFERRED_URL_SCHEME'] = scheme
@@ -33,7 +41,6 @@ set_logging(conf.LOG_PATH, log_level=conf.LOG_LEVEL)
application.config['SECRET_KEY'] = getattr(conf, 'WEBSERVER_SECRET', None)
if not application.config['SECRET_KEY']:
application.config['SECRET_KEY'] = os.urandom(12)
-application.config['SQLALCHEMY_DATABASE_URI'] = conf.SQLALCHEMY_DATABASE_URI
application.config['RECAPTCHA_USE_SSL'] = True
application.config['RECAPTCHA_PUBLIC_KEY'] = conf.RECAPTCHA_PUBLIC_KEY
diff --git a/pyaggr3g470r/controllers/abstract.py b/pyaggr3g470r/controllers/abstract.py
index 3ea4fbff..f67f932c 100644
--- a/pyaggr3g470r/controllers/abstract.py
+++ b/pyaggr3g470r/controllers/abstract.py
@@ -19,9 +19,12 @@ class AbstractController(object):
allowing for a kind of "super user" mode.
"""
self.user_id = user_id
- if self.user_id is not None \
- and self.user_id != g.user.id and not g.user.is_admin():
- self.user_id = g.user.id
+ try:
+ if self.user_id is not None \
+ and self.user_id != g.user.id and not g.user.is_admin():
+ self.user_id = g.user.id
+ except RuntimeError: # passing on out of context errors
+ pass
def _to_filters(self, **filters):
"""
@@ -67,13 +70,13 @@ class AbstractController(object):
def get(self, **filters):
"""Will return one single objects corresponding to filters"""
obj = self._get(**filters).first()
- if not obj:
- raise NotFound({'message': 'No %r (%r)'
- % (self._db_cls.__class__.__name__, filters)})
- if not self._has_right_on(obj):
+ if obj and not self._has_right_on(obj):
raise Forbidden({'message': 'No authorized to access %r (%r)'
% (self._db_cls.__class__.__name__, filters)})
+ if not obj:
+ raise NotFound({'message': 'No %r (%r)'
+ % (self._db_cls.__class__.__name__, filters)})
return obj
def create(self, **attrs):
diff --git a/pyaggr3g470r/lib/crawler.py b/pyaggr3g470r/lib/crawler.py
index cae3bd8f..c4c80ad4 100644
--- a/pyaggr3g470r/lib/crawler.py
+++ b/pyaggr3g470r/lib/crawler.py
@@ -225,9 +225,9 @@ class FeedCrawler(AbstractCrawler):
response.raise_for_status()
except Exception as error:
error_count = self.feed['error_count'] + 1
- logger.warn('%r %r - an error occured while fetching feed; bumping'
- ' error count to %r', self.feed['id'],
- self.feed['title'], error_count)
+ logger.error('%r %r - an error occured while fetching '
+ 'feed; bumping error count to %r', self.feed['id'],
+ self.feed['title'], error_count)
future = self.query_pyagg('put', 'feed/%d' % self.feed['id'],
{'error_count': error_count,
'last_error': str(error)})
diff --git a/pyaggr3g470r/models/__init__.py b/pyaggr3g470r/models/__init__.py
index 42903f4e..ba52b0de 100644
--- a/pyaggr3g470r/models/__init__.py
+++ b/pyaggr3g470r/models/__init__.py
@@ -98,4 +98,5 @@ def db_create(db):
user1.roles.extend([role_admin, role_user])
db.session.add(user1)
- db.session.commit() \ No newline at end of file
+ db.session.commit()
+ return role_admin, role_user
diff --git a/pyaggr3g470r/models/article.py b/pyaggr3g470r/models/article.py
index 58cd0384..16f15b9e 100644
--- a/pyaggr3g470r/models/article.py
+++ b/pyaggr3g470r/models/article.py
@@ -66,14 +66,15 @@ class Article(db.Model):
.order_by(asc("Article.date")).first()
def __repr__(self):
- return json.dumps({
- "title": self.title,
- "link": self.link,
- "content": self.content
- })
+ return json.dumps({"title": self.title,
+ "link": self.link,
+ "content": self.content
+ })
def dump(self):
return {"id": self.id,
+ "user_id": self.user_id,
+ "entry_id": self.entry_id,
"title": self.title,
"link": self.link,
"content": self.content,
diff --git a/pyaggr3g470r/views/views.py b/pyaggr3g470r/views/views.py
index 1b834d0e..3abc76df 100644
--- a/pyaggr3g470r/views/views.py
+++ b/pyaggr3g470r/views/views.py
@@ -169,7 +169,6 @@ def login():
session['email'] = form.email.data
identity_changed.send(current_app._get_current_object(),
identity=Identity(user.id))
- flash(gettext("Logged in successfully."), 'success')
return redirect(url_for('home'))
return render_template('login.html', form=form)
@@ -190,7 +189,8 @@ def logout():
session.pop(key, None)
# Tell Flask-Principal the user is anonymous
- identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity())
+ identity_changed.send(current_app._get_current_object(),
+ identity=AnonymousIdentity())
flash(gettext("Logged out successfully."), 'success')
return redirect(url_for('login'))
@@ -224,8 +224,9 @@ def signup():
# Send the confirmation email
try:
notifications.new_account_notification(user)
- except Exception as e:
- flash(gettext('Problem while sending activation email') + ': ' + str(e), 'danger')
+ except Exception as error:
+ flash(gettext('Problem while sending activation email: %(error)s',
+ error=error), 'danger')
return redirect(url_for('home'))
flash(gettext('Your account has been created. '
@@ -286,9 +287,9 @@ def render_home(filters=None, head_titles=None,
if page_to_render == 'search':
kwargs['query'] = request.args.get('query', '')
kwargs['search_title'] = request.args.get('search_title', 'off')
- kwargs['search_content'] = request.args.get('search_content', 'off')
- if kwargs['search_title']=='off' and \
- kwargs['search_content']=='off':
+ kwargs['search_content'] = request.args.get(
+ 'search_content', 'off')
+ if kwargs['search_title'] == kwargs['search_content'] == 'off':
kwargs['search_title'] = 'on'
return url_for(page_to_render, filter_=filter_, sort_=sort_,
limit=limit, feed_id=feed_id, **kwargs)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/base.py b/tests/base.py
new file mode 100644
index 00000000..d6f62583
--- /dev/null
+++ b/tests/base.py
@@ -0,0 +1,41 @@
+import os
+os.environ['PYAGG_TESTING'] = 'true'
+
+import unittest
+from bootstrap import db
+import runserver
+from tests.fixtures import populate_db, reset_db
+from werkzeug.exceptions import NotFound
+
+
+class BasePyaggTest(unittest.TestCase):
+ _contr_cls = None
+
+ def _get_from_contr(self, obj_id, user_id=None):
+ return self._contr_cls(user_id).get(id=obj_id).dump()
+
+ def _test_controller_rights(self, obj, user_id):
+ obj_id = obj['id']
+ self.assertEquals(obj, self._get_from_contr(obj_id))
+ self.assertEquals(obj, self._get_from_contr(obj_id, user_id))
+ # fetching non existent object
+ self.assertRaises(NotFound, self._get_from_contr, 99, user_id)
+ # fetching object with inexistent user
+ self.assertRaises(NotFound, self._get_from_contr, obj_id, 99)
+ # fetching object with wrong user
+ self.assertRaises(NotFound, self._get_from_contr, obj_id, user_id + 1)
+ self.assertRaises(NotFound, self._contr_cls().delete, 99)
+ self.assertRaises(NotFound, self._contr_cls(user_id).delete, 99)
+ self.assertEquals(obj['id'],
+ self._contr_cls(user_id).delete(obj_id).id)
+ self.assertRaises(NotFound, self._contr_cls(user_id).delete, obj_id)
+
+ def setUp(self):
+ populate_db(db)
+
+ def tearDown(self):
+ reset_db(db)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/controllers/__init__.py b/tests/controllers/__init__.py
new file mode 100644
index 00000000..26922c43
--- /dev/null
+++ b/tests/controllers/__init__.py
@@ -0,0 +1,5 @@
+from tests.controllers.feed import FeedControllerTest
+from tests.controllers.article import ArticleControllerTest
+
+
+__all__ = ['FeedControllerTest', 'ArticleControllerTest']
diff --git a/tests/controllers/article.py b/tests/controllers/article.py
new file mode 100644
index 00000000..9a635fa7
--- /dev/null
+++ b/tests/controllers/article.py
@@ -0,0 +1,23 @@
+from tests.base import BasePyaggTest
+from pyaggr3g470r.controllers import ArticleController
+
+
+class ArticleControllerTest(BasePyaggTest):
+ _contr_cls = ArticleController
+
+ def test_controller(self):
+ article = ArticleController(2).read()[0].dump()
+ self.assertFalse(article['readed'])
+ article['readed'] = True # article get read when retreived through get
+ self._test_controller_rights(article, article['user_id'])
+ self.assertEquals(0, len(list(ArticleController().challenge(
+ [{'id': art.id} for art in ArticleController(3).read()]))))
+ self.assertEquals(9, len(list(ArticleController(2).challenge(
+ [{'id': art.id} for art in ArticleController(3).read()]))))
+ self.assertEquals(9, len(list(ArticleController(2).challenge(
+ [{'entry_id': art.id} for art in ArticleController(3).read()]
+ ))))
+ self.assertEquals({1: 2, 2: 3, 3: 3},
+ ArticleController(2).get_unread())
+ self.assertEquals({4: 3, 5: 3, 6: 3},
+ ArticleController(3).get_unread())
diff --git a/tests/controllers/feed.py b/tests/controllers/feed.py
new file mode 100644
index 00000000..924b8ddd
--- /dev/null
+++ b/tests/controllers/feed.py
@@ -0,0 +1,25 @@
+from tests.base import BasePyaggTest
+from pyaggr3g470r.controllers import FeedController
+from pyaggr3g470r.controllers import ArticleController
+
+
+class FeedControllerTest(BasePyaggTest):
+ _contr_cls = FeedController
+
+ def test_controller(self):
+ feed = FeedController(2).read()[0].dump()
+ self.assertTrue(3,
+ ArticleController().read(feed_id=feed['id']).count())
+ self._test_controller_rights(feed, feed['user_id'])
+ # checking articles are deleted after the feed has been deleted
+ self.assertFalse(0,
+ ArticleController().read(feed_id=feed['id']).count())
+
+ self.assertEquals(3, len(FeedController(3).list_fetchable()))
+ self.assertEquals(0, len(FeedController(3).list_fetchable()))
+ self.assertEquals(2, len(FeedController().list_fetchable()))
+ self.assertEquals(0, len(FeedController().list_fetchable()))
+ self.assertEquals(3,
+ len(FeedController(3).list_fetchable(refresh_rate=0)))
+ self.assertEquals(5,
+ len(FeedController().list_fetchable(refresh_rate=0)))
diff --git a/tests/fixtures.py b/tests/fixtures.py
new file mode 100644
index 00000000..6aab645d
--- /dev/null
+++ b/tests/fixtures.py
@@ -0,0 +1,31 @@
+from pyaggr3g470r.models import db_create, db_empty, User, Article, Feed
+
+
+def populate_db(db):
+ role_admin, role_user = db_create(db)
+ user1, user2 = [User(nickname=name, email="%s@test.te" % name,
+ pwdhash=name, roles=[role_user], activation_key="")
+ for name in ["user1", "user2"]]
+ db.session.add(user1)
+ db.session.add(user2)
+ db.session.commit()
+
+ for user in (user1, user2):
+ for feed_name in ['feed1', 'feed2', 'feed3']:
+ feed = Feed(link=feed_name, user_id=user.id,
+ title="%r %r" % (user.nickname, feed_name))
+ db.session.add(feed)
+ db.session.commit()
+ for article in ['article1', 'article2', 'article3']:
+ entry = "%s %s %s" % (user.nickname, feed.title, article)
+ article = Article(entry_id=entry, link=article,
+ feed_id=feed.id, user_id=user.id,
+ title=entry, content=article)
+ db.session.add(article)
+ db.session.commit()
+
+ db.session.commit()
+
+
+def reset_db(db):
+ db_empty(db)
bgstack15