added NewsSource routes with tests

master
androiddrew 8 years ago
parent 095ef0b1db
commit a02d696f9f

@ -1,19 +1,76 @@
from apistar import Include, Route from apistar import Include, Route, http
from apistar.interfaces import Router
from apistar.frameworks.wsgi import WSGIApp as App from apistar.frameworks.wsgi import WSGIApp as App
from apistar.backends import sqlalchemy_backend from apistar.backends import sqlalchemy_backend
from apistar.backends.sqlalchemy_backend import Session from apistar.backends.sqlalchemy_backend import Session
from apistar.handlers import docs_urls, static_urls from apistar.handlers import docs_urls, static_urls
from .models import Base from .models import Base, NewsArticle, NewsSource
from .renders import JSONRenderer from .renders import JSONRenderer
from .schema import NewsSourceSchema
news_source_schema = NewsSourceSchema()
def welcome():
return {"message": "welcome to news"} def get_sources(session: Session):
sources = session.query(NewsSource).all()
return news_source_schema.dump(sources, many=True).data
def add_source(session: Session, request_data: http.RequestData, router: Router):
news_source_data, errors = news_source_schema.load(request_data)
if errors:
msg = {"message": "400 Bad Request", "error": errors}
return http.Response(msg, status=400)
news_source = NewsSource(**news_source_data)
session.add(news_source)
session.flush()
headers = {"Location": router.reverse_url('get_source', {"id": news_source.id})}
return http.Response(news_source_schema.dump(news_source).data, status=201, headers=headers)
def delete_source(session: Session, id: int):
"""Delete a single News Sources from the collection by id"""
news_source = session.query(NewsSource).filter_by(id=id).one_or_none()
if news_source is None:
msg = {"message": "404 Not Found"}
return http.Response(msg, status=404)
msg = {"message": "200 OK"}
return http.Response(msg, status=200)
def get_source(session: Session, id: int):
news_source = session.query(NewsSource).filter_by(id=id).one_or_none()
if news_source is None:
msg = {"message": "404 Not Found"}
return http.Response(msg, status=404)
return http.Response(news_source_schema.dump(news_source).data, status=200)
def get_articles(session: Session):
articles = session.query(NewsArticle).all()
return [article.to_dict() for article in articles]
def add_article(session: Session):
pass
def delete_article(session: Session):
pass
routes = [ routes = [
Route('/', 'GET', welcome), Route('/sources', 'GET', get_sources),
Route('/sources', 'POST', add_source),
Route('/sources/{id}', 'GET', get_source),
Route('/sources/{id}', 'DELETE', delete_source),
Route('/articles', 'GET', get_articles),
Include('/docs', docs_urls), Include('/docs', docs_urls),
Include('/static', static_urls) Include('/static', static_urls)
] ]
@ -26,7 +83,6 @@ _settings = {
'RENDERERS': [JSONRenderer()] 'RENDERERS': [JSONRenderer()]
} }
routes = routes routes = routes
commands = sqlalchemy_backend.commands commands = sqlalchemy_backend.commands

@ -9,7 +9,7 @@ from apistar.renderers import Renderer
def extended_encoder(obj): def extended_encoder(obj):
"""JSON encoder function with support for ISO 8601 datetime serialization and Decimal to float casting""" """JSON encoder function with support for ISO 8601 datetime serialization and Decimal to float casting"""
if isinstance(obj, dt.datetime): if isinstance(obj, dt.datetime):
return obj.isoformat() return obj.timestamp()
elif isinstance(obj, decimal.Decimal): elif isinstance(obj, decimal.Decimal):
return float(obj) return float(obj)

@ -0,0 +1,13 @@
from marshmallow import Schema, fields
class NewsSourceSchema(Schema):
id = fields.Int(dump_only=True)
created_date = fields.DateTime(dump_only=True)
modified_date = fields.DateTime()
url = fields.URL()
source_name = fields.Str(required=True, error_messages={'required': 'NewsSource name is a required field'})
source_type = fields.Str(required=True, error_messages={'required': 'NewsSource tyoe is a required field'})
class Meta:
ordered = True

