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

Bug fixes #113

Merged
merged 8 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[flake8]
per-file-ignores =
tests/*:E501, W503
tests/*:E501, W503
tests/test_bookops_worldcat.py: F401
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version}}
Expand Down
2 changes: 1 addition & 1 deletion bookops_worldcat/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Union, Tuple, TYPE_CHECKING
import sys

from requests.models import PreparedRequest
from requests import PreparedRequest
from requests.exceptions import ConnectionError, HTTPError, Timeout, RetryError
from .errors import WorldcatRequestError

Expand Down
3 changes: 3 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,9 @@ six==1.16.0 ; python_version >= "3.8" and python_version < "4.0" \
tomli==2.0.1 ; python_version >= "3.8" and python_full_version <= "3.11.0a6" \
--hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
--hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
types-pyyaml==6.0.12.20240917 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570 \
--hash=sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587
types-requests==2.31.0.20240125 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:03a28ce1d7cd54199148e043b2079cdded22d6795d19a2c2a6791a4b2b5e2eb5 \
--hash=sha256:9592a9a4cb92d6d75d9b491a41477272b710e021011a2a3061157e2fb1f1a5d1
Expand Down
13 changes: 12 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ types-requests = "^2.31.0.20240125"
mkdocs-material = "^9.5.13"
mkdocstrings = "^0.24.1"
mkdocstrings-python = "^1.9.0"
types-pyyaml = "^6.0.12.20240917"

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
88 changes: 27 additions & 61 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
import datetime
import json
import os

from typing import Dict, Generator, Union
import pytest
import requests

from requests import Response

from bookops_worldcat import WorldcatAccessToken, MetadataSession


@pytest.fixture
def live_keys():
if os.name == "nt":
if os.name == "nt" and not os.getenv("GITHUB_ACTIONS"):
fh = os.path.join(os.environ["USERPROFILE"], ".oclc/nyp_wc_test.json")
with open(fh, "r") as file:
data = json.load(file)
Expand All @@ -24,25 +22,20 @@ def live_keys():


@pytest.fixture
def stub_marc_xml():
def stub_marc_xml() -> str:
stub_marc_xml = "<record><leader>00000nam a2200000 a 4500</leader><controlfield tag='008'>120827s2012 nyua 000 0 eng d</controlfield><datafield tag='010' ind1=' ' ind2=' '><subfield code='a'> 63011276 </subfield></datafield><datafield tag='035' ind1=' ' ind2=' '><subfield code='a'>ocn850940548</subfield></datafield><datafield tag='040' ind1=' ' ind2=' '><subfield code='a'>OCWMS</subfield><subfield code='b'>eng</subfield><subfield code='c'>OCWMS</subfield></datafield><datafield tag='100' ind1='0' ind2=' '><subfield code='a'>OCLC Developer Network</subfield></datafield><datafield tag='245' ind1='1' ind2='0'><subfield code='a'>Test Record</subfield></datafield><datafield tag='500' ind1=' ' ind2=' '><subfield code='a'>FOR OCLC DEVELOPER NETWORK DOCUMENTATION</subfield></datafield></record>"
return stub_marc_xml


@pytest.fixture
def stub_holding_xml():
def stub_holding_xml() -> str:
stub_holding_xml = "<record><leader>00000nx a2200000zi 4500</leader><controlfield tag='004'>312010</controlfield><controlfield tag='007'>zu</controlfield><controlfield tag='008'>1103280p 0 4001uueng0210908</controlfield><datafield ind2=' ' ind1=' ' tag='852'><subfield code='a'>OCWMS</subfield><subfield code='b'>EAST</subfield><subfield code='c'>EAST-STACKS</subfield></datafield><datafield ind2=' ' ind1=' ' tag='876'><subfield code='p'>879456</subfield></datafield></record>"
return stub_holding_xml


@pytest.fixture
def stub_marc21():
fh = os.path.join(
os.environ["USERPROFILE"], "github/bookops-worldcat/tests/test.mrc"
)
with open(fh, "rb") as stub:
stub_marc21 = stub.read()
return stub_marc21
def stub_marc21() -> bytes:
return b"00266nam a2200097 a 4500008004100000010001700041040002200058100002700080245001600107500004500123\x1e120827s2012 nyua 000 0 eng d\x1e \x1fa 63011276 \x1e \x1faOCWMS\x1fbeng\x1fcOCWMS\x1e0 \x1faOCLC Developer Network\x1e10\x1faTest Record\x1e \x1faFOR OCLC DEVELOPER NETWORK DOCUMENTATION\x1e\x1d"


class FakeUtcNow(datetime.datetime):
Expand All @@ -59,10 +52,10 @@ def mock_now(monkeypatch):
class MockAuthServerResponseSuccess:
"""Simulates auth server response to successful token request"""

def __init__(self):
def __init__(self) -> None:
self.status_code = 200

def json(self):
def json(self) -> Dict[str, str]:
expires_at = datetime.datetime.strftime(
datetime.datetime.now() + datetime.timedelta(0, 1199),
"%Y-%m-%d %H:%M:%SZ",
Expand All @@ -83,11 +76,11 @@ def json(self):
class MockAuthServerResponseFailure:
"""Simulates auth server response to successful token request"""

def __init__(self):
def __init__(self) -> None:
self.status_code = 403
self.content = b""

def json(self):
def json(self) -> Dict[str, Union[str, int]]:
return {
"code": 403,
"message": "Invalid scope(s): invalid (invalid) [Invalid service specified, Not on key]",
Expand All @@ -97,38 +90,38 @@ def json(self):
class MockServiceErrorResponse:
"""Simulates web service error responses"""

def __init__(self, code, json_response, url):
def __init__(self, code, json_response, url) -> None:
self.status_code = code
self.msg = json_response
self.url = url
self.text = f"{json_response}"

def json(self):
def json(self) -> Dict[str, str]:
return self.msg


class MockUnexpectedException:
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
raise Exception


class MockTimeout:
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
raise requests.exceptions.Timeout


class MockConnectionError:
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
raise requests.exceptions.ConnectionError


class MockRetryError:
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
raise requests.exceptions.RetryError


class MockHTTPSessionResponse(Response):
def __init__(self, http_code):
class MockHTTPSessionResponse(requests.Response):
def __init__(self, http_code) -> None:
self.status_code = http_code
self.reason = "'foo'"
self.url = "https://foo.bar?query"
Expand All @@ -155,7 +148,7 @@ def mock_api_response(*args, http_code=http_code, **kwargs):


@pytest.fixture
def mock_credentials():
def mock_credentials() -> Dict[str, str]:
return {
"key": "my_WSkey",
"secret": "my_WSsecret",
Expand All @@ -164,7 +157,9 @@ def mock_credentials():


@pytest.fixture
def mock_oauth_server_response(mock_now, *args, **kwargs):
def mock_oauth_server_response(
mock_now, *args, **kwargs
) -> MockAuthServerResponseSuccess:
return MockAuthServerResponseSuccess()


Expand Down Expand Up @@ -213,18 +208,20 @@ def mock_retry_error(monkeypatch):


@pytest.fixture
def mock_token(mock_credentials, mock_successful_post_token_response):
def mock_token(
mock_credentials, mock_successful_post_token_response
) -> WorldcatAccessToken:
return WorldcatAccessToken(**mock_credentials)


@pytest.fixture
def stub_session(mock_token):
def stub_session(mock_token) -> Generator[MetadataSession, None, None]:
with MetadataSession(authorization=mock_token) as session:
yield session


@pytest.fixture
def stub_retry_session(mock_token):
def stub_retry_session(mock_token) -> Generator[MetadataSession, None, None]:
with MetadataSession(
authorization=mock_token,
totalRetries=3,
Expand All @@ -233,34 +230,3 @@ def stub_retry_session(mock_token):
allowedMethods=["GET", "POST", "PUT"],
) as session:
yield session


@pytest.fixture
def mock_400_response(monkeypatch):
def mock_api_response(*args, **kwargs):
msg = {
"type": "MISSING_QUERY_PARAMETER",
"title": "Validation Failure",
"detail": "details here",
}
url = "https://test.org/some_endpoint"
return MockServiceErrorResponse(code=400, json_response=msg, url=url)

monkeypatch.setattr(requests.Session, "get", mock_api_response)
monkeypatch.setattr(requests.Session, "post", mock_api_response)
monkeypatch.setattr(requests.Session, "delete", mock_api_response)


@pytest.fixture
def mock_409_response(monkeypatch):
def mock_api_response(*args, **kwargs):
msg = {
"code": {"value": "WS-409", "type": "application"},
"message": "Trying to set hold while holding already exists",
"detail": None,
}
url = "https://test.org/some_endpoint"
return MockServiceErrorResponse(code=409, json_response=msg, url=url)

monkeypatch.setattr(requests.Session, "post", mock_api_response)
monkeypatch.setattr(requests.Session, "delete", mock_api_response)
1 change: 0 additions & 1 deletion tests/test.mrc

This file was deleted.