Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create incidents endpoint and tests #355

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions backend/routes/incidents.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from backend.auth.jwt import min_role_required, contributor_has_partner
from backend.mixpanel.mix import track_to_mp
from mixpanel import MixpanelException
from flask import Blueprint, abort, current_app, request
from flask import Blueprint, abort, request
from flask_jwt_extended.view_decorators import jwt_required
from flask_jwt_extended import get_jwt
from pydantic import BaseModel
Expand Down Expand Up @@ -47,14 +47,28 @@ def get_incident(incident_id: int):
@validate(json=CreateIncidentSchema)
def create_incident():
"""Create a single incident.

Cannot be called in production environments
"""
if current_app.env == "production":
abort(418)
body = request.context.json
jwt_decoded: dict[str, str] = get_jwt()
user_id = jwt_decoded["sub"]
permission = PartnerMember.query.filter(
PartnerMember.user_id == user_id,
PartnerMember.role.in_((MemberRole.PUBLISHER, MemberRole.ADMIN)),
).first()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to consider: we should make sure that the source included request body is the same source that is being used to publish the incident. It's may seem strange, but it could be that the Partners endpoint is actually the best place for the incident creation function.

Just opining though. No changes needed here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to move the create_incidents to routes/partners.py , but seems like it'll break other tests in test_incidents as it is needed to create mock incidents for testing other endpoints. I could figure out a work around to this, but wasn't sure if I should do it.

if permission is None:
abort(403)

existing_incident = Incident.query.filter_by(
source_id=body.source_id,
time_of_incident=body.time_of_incident,
longitude=body.longitude,
latitude=body.latitude,
location=body.location,
).first()
if existing_incident:
abort(409, "Incident already exists")
try:
incident = incident_to_orm(request.context.json)
incident = incident_to_orm(body)
except Exception:
abort(400)

Expand Down
106 changes: 100 additions & 6 deletions backend/tests/test_incidents.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from backend.database import Incident, Partner, PrivacyStatus, User
from typing import Any

member_email = "[email protected]"
example_password = "my_password"
mock_partners = {
"cpdp": {"name": "Citizens Police Data Project"},
"mpv": {"name": "Mapping Police Violence"},
Expand Down Expand Up @@ -53,18 +55,25 @@


@pytest.fixture
def example_incidents(db_session, client, contributor_access_token):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think contributor_access_token is fine here. We want to test the lowest allowable auth (and highest non-allowed auth) to make sure we get the permission decision right.

Copy link
Collaborator Author

@hrauniya hrauniya Mar 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For issue 2, added the code to make sure that a user is turned into a contributor once they become an admin or a publisher for a partner. This is possible through two endpoints AFAIK, create_partners, and role_change.

Since create_partners has another PR open right now, I can add the updated tests through that PR and tests for it as well.

def example_incidents(db_session, client , partner_admin):
for id, mock in mock_partners.items():
db_session.add(Partner(**mock))
db_session.commit()

created = {}
access_token = res = client.post(
"api/v1/auth/login",
json={
"email": partner_admin.email ,
"password": "my_password"
},
).json["access_token"]
for name, mock in mock_incidents.items():
res = client.post(
"/api/v1/incidents/create",
json=mock,
headers={
"Authorization": "Bearer {0}".format(contributor_access_token)
"Authorization": "Bearer {0}".format(access_token)
},
)
assert res.status_code == 200
Expand All @@ -73,9 +82,6 @@ def example_incidents(db_session, client, contributor_access_token):


def test_create_incident(db_session, example_incidents):
# TODO: test that the User actually has permission to create an
# incident for the partner
# expected = mock_incidents["domestic"]
created = example_incidents["domestic"]

incident_obj = (
Expand All @@ -88,7 +94,95 @@ def test_create_incident(db_session, example_incidents):
incident_obj.perpetrators[i].id == created["perpetrators"][i]["id"]
)
assert incident_obj.use_of_force[0].id == created["use_of_force"][0]["id"]
# assert incident_obj.source == expected["source"]
assert incident_obj.location == created["location"]
assert incident_obj.description == created["description"]


"""
test for creating a new incident and
creating same incident
"""


def test_create_incident_exists(
client,
partner_admin,

):
created = {}
access_token = client.post(
"api/v1/auth/login",
json={
"email": partner_admin.email ,
"password": "my_password"
},
).json["access_token"]
# creating new incident
res = client.post(
"/api/v1/incidents/create",
headers={"Authorization": f"Bearer {access_token}"},
json=mock_incidents["domestic"]
)
created["domestic"] = res.json
domestic_instance = created["domestic"]
print(created)
assert res.status_code == 200
expected = mock_incidents["domestic"]
incident_obj = Incident.query.filter_by(
time_of_incident=expected["time_of_incident"]
).first()
date_format = '%Y-%m-%d %H:%M:%S'
date_obj = datetime.strptime(
expected["time_of_incident"],
date_format)
assert incident_obj.time_of_incident == date_obj
for i in [0, 1]:
assert (
incident_obj.perpetrators[i].id ==
domestic_instance["perpetrators"][i]["id"]
)
assert (incident_obj.use_of_force[0].id ==
domestic_instance["use_of_force"][0]["id"])
assert incident_obj.location == domestic_instance["location"]
assert incident_obj.description == domestic_instance["description"]

# creating the same incident
# should not be able to create incident
res = client.post(
"/api/v1/incidents/create",
headers={"Authorization": f"Bearer {access_token}"},
json=mock_incidents["domestic"]
)

assert res.status_code == 409


"""
creating incident when user
does not have permission
"""


def test_create_incident_no_permission(
client,
example_user

):
access_token = client.post(
"api/v1/auth/login",
json={
"email": example_user.email,
"password": "my_password"
},
).json["access_token"]
print(access_token)
# creating new incident
res = client.post(
"/api/v1/incidents/create",
headers={"Authorization": f"Bearer {access_token}"},
json=mock_incidents["domestic"]
)
assert res.status_code == 403


def test_get_incident(app, client, db_session, access_token):
Expand Down
Loading