@ -9,8 +9,7 @@ from news.app import commands, routes, components
settings = { settings = {
'DATABASE': { 'DATABASE': {
'URL': 'postgresql://apistar:local@localhost/news', 'URL': 'postgresql://apistar:local@localhost/testnews',
#'URL': 'sqlite:///',
'METADATA': Base.metadata 'METADATA': Base.metadata
}, },
'RENDERERS': [JSONRenderer()], 'RENDERERS': [JSONRenderer()],

@ -1,5 +1,143 @@
from news.app import welcome import datetime as dt
from unittest import mock
def test_welcome_route(): import pytest
message = {"message": "welcome to news"}
assert message == welcome() from news.app import get_articles, get_sources, add_source, get_source, delete_source
from news.models import NewsArticle, NewsSource, Category, Tag
# Sources
def test_get_empty__news_sources(rb_session):
assert [] == get_sources(rb_session)
def test_get_news_sources(rb_session):
test_source = NewsSource(url='http://money.test.com',
source_name='TEST',
source_type='website',
categories=[Category(category_name='finance')]
)
rb_session.add(test_source)
rb_session.flush()
assert 1 == len(get_sources(rb_session))
def test_add_news_source(rb_session):
test_source = {
'source_name': 'TEST',
'source_type': 'website',
'url': 'http://money.test.com'
}
mock_router = mock.Mock()
mock_router.configure_mock(
**{"reverse_url.return_value": "/mylocation"}
)
result = add_source(rb_session, test_source, mock_router)
assert 201 == result.status
assert 'location' in result.headers.keys()
def test_add_news_source_error(rb_session):
"""Testing with missing required field"""
test_source = {
'source_type': 'website',
'url': 'http://money.test.com'
}
mock_router = mock.Mock()
mock_router.configure_mock(
**{"reverse_url.return_value": "/mylocation"}
)
result = add_source(rb_session, test_source, mock_router)
print(result.headers)
assert 400 == result.status
def test_get_news_source(rb_session):
test_source = NewsSource(url='http://money.test.com',
source_name='TEST',
source_type='website',
categories=[Category(category_name='finance')]
)
rb_session.add(test_source)
rb_session.flush()
result = get_source(rb_session, 1)
assert 200 == result.status
def test_get_news_source_404_error(rb_session):
test_source = NewsSource(url='http://money.test.com',
source_name='TEST',
source_type='website',
categories=[Category(category_name='finance')]
)
rb_session.add(test_source)
rb_session.flush()
result = get_source(rb_session, 2)
assert 404 == result.status
def test_delete_news_source(rb_session):
test_source = NewsSource(url='http://money.test.com',
source_name='TEST',
source_type='website',
categories=[Category(category_name='finance')]
)
rb_session.add(test_source)
rb_session.flush()
result = delete_source(rb_session, 1)
assert 200 == result.status
def test_delete_news_source_404(rb_session):
test_source = NewsSource(url='http://money.test.com',
source_name='TEST',
source_type='website',
categories=[Category(category_name='finance')]
)
rb_session.add(test_source)
rb_session.flush()
result = delete_source(rb_session, 2)
assert 404 == result.status
# Articles
def test_get_empty_articles(rb_session):
assert get_articles(rb_session) == []
def test_get_articles(rb_session):
source = NewsSource(url='http://money.test.com',
source_name='TEST',
source_type='website',
categories=[Category(category_name='finance')]
)
article = NewsArticle(url='http://money.test.com/article',
title='article',
authors=['drew', 'jesse'],
publish_date=dt.datetime.utcnow(),
news_blob='article content',
news_source=source,
tags=[Tag(tag_name='article')]
)
rb_session.add(article)
rb_session.flush()
assert 1 == len(get_articles(rb_session))

@ -7,7 +7,7 @@ from news.renders import extended_encoder, JSONRenderer
def test_extended_encoder_date_parsing(): def test_extended_encoder_date_parsing():
test_date = dt.datetime(2017, 5, 10) test_date = dt.datetime(2017, 5, 10)
assert test_date.isoformat() == extended_encoder(test_date) assert test_date.timestamp() == extended_encoder(test_date)
def test_extended_encoder_decimal_casting(): def test_extended_encoder_decimal_casting():
@ -18,6 +18,6 @@ def test_extended_encoder_decimal_casting():
def test_render_with_extended_encoder(): def test_render_with_extended_encoder():
test_date = dt.datetime(2017, 5, 10) test_date = dt.datetime(2017, 5, 10)
test_decimal = Decimal('0.1') test_decimal = Decimal('0.1')
expected = dict(my_date="2017-05-10T00:00:00", my_float=0.1) expected = dict(my_date=1494388800.0, my_float=0.1)
test_response = dict(my_date=test_date, my_float=test_decimal) test_response = dict(my_date=test_date, my_float=test_decimal)
assert json.dumps(expected).encode('utf-8') == JSONRenderer().render(test_response) assert json.dumps(expected).encode('utf-8') == JSONRenderer().render(test_response)
Loading…
Cancel
Save