Skip to content

Commit

Permalink
Merge pull request #7 from MarekSuchanek/task04
Browse files Browse the repository at this point in the history
Task04
  • Loading branch information
MarekSuchanek authored Nov 1, 2016
2 parents 350d6f8 + eba5d26 commit dc76e03
Show file tree
Hide file tree
Showing 21 changed files with 3,094 additions and 58 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language: python
python:
- '3.5'
install:
- python setup.py install
script:
- python setup.py test
4 changes: 3 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
include LICENSE
exclude README.md
include pytest.ini
include config/auth.example.cfg
include tests/*.py
graft tests/fixtures
49 changes: 48 additions & 1 deletion README
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Twitter Wall

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
![Version](https://img.shields.io/badge/release-v0.3-orange.svg)
![Version](https://img.shields.io/badge/release-v0.4-orange.svg)
[![Build Status](https://travis-ci.com/MarekSuchanek/PYT-TwitterWall.svg?token=XD73y3snHDycemSiHx3H&branch=task04)](https://travis-ci.com/MarekSuchanek/PYT-TwitterWall)


Twitter Wall is simple [Python](https://www.python.org) powered app for
Expand Down Expand Up @@ -244,6 +245,52 @@ Enlarged photo of cat :smiley_cat: via [Lightbox](http://lokeshdhakar.com/projec

![Enlarged photo of cat via Lightbox](http://marsu.9e.cz/github/twitterwall-lightbox.png)


## Testing

This project uses the most fabulous testing tools for Python:

* [pytest](http://doc.pytest.org/)
* [pytest-cov](https://pypi.python.org/pypi/pytest-cov)
* [pytest-pep8](https://pypi.python.org/pypi/pytest-pep8)
* [pytest-sugar](https://pypi.python.org/pypi/pytest-sugar)
* [flexmock](http://flexmock.readthedocs.io/en/latest/)
* [betamax](betamax.readthedocs.io)

Run tests:

```
python setup.py test
```

or (if you have installed dependencies):

```
python -m pytest [options]
pytest [options]
```

### Betamax cassettes

Betamax cassettes are stored in `tests/fixtures/cassettes` directory. If
you are not connected to the internet, Twitter API is not working and/or
you don't have own API credentials you will use (replay) them in order to
test API client.

If you want to run your own cassettes, you need to setup system variables

* `API_KEY`
* `API_SECRET`

Your test command then might look like:

```
API_KEY=<YOUR_API_KEY> API_SECRET=<YOUR_API_SECRET> \
python setup.py test
```

For more information, enjoy reading [Betamax documentation](http://betamax.readthedocs.io/en/latest/introduction.html).

## Authors

* Marek Suchánek [[[email protected]](mailto:[email protected])]
Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
addopts = tests/ twitterwall/ --pep8 --cov=twitterwall
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[aliases]
test=pytest
17 changes: 15 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='twitterwall',
version='0.3',
version='0.4',
keywords='twitter feed cli web tweet wall',
description='Simple CLI & WEB based Twitter tweets feed',
long_description=long_description,
Expand All @@ -25,15 +25,28 @@
},
entry_points={
'console_scripts': [
'twitterwall = twitterwall.cli:twitter_wall',
'twitterwall = twitterwall.cli:main',
],
},
install_requires=[
'Flask>=0.10.0',
'Flask-Injector>=0.8.0',
'injector>=0.9.0',
'Jinja2>=2.6',
'click>=6.6',
'requests>=2.10.0'
],
setup_requires=[
'pytest-runner',
],
tests_require=[
'pytest',
'pytest-pep8',
'pytest-cov',
'pytest-sugar',
'betamax',
'flexmock',
],
classifiers=[
'Environment :: Console',
'Environment :: Web Environment',
Expand Down
89 changes: 89 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest
import betamax
import flexmock
from betamax.cassette import cassette
import os
import json
import flask_injector
import injector
from twitterwall.common import TwitterConnection


def sanitize_requests(interaction, current_cassette):
headers = interaction.data['request']['headers']
auth = headers.get('Authorization')[0]
if auth is None:
return
current_cassette.placeholders.append(
cassette.Placeholder(
placeholder='<AUTH>',
replace=auth
)
)


def sanitize_responses(interaction, current_cassette):
if interaction.data['response']['status']['code'] != 200:
return
data = interaction.as_response().json()
if 'access_token' not in data:
return
current_cassette.placeholders.append(
cassette.Placeholder(
placeholder='<TOKEN>',
replace=data['access_token'])
)


def sanitize_token(interaction, current_cassette):
sanitize_requests(interaction, current_cassette)
sanitize_responses(interaction, current_cassette)


with betamax.Betamax.configure() as config:
config.cassette_library_dir = 'tests/fixtures/cassettes'
if 'API_KEY' in os.environ and 'API_SECRET' in os.environ:
config.default_cassette_options['record_mode'] = 'all'
else:
config.default_cassette_options['record_mode'] = 'none'
config.before_record(callback=sanitize_token)


@pytest.fixture
def twitter(betamax_session):
"""TwitterConnection with betamax session"""
betamax_session.headers.update({'accept-encoding': 'identity'})
api_key = os.environ.get('API_KEY', 'fake_key')
api_secret = os.environ.get('API_SECRET', 'fake_secret')
return TwitterConnection(api_key, api_secret, session=betamax_session)


@pytest.fixture
def twitter_mock():
"""TwitterConnection Mock returning tweets from example JSON file"""
from twitterwall.common import Tweet

def get_tweets(params):
with open('tests/fixtures/tweets.json') as f:
return [Tweet(data) for data in json.load(f)['statuses']]
return flexmock.flexmock(get_tweets=get_tweets)


@pytest.fixture
def webapp(twitter_mock):
"""Flask web application test client"""
from twitterwall.web import app

def configure(binder):
binder.bind(
TwitterConnection,
to=twitter_mock,
scope=injector.singleton
)

flask_injector.FlaskInjector(app=app, modules=[configure])

app.config['TESTING'] = True
app.config['AJAX_INTERVAL'] = 7
app.config['INIT_COUNT'] = 10
return app.test_client()

Large diffs are not rendered by default.

1,039 changes: 1,039 additions & 0 deletions tests/fixtures/cassettes/test_twitterconnection.test_client_query.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

169 changes: 169 additions & 0 deletions tests/fixtures/cassettes/test_twitterconnection.test_client_until.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
{
"http_interactions": [
{
"response": {
"body": {
"encoding": "utf-8",
"string": "{\"statuses\":[],\"search_metadata\":{\"completed_in\":0.004,\"max_id\":793485822497128448,\"max_id_str\":\"793485822497128448\",\"query\":\"%23python+until%3A2016-01-01\",\"refresh_url\":\"?since_id=793485822497128448&q=%23python%20until%3A2016-01-01&include_entities=1\",\"count\":10,\"since_id\":0,\"since_id_str\":\"0\"}}"
},
"headers": {
"x-content-type-options": "nosniff",
"x-access-level": "read",
"server": "tsa_b",
"status": "200 OK",
"x-response-time": "18",
"content-length": "297",
"x-frame-options": "SAMEORIGIN",
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"x-rate-limit-remaining": "418",
"x-connection-hash": "3c4f965e9d6d4dbe12f4acaf111abe28",
"x-twitter-response-tags": "BouncerCompliant",
"x-rate-limit-limit": "450",
"last-modified": "Tue, 01 Nov 2016 16:12:16 GMT",
"strict-transport-security": "max-age=631138519",
"pragma": "no-cache",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"x-xss-protection": "1; mode=block",
"content-type": "application/json;charset=utf-8",
"x-rate-limit-reset": "1478017059",
"x-transaction": "00fcd53a009082b9",
"content-disposition": "attachment; filename=json.json",
"date": "Tue, 01 Nov 2016 16:12:16 GMT"
},
"status": {
"code": 200,
"message": "OK"
},
"url": "https://api.twitter.com/1.1/search/tweets.json?count=10&q=%23python&until=2016-01-01"
},
"recorded_at": "2016-11-01T16:12:17",
"request": {
"body": {
"encoding": "utf-8",
"string": ""
},
"headers": {
"Cookie": "guest_id=v1%3A147801673635265550",
"Connection": "keep-alive",
"User-Agent": "python-requests/2.11.1",
"Accept": "*/*",
"accept-encoding": "identity",
"Authorization": "Bearer <TOKEN>"
},
"method": "GET",
"uri": "https://api.twitter.com/1.1/search/tweets.json?count=10&q=%23python&until=2016-01-01"
}
},
{
"response": {
"body": {
"encoding": "utf-8",
"string": "{\"token_type\":\"bearer\",\"access_token\":\"<TOKEN>\"}"
},
"headers": {
"x-content-type-options": "nosniff",
"content-length": "153",
"server": "tsa_b",
"status": "200 OK",
"set-cookie": "guest_id=v1%3A147801673889803128; Domain=.twitter.com; Path=/; Expires=Thu, 01-Nov-2018 16:12:18 UTC",
"x-response-time": "20",
"x-twitter-response-tags": "BouncerCompliant",
"x-frame-options": "DENY",
"ml": "A",
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"x-connection-hash": "a7a26fb2b110f8399b56de3a07baed1b",
"last-modified": "Tue, 01 Nov 2016 16:12:18 GMT",
"strict-transport-security": "max-age=631138519",
"pragma": "no-cache",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"x-xss-protection": "1; mode=block",
"content-type": "application/json;charset=utf-8",
"x-transaction": "0073101400f5db58",
"content-disposition": "attachment; filename=json.json",
"x-tsa-request-body-time": "76",
"x-ua-compatible": "IE=edge,chrome=1",
"date": "Tue, 01 Nov 2016 16:12:18 GMT"
},
"url": "https://api.twitter.com/oauth2/token",
"status": {
"code": 200,
"message": "OK"
}
},
"recorded_at": "2016-11-01T16:12:18",
"request": {
"body": {
"encoding": "utf-8",
"string": "grant_type=client_credentials"
},
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "<AUTH>",
"Connection": "keep-alive",
"Content-Length": "29",
"User-Agent": "python-requests/2.11.1",
"Accept": "*/*",
"accept-encoding": "identity",
"Host": "api.twitter.com"
},
"method": "POST",
"uri": "https://api.twitter.com/oauth2/token"
}
},
{
"response": {
"body": {
"encoding": "utf-8",
"string": "{\"statuses\":[],\"search_metadata\":{\"completed_in\":0.004,\"max_id\":793485831338721280,\"max_id_str\":\"793485831338721280\",\"query\":\"%23python+until%3A2015-05-15\",\"refresh_url\":\"?since_id=793485831338721280&q=%23python%20until%3A2015-05-15&include_entities=1\",\"count\":10,\"since_id\":0,\"since_id_str\":\"0\"}}"
},
"headers": {
"x-content-type-options": "nosniff",
"content-length": "297",
"server": "tsa_b",
"status": "200 OK",
"x-access-level": "read",
"x-response-time": "18",
"x-twitter-response-tags": "BouncerCompliant",
"x-frame-options": "SAMEORIGIN",
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"x-rate-limit-remaining": "417",
"x-connection-hash": "a7a26fb2b110f8399b56de3a07baed1b",
"x-rate-limit-limit": "450",
"last-modified": "Tue, 01 Nov 2016 16:12:19 GMT",
"strict-transport-security": "max-age=631138519",
"pragma": "no-cache",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"x-xss-protection": "1; mode=block",
"content-type": "application/json;charset=utf-8",
"x-rate-limit-reset": "1478017059",
"x-transaction": "009295110002f388",
"content-disposition": "attachment; filename=json.json",
"date": "Tue, 01 Nov 2016 16:12:19 GMT"
},
"url": "https://api.twitter.com/1.1/search/tweets.json?count=10&q=%23python&until=2015-05-15",
"status": {
"code": 200,
"message": "OK"
}
},
"recorded_at": "2016-11-01T16:12:19",
"request": {
"body": {
"encoding": "utf-8",
"string": ""
},
"headers": {
"Cookie": "guest_id=v1%3A147801673889803128",
"Connection": "keep-alive",
"Authorization": "Bearer <TOKEN>",
"Accept": "*/*",
"accept-encoding": "identity",
"User-Agent": "python-requests/2.11.1"
},
"method": "GET",
"uri": "https://api.twitter.com/1.1/search/tweets.json?count=10&q=%23python&until=2015-05-15"
}
}
],
"recorded_with": "betamax/0.8.0"
}
Loading

0 comments on commit dc76e03

Please sign in to comment.