Skip to content
Snippets Groups Projects
Commit 2215e734 authored by jan.bednarik's avatar jan.bednarik
Browse files

Update of old report creates historical revision

parent b306a938
Branches
No related tags found
No related merge requests found
# Open Lobby Server # Open Lobby Server
Open Lobby is register of lobby meetings. It's being developed for and tested Open Lobby is register of lobby meetings. It's being developed for and tested
on [Czech Pirate Party](https://www.pirati.cz) but later it may be used by on [Czech Pirate Party](https://www.pirati.cz) but later it may be used by
any party, organization, agency, ... any party, organization, agency, ...
_Open Lobby is in early beta version now. Not for production use._ This is core of the register - server with [GraphQL API](http://graphql.org).
Over API are connected application interfaces. Default web application is
This is core of the register - server with [GraphQL API](http://graphql.org). available at
Over API are connected application interfaces. Default web application is
available at
[openlobby/openlobby-app](https://github.com/openlobby/openlobby-app). [openlobby/openlobby-app](https://github.com/openlobby/openlobby-app).
Register is built on top of Register is built on top of
[Elasticsearch](https://www.elastic.co/products/elasticsearch). For now it's [Elasticsearch](https://www.elastic.co/products/elasticsearch). For now it's
intended for search in Czech language with custom Czech text analyzer. There is intended for search in Czech language with custom Czech text analyzer. There is
prepared Elasticsearch Docker container with Czech support at prepared Elasticsearch Docker container with Czech support at
[openlobby/openlobby-es-czech](https://github.com/openlobby/openlobby-es-czech). [openlobby/openlobby-es-czech](https://github.com/openlobby/openlobby-es-czech).
## Configuration ## Configuration
...@@ -29,24 +27,26 @@ Configuration is done by environment variables: ...@@ -29,24 +27,26 @@ Configuration is done by environment variables:
- `REDIRECT_URI` - redirect URI used in OpenID Connect authentication (default: `http://localhost:8010/login-redirect`) - `REDIRECT_URI` - redirect URI used in OpenID Connect authentication (default: `http://localhost:8010/login-redirect`)
- put there address where you run server, but keep there `/login-redirect` - put there address where you run server, but keep there `/login-redirect`
- this is the Redirect URI for static client registration at OpenID Provider - this is the Redirect URI for static client registration at OpenID Provider
- `FREE_EDIT_MINUTES` - edit will save historical revision after this time
since last edit (or publishing) of report (default: `60`)
### Login shortcuts aka preregistered OpenID Clients ### Login shortcuts aka preregistered OpenID Clients
Some OpenID Providers does not allow dynamic client registration. You can still Some OpenID Providers does not allow dynamic client registration. You can still
use them. Register client with `REDIRECT_URI` and save client's credentials into use them. Register client with `REDIRECT_URI` and save client's credentials into
database. You can do it in admin interface running at `/admin`. It's standard database. You can do it in admin interface running at `/admin`. It's standard
Django admin (create superuser for yourself like `./manage.py createsuperuser`). Django admin (create superuser for yourself like `./manage.py createsuperuser`).
## Docker ## Docker
Docker image is at Docker Hub Docker image is at Docker Hub
[openlobby/openlobby-server](https://hub.docker.com/r/openlobby/openlobby-server/). [openlobby/openlobby-server](https://hub.docker.com/r/openlobby/openlobby-server/).
It exposes server on port 8010. You should provide it environment variables for It exposes server on port 8010. You should provide it environment variables for
configuration (at least `SECRET_KEY`). configuration (at least `SECRET_KEY`).
## Demo ## Demo
Demo of Open Lobby with instructions is in repository Demo of Open Lobby with instructions is in repository
[openlobby/demo](https://github.com/openlobby/demo). [openlobby/demo](https://github.com/openlobby/demo).
## Local run and development ## Local run and development
...@@ -55,13 +55,13 @@ Demo of Open Lobby with instructions is in repository ...@@ -55,13 +55,13 @@ Demo of Open Lobby with instructions is in repository
You need to have Python 3 installed. You need to have Python 3 installed.
Run PostgreSQL database on `localhost:5432` with user `db`, password `db` and Run PostgreSQL database on `localhost:5432` with user `db`, password `db` and
database `openlobby`. You can provide different address in environment variable database `openlobby`. You can provide different address in environment variable
`DATABASE_DSN`. `DATABASE_DSN`.
Run Elasticsearch server Run Elasticsearch server
[openlobby/openlobby-es-czech](https://github.com/openlobby/openlobby-es-czech) [openlobby/openlobby-es-czech](https://github.com/openlobby/openlobby-es-czech)
on `http://localhost:9200`. You can provide different address in environment on `http://localhost:9200`. You can provide different address in environment
variable `ELASTICSEARCH_DSN`. variable `ELASTICSEARCH_DSN`.
### Local run ### Local run
...@@ -74,7 +74,7 @@ Clone this repository and run: ...@@ -74,7 +74,7 @@ Clone this repository and run:
4. `make migrate` - runs database migrations and rebuilds Elasticsearch index 4. `make migrate` - runs database migrations and rebuilds Elasticsearch index
5. `make run` - runs development server on port `8010` 5. `make run` - runs development server on port `8010`
Now you can use GraphQL API endpoint and GraphiQL web interface at Now you can use GraphQL API endpoint and GraphiQL web interface at
`http://localhost:8010/graphql` `http://localhost:8010/graphql`
Next time you can just do steps 2 and 5. Next time you can just do steps 2 and 5.
...@@ -83,14 +83,14 @@ Next time you can just do steps 2 and 5. ...@@ -83,14 +83,14 @@ Next time you can just do steps 2 and 5.
Run: `pytest` Run: `pytest`
For full test suite run you have to provide OpenID Provider issuer URL which For full test suite run you have to provide OpenID Provider issuer URL which
allows client registration. For example you can run Keycloak sever locally: allows client registration. For example you can run Keycloak sever locally:
`docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=pass -p 8080:8080 --rm jboss/keycloak` `docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=pass -p 8080:8080 --rm jboss/keycloak`
Login into Keycloak admin console `http://localhost:8080/auth/admin/` Login into Keycloak admin console `http://localhost:8080/auth/admin/`
(as admin/pass) and go to Realm Settings -> Client Registration -> Client (as admin/pass) and go to Realm Settings -> Client Registration -> Client
Registration Policies -> Trusted Hosts. There add `localhost` to "Trusted Registration Policies -> Trusted Hosts. There add `localhost` to "Trusted
Hosts", turn off "Host Sending Client Registration Request Must Match" and save Hosts", turn off "Host Sending Client Registration Request Must Match" and save
it. it.
Now run: `pytest --issuer=http://localhost:8080/auth/realms/master` Now run: `pytest --issuer=http://localhost:8080/auth/realms/master`
......
import arrow import arrow
from django.conf import settings
import graphene import graphene
from graphene import relay from graphene import relay
from graphene.types.datetime import DateTime from graphene.types.datetime import DateTime
...@@ -178,11 +179,21 @@ class UpdateReport(relay.ClientIDMutation): ...@@ -178,11 +179,21 @@ class UpdateReport(relay.ClientIDMutation):
if is_draft and not report.is_draft: if is_draft and not report.is_draft:
raise Exception("You cannot update published Report with draft.") raise Exception("You cannot update published Report with draft.")
# TODO updating published report older than like a hour should create free_edit_deadline = arrow.utcnow().shift(minutes=-settings.FREE_EDIT_MINUTES)
# new revision in history of report if (
not (is_draft or report.is_draft)
and free_edit_deadline.datetime > report.edited
):
original_id = report.id
# save historical revision copy
report.id = None
report.superseded_by_id = original_id
report.save()
# restore id so this can be updated
report.id = original_id
report.superseded_by_id = None
report.edited = arrow.utcnow().datetime report.edited = arrow.utcnow().datetime
if is_draft or report.is_draft: if is_draft or report.is_draft:
report.published = report.edited report.published = report.edited
......
from django.db.models import Count, Q
from elasticsearch import NotFoundError from elasticsearch import NotFoundError
import graphene import graphene
from graphene import relay from graphene import relay
......
...@@ -150,3 +150,6 @@ SITE_NAME = os.environ.get("SITE_NAME", "Open Lobby") ...@@ -150,3 +150,6 @@ SITE_NAME = os.environ.get("SITE_NAME", "Open Lobby")
# redirect URI used in OpenID authentication # redirect URI used in OpenID authentication
REDIRECT_URI = os.environ.get("REDIRECT_URI", "http://localhost:8010/login-redirect") REDIRECT_URI = os.environ.get("REDIRECT_URI", "http://localhost:8010/login-redirect")
# edit will save historical revision after this time since last edit
FREE_EDIT_MINUTES = os.environ.get("FREE_EDIT_MINUTES", 60)
...@@ -58,22 +58,22 @@ snapshots["test_update_draft_with_draft 1"] = { ...@@ -58,22 +58,22 @@ snapshots["test_update_draft_with_draft 1"] = {
"author": { "author": {
"extra": '{"movies": 1}', "extra": '{"movies": 1}',
"firstName": "Winston", "firstName": "Winston",
"id": "QXV0aG9yOjE=", "id": "QXV0aG9yOjQy",
"lastName": "Wolfe", "lastName": "Wolfe",
"totalReports": 0, "totalReports": 0,
}, },
"body": "I visited Tesla factory and talked with Elon Musk.", "body": "Rewrited",
"date": "2018-03-03 00:00:00+00:00", "date": "2018-03-03 00:00:00+00:00",
"edited": "2018-03-08 12:00:00+00:00", "edited": "2018-01-02 05:50:00+00:00",
"extra": None, "extra": None,
"id": "UmVwb3J0OjE=", "id": "UmVwb3J0OjY2Ng==",
"isDraft": True, "isDraft": True,
"otherParticipants": "Elon Musk", "otherParticipants": "grandchilds",
"ourParticipants": "me", "ourParticipants": "kids",
"providedBenefit": "nothing", "providedBenefit": "water",
"published": "2018-03-08 12:00:00+00:00", "published": "2018-01-02 05:50:00+00:00",
"receivedBenefit": "Tesla Model S", "receivedBenefit": "cake",
"title": "Free Tesla", "title": "New title",
} }
} }
} }
...@@ -86,22 +86,22 @@ snapshots["test_update_draft_with_published 1"] = { ...@@ -86,22 +86,22 @@ snapshots["test_update_draft_with_published 1"] = {
"author": { "author": {
"extra": '{"movies": 1}', "extra": '{"movies": 1}',
"firstName": "Winston", "firstName": "Winston",
"id": "QXV0aG9yOjE=", "id": "QXV0aG9yOjQy",
"lastName": "Wolfe", "lastName": "Wolfe",
"totalReports": 1, "totalReports": 1,
}, },
"body": "I visited Tesla factory and talked with Elon Musk.", "body": "Rewrited",
"date": "2018-03-03 00:00:00+00:00", "date": "2018-03-03 00:00:00+00:00",
"edited": "2018-03-08 12:00:00+00:00", "edited": "2018-01-02 05:50:00+00:00",
"extra": None, "extra": None,
"id": "UmVwb3J0OjE=", "id": "UmVwb3J0OjY2Ng==",
"isDraft": False, "isDraft": False,
"otherParticipants": "Elon Musk", "otherParticipants": "grandchilds",
"ourParticipants": "me", "ourParticipants": "kids",
"providedBenefit": "nothing", "providedBenefit": "water",
"published": "2018-03-08 12:00:00+00:00", "published": "2018-01-02 05:50:00+00:00",
"receivedBenefit": "Tesla Model S", "receivedBenefit": "cake",
"title": "Free Tesla", "title": "New title",
} }
} }
} }
...@@ -114,22 +114,22 @@ snapshots["test_update_published_with_published 1"] = { ...@@ -114,22 +114,22 @@ snapshots["test_update_published_with_published 1"] = {
"author": { "author": {
"extra": '{"movies": 1}', "extra": '{"movies": 1}',
"firstName": "Winston", "firstName": "Winston",
"id": "QXV0aG9yOjE=", "id": "QXV0aG9yOjQy",
"lastName": "Wolfe", "lastName": "Wolfe",
"totalReports": 1, "totalReports": 1,
}, },
"body": "I visited Tesla factory and talked with Elon Musk.", "body": "Rewrited",
"date": "2018-03-03 00:00:00+00:00", "date": "2018-03-03 00:00:00+00:00",
"edited": "2018-03-08 12:00:00+00:00", "edited": "2018-01-02 05:50:00+00:00",
"extra": None, "extra": None,
"id": "UmVwb3J0OjE=", "id": "UmVwb3J0OjY2Ng==",
"isDraft": False, "isDraft": False,
"otherParticipants": "Elon Musk", "otherParticipants": "grandchilds",
"ourParticipants": "me", "ourParticipants": "kids",
"providedBenefit": "nothing", "providedBenefit": "water",
"published": "2018-01-02 00:00:00+00:00", "published": "2018-01-02 00:00:00+00:00",
"receivedBenefit": "Tesla Model S", "receivedBenefit": "cake",
"title": "Free Tesla", "title": "New title",
} }
} }
} }
...@@ -142,15 +142,15 @@ snapshots["test_input_sanitization 1"] = { ...@@ -142,15 +142,15 @@ snapshots["test_input_sanitization 1"] = {
"author": { "author": {
"extra": '{"movies": 1}', "extra": '{"movies": 1}',
"firstName": "Winston", "firstName": "Winston",
"id": "QXV0aG9yOjE=", "id": "QXV0aG9yOjQy",
"lastName": "Wolfe", "lastName": "Wolfe",
"totalReports": 1, "totalReports": 1,
}, },
"body": "some link in body", "body": "some link in body",
"date": "2018-03-03 00:00:00+00:00", "date": "2018-03-03 00:00:00+00:00",
"edited": "2018-03-08 12:00:00+00:00", "edited": "2018-01-02 05:50:00+00:00",
"extra": None, "extra": None,
"id": "UmVwb3J0OjE=", "id": "UmVwb3J0OjY2Ng==",
"isDraft": False, "isDraft": False,
"otherParticipants": "you!", "otherParticipants": "you!",
"ourParticipants": "me, myself", "ourParticipants": "me, myself",
...@@ -162,3 +162,235 @@ snapshots["test_input_sanitization 1"] = { ...@@ -162,3 +162,235 @@ snapshots["test_input_sanitization 1"] = {
} }
} }
} }
snapshots["test_update_draft_with_draft__late_edit 1"] = {
"data": {
"updateReport": {
"report": {
"author": {
"extra": '{"movies": 1}',
"firstName": "Winston",
"id": "QXV0aG9yOjQy",
"lastName": "Wolfe",
"totalReports": 0,
},
"body": "Rewrited",
"date": "2018-03-03 00:00:00+00:00",
"edited": "2018-01-02 06:10:00+00:00",
"extra": None,
"id": "UmVwb3J0OjY2Ng==",
"isDraft": True,
"otherParticipants": "grandchilds",
"ourParticipants": "kids",
"providedBenefit": "water",
"published": "2018-01-02 06:10:00+00:00",
"receivedBenefit": "cake",
"title": "New title",
}
}
}
}
snapshots["test_update_draft_with_published__late_edit 1"] = {
"data": {
"updateReport": {
"report": {
"author": {
"extra": '{"movies": 1}',
"firstName": "Winston",
"id": "QXV0aG9yOjQy",
"lastName": "Wolfe",
"totalReports": 1,
},
"body": "Rewrited",
"date": "2018-03-03 00:00:00+00:00",
"edited": "2018-01-02 06:10:00+00:00",
"extra": None,
"id": "UmVwb3J0OjY2Ng==",
"isDraft": False,
"otherParticipants": "grandchilds",
"ourParticipants": "kids",
"providedBenefit": "water",
"published": "2018-01-02 06:10:00+00:00",
"receivedBenefit": "cake",
"title": "New title",
}
}
}
}
snapshots["test_update_draft_with_draft 2"] = [
{
"author_id": 42,
"body": "Rewrited",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T05:50:00+00:00",
"extra": None,
"id": 666,
"is_draft": True,
"other_participants": "grandchilds",
"our_participants": "kids",
"provided_benefit": "water",
"published": "2018-01-02T05:50:00+00:00",
"received_benefit": "cake",
"superseded_by_id": None,
"title": "New title",
}
]
snapshots["test_update_draft_with_draft__late_edit 2"] = [
{
"author_id": 42,
"body": "Rewrited",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T06:10:00+00:00",
"extra": None,
"id": 666,
"is_draft": True,
"other_participants": "grandchilds",
"our_participants": "kids",
"provided_benefit": "water",
"published": "2018-01-02T06:10:00+00:00",
"received_benefit": "cake",
"superseded_by_id": None,
"title": "New title",
}
]
snapshots["test_update_draft_with_published 2"] = [
{
"author_id": 42,
"body": "Rewrited",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T05:50:00+00:00",
"extra": None,
"id": 666,
"is_draft": False,
"other_participants": "grandchilds",
"our_participants": "kids",
"provided_benefit": "water",
"published": "2018-01-02T05:50:00+00:00",
"received_benefit": "cake",
"superseded_by_id": None,
"title": "New title",
}
]
snapshots["test_update_draft_with_published__late_edit 2"] = [
{
"author_id": 42,
"body": "Rewrited",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T06:10:00+00:00",
"extra": None,
"id": 666,
"is_draft": False,
"other_participants": "grandchilds",
"our_participants": "kids",
"provided_benefit": "water",
"published": "2018-01-02T06:10:00+00:00",
"received_benefit": "cake",
"superseded_by_id": None,
"title": "New title",
}
]
snapshots["test_update_published_with_published 2"] = [
{
"author_id": 42,
"body": "Rewrited",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T05:50:00+00:00",
"extra": None,
"id": 666,
"is_draft": False,
"other_participants": "grandchilds",
"our_participants": "kids",
"provided_benefit": "water",
"published": "2018-01-02T00:00:00+00:00",
"received_benefit": "cake",
"superseded_by_id": None,
"title": "New title",
}
]
snapshots["test_update_published_with_published__late_edit 1"] = {
"data": {
"updateReport": {
"report": {
"author": {
"extra": '{"movies": 1}',
"firstName": "Winston",
"id": "QXV0aG9yOjQy",
"lastName": "Wolfe",
"totalReports": 1,
},
"body": "Rewrited",
"date": "2018-03-03 00:00:00+00:00",
"edited": "2018-01-02 06:10:00+00:00",
"extra": None,
"id": "UmVwb3J0OjY2Ng==",
"isDraft": False,
"otherParticipants": "grandchilds",
"ourParticipants": "kids",
"providedBenefit": "water",
"published": "2018-01-02 00:00:00+00:00",
"receivedBenefit": "cake",
"title": "New title",
}
}
}
}
snapshots["test_update_published_with_published__late_edit 2"] = {
"author_id": 42,
"body": "Rewrited",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T06:10:00+00:00",
"extra": None,
"id": 666,
"is_draft": False,
"other_participants": "grandchilds",
"our_participants": "kids",
"provided_benefit": "water",
"published": "2018-01-02T00:00:00+00:00",
"received_benefit": "cake",
"superseded_by_id": None,
"title": "New title",
}
snapshots["test_input_sanitization 2"] = [
{
"author_id": 42,
"body": "some link in body",
"date": "2018-03-03T00:00:00+00:00",
"edited": "2018-01-02T05:50:00+00:00",
"extra": None,
"id": 666,
"is_draft": False,
"other_participants": "you!",
"our_participants": "me, myself",
"provided_benefit": "tea",
"published": "2018-01-02T00:00:00+00:00",
"received_benefit": "coffee",
"superseded_by_id": None,
"title": "No tags",
}
]
snapshots["test_update_published_with_published__late_edit 3"] = {
"author_id": 42,
"body": "Previous body.",
"date": "2018-01-01T00:00:00+00:00",
"edited": "2018-01-02T05:00:00+00:00",
"extra": None,
"id": "__STRIPPED__",
"is_draft": False,
"other_participants": "grandma",
"our_participants": "grandpa",
"provided_benefit": "old tea",
"published": "2018-01-02T00:00:00+00:00",
"received_benefit": "old coffee",
"superseded_by_id": 666,
"title": "Original",
}
...@@ -3,10 +3,9 @@ import arrow ...@@ -3,10 +3,9 @@ import arrow
from graphql_relay import to_global_id from graphql_relay import to_global_id
from unittest.mock import patch from unittest.mock import patch
from openlobby.core.models import User, Report from openlobby.core.models import Report
from ..dummy import prepare_report from ..utils import dates_to_iso, strip_value
from ..utils import call_api
pytestmark = [pytest.mark.django_db, pytest.mark.usefixtures("django_es")] pytestmark = [pytest.mark.django_db, pytest.mark.usefixtures("django_es")]
...@@ -40,126 +39,162 @@ mutation updateReport ($input: UpdateReportInput!) { ...@@ -40,126 +39,162 @@ mutation updateReport ($input: UpdateReportInput!) {
} }
""" """
published = arrow.get(2018, 1, 2) original_edited = arrow.get(2018, 1, 2, 5)
edited = arrow.get(2018, 3, 8, 12) edited = original_edited.shift(minutes=50)
late_edited = original_edited.shift(minutes=70)
date = arrow.get(2018, 3, 3)
title = "Free Tesla"
body = "I visited Tesla factory and talked with Elon Musk."
received_benefit = "Tesla Model S"
provided_benefit = "nothing"
our_participants = "me"
other_participants = "Elon Musk"
@pytest.fixture
def original_report(author_fix, report_factory):
return report_factory(
id=666,
author=author_fix,
date=arrow.get(2018, 1, 1).datetime,
published=arrow.get(2018, 1, 2).datetime,
edited=original_edited.datetime,
title="Original",
body="Previous body.",
received_benefit="old coffee",
provided_benefit="old tea",
our_participants="grandpa",
other_participants="grandma",
)
def get_input(is_draft=False, id=1):
@pytest.fixture
def original_report_draft(original_report):
original_report.is_draft = True
original_report.save()
return original_report
def prepare_input(is_draft=False, id=1):
return { return {
"id": to_global_id("Report", id), "id": to_global_id("Report", id),
"title": title, "title": "New title",
"body": body, "body": "Rewrited",
"receivedBenefit": received_benefit, "receivedBenefit": "cake",
"providedBenefit": provided_benefit, "providedBenefit": "water",
"ourParticipants": our_participants, "ourParticipants": "kids",
"otherParticipants": other_participants, "otherParticipants": "grandchilds",
"date": date.isoformat(), "date": arrow.get(2018, 3, 3).isoformat(),
"isDraft": is_draft, "isDraft": is_draft,
} }
def assert_report(is_draft, published, edited): def test_unauthorized(call_api, snapshot, original_report):
report = Report.objects.get(id=1) input = prepare_input(id=original_report.id)
assert report.author_id == 1 response = call_api(query, input)
assert report.date == date.datetime
assert report.published == published.datetime
assert report.edited == edited.datetime
assert report.title == title
assert report.body == body
assert report.received_benefit == received_benefit
assert report.provided_benefit == provided_benefit
assert report.our_participants == our_participants
assert report.other_participants == other_participants
assert report.extra is None
assert report.is_draft is is_draft
def test_unauthorized(client, snapshot):
prepare_report()
input = get_input()
response = call_api(client, query, input)
snapshot.assert_match(response) snapshot.assert_match(response)
def test_not_author(client, snapshot): def test_not_author(call_api, snapshot, original_report, user):
prepare_report() input = prepare_input(id=original_report.id)
User.objects.create(id=2, username="hacker") response = call_api(query, input, user)
input = get_input()
response = call_api(client, query, input, "hacker")
snapshot.assert_match(response) snapshot.assert_match(response)
def test_report_does_not_exist(client, snapshot): def test_report_does_not_exist(call_api, snapshot, author_fix):
prepare_report() input = prepare_input(id=789)
input = get_input(id=666) response = call_api(query, input, author_fix)
response = call_api(client, query, input, "wolf")
snapshot.assert_match(response) snapshot.assert_match(response)
def test_update_published_with_draft(client, snapshot): def test_update_published_with_draft(call_api, snapshot, original_report):
prepare_report() input = prepare_input(id=original_report.id, is_draft=True)
input = get_input(is_draft=True) response = call_api(query, input, original_report.author)
response = call_api(client, query, input, "wolf")
snapshot.assert_match(response) snapshot.assert_match(response)
def test_update_draft_with_draft(client, snapshot): def test_update_draft_with_draft(call_api, snapshot, original_report_draft):
prepare_report(is_draft=True) input = prepare_input(id=original_report_draft.id, is_draft=True)
input = get_input(is_draft=True)
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited): with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited):
response = call_api(client, query, input, "wolf") response = call_api(query, input, original_report_draft.author)
snapshot.assert_match(response) snapshot.assert_match(response)
assert_report(True, edited, edited) reports = list(map(dates_to_iso, Report.objects.all().values()))
snapshot.assert_match(reports)
def test_update_draft_with_draft__late_edit(call_api, snapshot, original_report_draft):
input = prepare_input(id=original_report_draft.id, is_draft=True)
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=late_edited):
response = call_api(query, input, original_report_draft.author)
snapshot.assert_match(response)
reports = list(map(dates_to_iso, Report.objects.all().values()))
snapshot.assert_match(reports)
def test_update_draft_with_published(call_api, snapshot, original_report_draft):
input = prepare_input(id=original_report_draft.id)
def test_update_draft_with_published(client, snapshot):
prepare_report(is_draft=True)
input = get_input()
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited): with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited):
response = call_api(client, query, input, "wolf") response = call_api(query, input, original_report_draft.author)
snapshot.assert_match(response) snapshot.assert_match(response)
assert_report(False, edited, edited) reports = list(map(dates_to_iso, Report.objects.all().values()))
snapshot.assert_match(reports)
def test_update_draft_with_published__late_edit(
call_api, snapshot, original_report_draft
):
input = prepare_input(id=original_report_draft.id)
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=late_edited):
response = call_api(query, input, original_report_draft.author)
snapshot.assert_match(response)
reports = list(map(dates_to_iso, Report.objects.all().values()))
snapshot.assert_match(reports)
def test_update_published_with_published(call_api, snapshot, original_report):
input = prepare_input(id=original_report.id)
def test_update_published_with_published(client, snapshot):
prepare_report()
input = get_input()
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited): with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited):
response = call_api(client, query, input, "wolf") response = call_api(query, input, original_report.author)
snapshot.assert_match(response) snapshot.assert_match(response)
assert_report(False, published, edited) reports = list(map(dates_to_iso, Report.objects.all().values()))
snapshot.assert_match(reports)
def test_input_sanitization(client, snapshot): def test_update_published_with_published__late_edit(
prepare_report() call_api, snapshot, original_report
):
input = prepare_input(id=original_report.id)
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=late_edited):
response = call_api(query, input, original_report.author)
snapshot.assert_match(response)
assert Report.objects.count() == 2
updated = Report.objects.filter(id=original_report.id).values()[0]
original = Report.objects.filter(superseded_by_id=original_report.id).values()[0]
strip_value(original, "id")
snapshot.assert_match(dates_to_iso(updated))
snapshot.assert_match(dates_to_iso(original))
def test_input_sanitization(call_api, snapshot, original_report):
input = { input = {
"id": to_global_id("Report", 1), "id": to_global_id("Report", original_report.id),
"title": "<s>No</s> tags", "title": "<s>No</s> tags",
"body": 'some <a href="http://foo">link</a> <br>in body', "body": 'some <a href="http://foo">link</a> <br>in body',
"receivedBenefit": "<b>coffee</b>", "receivedBenefit": "<b>coffee</b>",
"providedBenefit": "<li>tea", "providedBenefit": "<li>tea",
"ourParticipants": "me, <u>myself</u>", "ourParticipants": "me, <u>myself</u>",
"otherParticipants": "<strong>you!</strong>", "otherParticipants": "<strong>you!</strong>",
"date": date.isoformat(), "date": arrow.get(2018, 3, 3).isoformat(),
} }
with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited): with patch("openlobby.core.api.mutations.arrow.utcnow", return_value=edited):
response = call_api(client, query, input, "wolf") response = call_api(query, input, original_report.author)
snapshot.assert_match(response) snapshot.assert_match(response)
reports = list(map(dates_to_iso, Report.objects.all().values()))
report = Report.objects.get() snapshot.assert_match(reports)
assert report.title == "No tags"
assert report.body == "some link in body"
assert report.received_benefit == "coffee"
assert report.provided_benefit == "tea"
assert report.our_participants == "me, myself"
assert report.other_participants == "you!"
from datetime import datetime
import json import json
from openlobby.core.auth import create_access_token from openlobby.core.auth import create_access_token
...@@ -26,3 +27,10 @@ def strip_value(data, *path): ...@@ -26,3 +27,10 @@ def strip_value(data, *path):
return value return value
else: else:
return strip_value(value, *path[1:]) return strip_value(value, *path[1:])
def dates_to_iso(data):
for key, val in data.items():
if isinstance(val, datetime):
data[key] = val.isoformat()
return data
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment