Skip to content

Commit ab808f7

Browse files
committed
add feed api endpoint unit tests
1 parent 78dfb30 commit ab808f7

File tree

4 files changed

+213
-4
lines changed

4 files changed

+213
-4
lines changed

functions-python/operations_api/src/feeds_operations/impl/feeds_operations_impl.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ def detect_changes(
5353
"""
5454
# Normalize the feed and the update request and compare them
5555
copy_feed = UpdateRequestGtfsFeedImpl.from_orm(feed)
56+
# Temporary solution to update the operational status
57+
copy_feed.operational_status_action = (
58+
update_request_gtfs_feed.operational_status_action
59+
)
5660
diff = DeepDiff(
5761
copy_feed.model_dump(),
5862
update_request_gtfs_feed.model_dump(),
@@ -89,7 +93,10 @@ async def update_gtfs_feed(
8993
session, update_request_gtfs_feed.id, DataType.GTFS.name
9094
)
9195
if feed is None:
92-
raise HTTPException(status_code=400, detail=f"Feed ID not found: {id}")
96+
raise HTTPException(
97+
status_code=400,
98+
detail=f"Feed ID not found: {update_request_gtfs_feed.id}",
99+
)
93100

94101
logging.info(
95102
f"Feed ID: {id} attempting to update with the following request: {update_request_gtfs_feed}"
@@ -104,9 +111,9 @@ async def update_gtfs_feed(
104111
)
105112
# This is a temporary solution as the operational_status is not visible in the diff
106113
feed.operational_status = (
107-
"wip"
108-
if update_request_gtfs_feed.operational_status_action == "wip"
109-
else ""
114+
feed.operational_status
115+
if update_request_gtfs_feed.operational_status_action == "no_change"
116+
else update_request_gtfs_feed.operational_status_action
110117
)
111118
session.add(feed)
112119
session.commit()
@@ -119,6 +126,8 @@ async def update_gtfs_feed(
119126
return Response(status_code=204)
120127
except Exception as e:
121128
logging.error(f"Failed to update feed ID: {id}. Error: {e}")
129+
if isinstance(e, HTTPException):
130+
raise e
122131
raise HTTPException(status_code=500, detail=f"Internal server error: {e}")
123132
finally:
124133
if session:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import sys
2+
3+
sys.path.append("..")
4+
5+
import os
6+
7+
print(os.getcwd())
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#
2+
# MobilityData 2023
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
from database_gen.sqlacodegen_models import Gtfsfeed
17+
from test_utils.database_utils import clean_testing_db, get_testing_session
18+
19+
feed_mdb_40 = Gtfsfeed(
20+
id="mdb-40",
21+
data_type="gtfs",
22+
feed_name="London Transit Commission",
23+
note="note",
24+
producer_url="producer_url",
25+
authentication_type="1",
26+
authentication_info_url="authentication_info_url",
27+
api_key_parameter_name="api_key_parameter_name",
28+
license_url="license_url",
29+
stable_id="mdb-40",
30+
status="active",
31+
feed_contact_email="feed_contact_email",
32+
provider="provider",
33+
)
34+
35+
36+
def populate_database():
37+
"""
38+
Populates the database with fake data with the following distribution:
39+
- 1 GTFS feeds
40+
- 5 active
41+
- 5 inactive
42+
- 5 GTFS Realtime feeds
43+
- 9 GTFS datasets
44+
- 3 active in active feeds
45+
- 6 active in inactive feeds
46+
"""
47+
session = get_testing_session()
48+
49+
session.add(feed_mdb_40)
50+
session.commit()
51+
52+
53+
def pytest_configure(config):
54+
"""
55+
Allows plugins and conftest files to perform initial configuration.
56+
This hook is called for every plugin and initial conftest
57+
file after command line options have been parsed.
58+
"""
59+
60+
61+
def pytest_sessionstart(session):
62+
"""
63+
Called after the Session object has been created and
64+
before performing collection and entering the run test loop.
65+
"""
66+
clean_testing_db()
67+
populate_database()
68+
69+
70+
def pytest_sessionfinish(session, exitstatus):
71+
"""
72+
Called after whole test run finished, right before
73+
returning the exit status to the system.
74+
"""
75+
clean_testing_db()
76+
77+
78+
def pytest_unconfigure(config):
79+
"""
80+
called before test process is exited.
81+
"""
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import os
2+
from unittest import mock
3+
4+
import pytest
5+
from fastapi import HTTPException
6+
from starlette.responses import Response
7+
8+
from database_gen.sqlacodegen_models import Gtfsfeed
9+
from feeds_operations.impl.feeds_operations_impl import OperationsApiImpl
10+
from feeds_operations_gen.models.authentication_type import AuthenticationType
11+
from feeds_operations_gen.models.feed_status import FeedStatus
12+
from feeds_operations_gen.models.source_info import SourceInfo
13+
from feeds_operations_gen.models.update_request_gtfs_feed import UpdateRequestGtfsFeed
14+
from operations_api.tests.conftest import feed_mdb_40
15+
from test_utils.database_utils import get_testing_session, default_db_url
16+
17+
18+
@pytest.fixture
19+
def update_request_gtfs_feed():
20+
return UpdateRequestGtfsFeed(
21+
id=feed_mdb_40.id,
22+
status=FeedStatus(feed_mdb_40.status.lower()),
23+
external_ids=[],
24+
provider=feed_mdb_40.provider,
25+
feed_name=feed_mdb_40.feed_name,
26+
note=feed_mdb_40.note,
27+
feed_contact_email=feed_mdb_40.feed_contact_email,
28+
source_info=SourceInfo(
29+
producer_url=feed_mdb_40.producer_url,
30+
authentication_type=AuthenticationType(
31+
int(feed_mdb_40.authentication_type)
32+
),
33+
authentication_info_url=feed_mdb_40.authentication_info_url,
34+
api_key_parameter_name=feed_mdb_40.api_key_parameter_name,
35+
license_url=feed_mdb_40.license_url,
36+
),
37+
redirects=[],
38+
operational_status_action="no_change",
39+
)
40+
41+
42+
@mock.patch.dict(
43+
os.environ,
44+
{
45+
"FEEDS_DATABASE_URL": default_db_url,
46+
},
47+
)
48+
@pytest.mark.asyncio
49+
async def test_update_gtfs_feed_no_changes(update_request_gtfs_feed):
50+
api = OperationsApiImpl()
51+
response: Response = await api.update_gtfs_feed(update_request_gtfs_feed)
52+
assert response.status_code == 204
53+
54+
55+
@mock.patch.dict(
56+
os.environ,
57+
{
58+
"FEEDS_DATABASE_URL": default_db_url,
59+
},
60+
)
61+
@pytest.mark.asyncio
62+
async def test_update_gtfs_feed_field_change(update_request_gtfs_feed):
63+
update_request_gtfs_feed.feed_name = "New feed name"
64+
with get_testing_session() as session:
65+
api = OperationsApiImpl()
66+
response: Response = await api.update_gtfs_feed(update_request_gtfs_feed)
67+
assert response.status_code == 200
68+
69+
db_feed = (
70+
session.query(Gtfsfeed)
71+
.filter(Gtfsfeed.stable_id == feed_mdb_40.stable_id)
72+
.one()
73+
)
74+
assert db_feed.feed_name == "New feed name"
75+
76+
77+
@mock.patch.dict(
78+
os.environ,
79+
{
80+
"FEEDS_DATABASE_URL": default_db_url,
81+
},
82+
)
83+
@pytest.mark.asyncio
84+
async def test_update_gtfs_feed_set_wip(update_request_gtfs_feed):
85+
update_request_gtfs_feed.operational_status_action = "wip"
86+
with get_testing_session() as session:
87+
api = OperationsApiImpl()
88+
response: Response = await api.update_gtfs_feed(update_request_gtfs_feed)
89+
assert response.status_code == 200
90+
91+
db_feed = (
92+
session.query(Gtfsfeed)
93+
.filter(Gtfsfeed.stable_id == feed_mdb_40.stable_id)
94+
.one()
95+
)
96+
assert db_feed.operational_status == "wip"
97+
98+
99+
@mock.patch.dict(
100+
os.environ,
101+
{
102+
"FEEDS_DATABASE_URL": default_db_url,
103+
},
104+
)
105+
@pytest.mark.asyncio
106+
async def test_update_gtfs_feed_invalid_feed(update_request_gtfs_feed):
107+
update_request_gtfs_feed.id = "invalid"
108+
api = OperationsApiImpl()
109+
with pytest.raises(HTTPException) as exc_info:
110+
await api.update_gtfs_feed(update_request_gtfs_feed)
111+
assert exc_info.value.status_code == 400
112+
assert exc_info.value.detail == "Feed ID not found: invalid"

0 commit comments

Comments
 (0)