feat(dev): add mockservices — mocks IA/third-party services for local dev (replaces /internal/fake/* stubs)#13045
Conversation
|
This endpoint would be great to have This endpoint is needed for testing #12689 The way we'd want this to work is:
|
cf1abe0 to
a9f070c
Compare
|
PAM (Open Library's Project AI Manager) — status update on this PR. Rebased onto latest Fixed
Added
Known limitation (pre-existing, not a regression)The |
a9f070c to
9252ac1
Compare
…e2e testing
Adds docker/mockservices/ — a standalone FastAPI service that replaces the
web.py inline stubs (/internal/fake/*) with real isolated endpoints, and
mocks additional external services that previously hit archive.org in dev.
Endpoints implemented:
- POST /services/xauthn/ (was /internal/fake/xauth)
- GET /services/s3auth/ (was /internal/fake/s3auth)
- POST /services/loans/loan/ (was /internal/fake/loans)
- POST /services/availability/ (was not mocked — pointed at archive.org)
- GET /services/borrow/{id} (was hardcoded in lending.py)
- POST /recaptcha/api/siteverify (was not mocked — pointed at recaptcha.net)
- GET /fts/v1/search (available, but NOT wired as the default —
be-api.us.archive.org works fine for local
dev as-is, so search_endpoint keeps pointing
there)
- POST /paapi5/getItems (Amazon PA-API stub)
mailpit runs alongside FastAPI in the same container:
- SMTP on :1025 — receives email instead of dummy_sendmail stdout logging
- Web UI on :8025 — browse captured mail at http://localhost:8025
openlibrary.yml updated to point all ia_* URLs at mockservices:8090.
Closes #13044
… cover S3 loan URL - Remove the four /internal/fake/* stub classes from account.py (availability, loans, xauth, s3auth); they are now served by the mockservices container. - Add ia_s3_loan_url config key to lending.py so get_groundtruth_availability and s3_loan_api can be redirected to mockservices (previously they used S3_LOAN_URL % config_bookreader_host which hardcoded archive.org). - Wire ia_s3_loan_url in openlibrary.yml pointing at mockservices. - Forward smtp_port from openlibrary.yml to web.config in openlibrary/config.py's load_config() (previously only smtp_server was forwarded by infogami, so smtp_port: 1025 in openlibrary.yml was silently ignored and mailpit never received email). Done on the OL side rather than patching the infogami submodule, since that commit was never pushed to the infogami remote and broke CI's submodule fetch.
COPY requirements.txt/main.py/start.sh in the Dockerfile resolved against the build context (repo root, per compose.override.yaml), not the Dockerfile's own directory — this silently pulled in the root requirements.txt (with its git+https deps) instead of the minimal mockservices one, breaking the build. The healthcheck also called wget, which the Dockerfile purges right after using it to install mailpit, so the container never reported healthy. Switched the healthcheck to python3, always present in the base image.
xauthn read `op` from the form body and returned a `status` field, but the real client (InternetArchiveAccount.xauth) sends `op` as a query param, the payload as a JSON body, and reads `success` — so every op was effectively broken (op always empty, "Unknown op" for every call). redeem_otp also checked the wrong field name (`otp` instead of `password`, which is what the client actually sends the OTP in), and issue_key/activate were missing entirely, breaking S3 key issuance and account activation. Added python-multipart, needed for the (now correctly parsed) request bodies. Also adds a GET /services/loans/loan/?action=changes handler mocking IA's loan changes feed, needed by the near-realtime loan availability updater (#12689) for local testing. Seeds 1000 borrow/return events on startup using real `ia` identifiers from local Solr (falling back to a static pool if Solr is empty/unreachable), with timestamps spread over the last 14 days to exercise catchup/backfill. A background loop then adds 50 events and evicts the oldest 50 every 60s to exercise ongoing steady-state polling.
…iner Runs directly against mockservices over the webnet docker network (docker compose run --rm home pytest docker/mockservices/tests/test_e2e.py), exercising every endpoint with the exact wire format the real OL client code sends. Skips gracefully (not a hard failure) when mockservices isn't reachable, so it's safe to include in a full test run without Docker up.
Renames docker/mockservices/ -> docker/ghostwriter/, the compose service, container hostname in openlibrary.yml, and every internal reference. Dev-only — confirmed zero references in compose.yaml (the prod/staging base file); the service is defined entirely in compose.override.yaml, which never applies outside local dev.
9252ac1 to
6c6daa4
Compare
|
@RayBB I think this PR should be a big improvement in terms of creating a microservice sidecar that local Open Library can hit to safely test external dependencies like account creation and loans (which are currently difficult for developers to test). |
|
@cdrini; Mocker instead of Ghostwriter? |
|
Feel free to change to Mocker, for now, would like this functionality available to the team and we can open a follow-up PR. I'd like to unblock @benbdeitch who can benefit from the changes API that is in this service. Gave PR a 2 week grace period for review. |
…es naming Team decided MockServices was the better name after all. Reverts the prior rename commit as a new commit rather than rewriting history -- the intermediate commits stay intact. This reverts commit 6c6daa4.
Summary
Replaces the four inline web.py stub classes in
account.pywith a standaloneFastAPI container, mockservices, that mocks all external IA/third-party
services that dev environments need to hit. Adds mailpit for email capture.
Stubs removed from
account.py:/internal/fake/availability→POST /services/availability//internal/fake/loans→POST /services/loans/loan//internal/fake/xauth→POST /services/xauthn//internal/fake/s3auth→GET /services/s3auth/New endpoints in mockservices:
POST /services/xauthn//internal/fake/xauthGET /services/s3auth//internal/fake/s3authPOST /services/loans/loan//internal/fake/loansGET /services/loans/loan/?action=changesPOST /services/availability//internal/fake/availabilityGET /services/borrow/{id}POST /recaptcha/api/siteverifyGET /fts/v1/searchbe-api.us.archive.orgworks fine for local dev as-isPOST /paapi5/getItemsLoan changes feed: seeds 1000 borrow/return events on startup using real
iaidentifiers pulled from local Solr (falls back to a static pool if Solris empty/unreachable), timestamps spread over the last 14 days to exercise
catchup/backfill. A background loop then adds 50 events and evicts the oldest
50 every 60s to exercise ongoing steady-state polling.
Config changes in
openlibrary.yml:Most external service URLs now point at
http://mockservices:8090/....smtp_server: mockservices,smtp_port: 1025,dummy_sendmail: Falserouteemail to mailpit (browse at http://localhost:8025).
search_endpointis leftpointing at the real
be-api.us.archive.org— it works fine for local dev.S3 loan URL coverage:
Added
ia_s3_loan_urlconfig key tolending.py. Theget_groundtruth_availability()and
s3_loan_api()functions previously usedS3_LOAN_URL % config_bookreader_hostwhich hardcoded
archive.orgas the host. They now fall back to the new config key.smtp_port fix:
web.sendmail()readsweb.config.smtp_portbut infogami only copiedsmtp_serverto
web.config. Originally patched in theinfogamisubmodule, but that commit wasnever pushed to
internetarchive/infogami, which broke CI's submodule fetch. Movedthe fix to
openlibrary/config.py'sload_config()instead — no infogami changesneeded, and
smtp_portnow correctly forwards so mailpit receives email.Naming note: this was briefly renamed to "ghostwriter" mid-PR; the team decided
MockServiceswas the better name, so a later commit reverts that rename rather thanrewriting history — the intermediate commits (including the rename itself) stay intact.
Testing
Related
Closes #13044