-
Notifications
You must be signed in to change notification settings - Fork 213
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add pagination functionality to FHIRClient and related unit tests
- Loading branch information
Lana
committed
Aug 6, 2024
1 parent
796ffa6
commit 1d9f8f6
Showing
2 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import unittest | ||
from unittest.mock import patch, MagicMock | ||
|
||
import requests | ||
from fhirclient.client import FHIRClient | ||
|
||
|
||
class TestFHIRClientPagination(unittest.TestCase): | ||
def setUp(self) -> None: | ||
state = { | ||
"app_id": "AppID", | ||
"app_secret": "AppSecret", | ||
"scope": "user/*.read", | ||
"redirect": "http://test.invalid/redirect", | ||
"patient_id": "PatientID", | ||
"server": { | ||
"base_uri": "http://test.invalid/", | ||
"auth_type": "none", | ||
"auth": { | ||
"app_id": "AppId", | ||
}, | ||
}, | ||
"launch_token": "LaunchToken", | ||
"launch_context": { | ||
"encounter": "EncounterID", | ||
"patient": "PatientID", | ||
}, | ||
"jwt_token": "JwtToken", | ||
} | ||
|
||
# Confirm round trip | ||
self.client = FHIRClient(state=state) | ||
|
||
self.bundle = { | ||
"link": [ | ||
{"relation": "self", "url": "http://example.com/fhir/Bundle/1"}, | ||
{"relation": "next", "url": "http://example.com/fhir/Bundle/2"}, | ||
], | ||
"entry": [ | ||
{ | ||
"fullUrl": "http://example.com/fhir/Patient/1", | ||
"resource": { | ||
"resourceType": "Patient", | ||
"id": "1", | ||
"name": [{"family": "Doe", "given": ["John"]}], | ||
"gender": "male", | ||
"birthDate": "1980-01-01", | ||
}, | ||
}, | ||
{ | ||
"fullUrl": "http://example.com/fhir/Patient/2", | ||
"resource": { | ||
"resourceType": "Patient", | ||
"id": "2", | ||
"name": [{"family": "Smith", "given": ["Jane"]}], | ||
"gender": "female", | ||
"birthDate": "1990-05-15", | ||
}, | ||
}, | ||
], | ||
} | ||
|
||
def test_get_next_link(self): | ||
next_link = self.client.get_next_link(self.bundle) | ||
self.assertEqual(next_link, "http://example.com/fhir/Bundle/2") | ||
|
||
def test_get_next_link_no_next(self): | ||
bundle_without_next = {"link": [{"relation": "self", "url": "http://example.com/fhir/Bundle/1"}]} | ||
next_link = self.client.get_next_link(bundle_without_next) | ||
self.assertIsNone(next_link) | ||
|
||
def test_sanitize_next_link_valid(self): | ||
next_link = "http://example.com/fhir/Bundle/2?page=2&size=10" | ||
sanitized_link = self.client.sanitize_next_link(next_link) | ||
self.assertEqual(sanitized_link, next_link) | ||
|
||
def test_sanitize_next_link_invalid_scheme(self): | ||
next_link = "ftp://example.com/fhir/Bundle/2?page=2&size=10" | ||
with self.assertRaises(ValueError): | ||
self.client.sanitize_next_link(next_link) | ||
|
||
def test_sanitize_next_link_invalid_domain(self): | ||
next_link = "http:///fhir/Bundle/2?page=2&size=10" | ||
with self.assertRaises(ValueError): | ||
self.client.sanitize_next_link(next_link) | ||
|
||
@patch("requests.get") | ||
def test_execute_pagination_request_success(self, mock_get): | ||
mock_response = MagicMock() | ||
# Set up the mock to return a specific JSON payload when its json() method is called | ||
mock_response.json.return_value = self.bundle | ||
mock_response.raise_for_status = MagicMock() | ||
mock_get.return_value = mock_response | ||
|
||
next_link = "http://example.com/fhir/Bundle/2" | ||
sanitized_link = self.client.sanitize_next_link(next_link) | ||
result = self.client.execute_pagination_request(sanitized_link) | ||
|
||
# Check that the result is a dictionary (which is the mocked JSON response) | ||
self.assertIsInstance(result, dict) | ||
# Ensure that "entry" is a key in the returned dictionary | ||
self.assertIn("entry", result) | ||
# Assert that requests.get was called exactly once with the sanitized URL | ||
mock_get.assert_called_once_with(sanitized_link) | ||
|
||
@patch("requests.get") | ||
def test_execute_pagination_request_http_error(self, mock_get): | ||
mock_get.side_effect = requests.exceptions.HTTPError("HTTP Error") | ||
|
||
next_link = "http://example.com/fhir/Bundle/2" | ||
sanitized_link = self.client.sanitize_next_link(next_link) | ||
|
||
with self.assertRaises(requests.exceptions.HTTPError): | ||
self.client.execute_pagination_request(sanitized_link) | ||
|
||
@patch("fhirclient.client.FHIRClient.execute_pagination_request") | ||
def test_fetch_next_page(self, mock_execute_request): | ||
mock_execute_request.return_value = self.bundle | ||
|
||
result = self.client.fetch_next_page(self.bundle) | ||
|
||
self.assertIsInstance(result, dict) | ||
self.assertIn("entry", result) | ||
mock_execute_request.assert_called_once() | ||
|
||
def test_fetch_next_page_no_next_link(self): | ||
bundle_without_next = {"link": [{"relation": "self", "url": "http://example.com/fhir/Bundle/1"}]} | ||
result = self.client.fetch_next_page(bundle_without_next) | ||
self.assertIsNone(result) |