fakecspace
A small simulated CollectionSpace services API, for developing and testing UC Berkeley's CollectionSpace apps: the Bulk Media Uploader (cspace-bulk-media-uploader) and Serena, the media server (cspace-media-server). Both apps use this one copy of the simulator.
It is not CollectionSpace, and it must never hold real data. Its accounts are synthetic and each password is the user name (published here, in
src/fakecspace/accounts.py). Its records are in memory and gone when it stops. Behaviour against the real server must be confirmed on a CollectionSpace test tenant. Each app's deployment keeps its own switch, so the simulator is never deployed with production.
What it simulates
- One CollectionSpace per museum (bampfa, botgarden, cinefiles, pahma, ucjeps), each with its own records. The
museum comes from the host name a request uses (
pahma.fakecspace) or a path prefix (/pahma/cspace-services/...); PAHMA when neither names one. Tenant IDs are synthetic (PAHMA is 14). - Accounts:
accounts/0/accountpermsandaccounts/0/accountroles, with the BMU's users (admin,limited,intern,newstaff,reader,bmureader) and Serena's (serena,pahma-admin,viewer;adminis a Serena admin of every museum). - Records: Media (GET, POST, search by
identificationNumber,titleorblobCsid), files attached withPUT media/<csid>/blob, Objects, Relations, groups, Person and Organization terms, the languages vocabulary, and the date parser. - Files:
media/<csid>/blob/content,media/<csid>/blob/derivatives/<name>/content,blobs/<csid>andblobs/<csid>/derivatives/<name>/content. A file uploaded the BMU's way is served as the original and as every derivative (Thumbnail, Small, Medium, FullHD, OriginalJpeg). A file over 5 MB isn't kept: the record then has no file. - Development hooks under
/_fake(src/fakecspace/hooks.pylists them): reset, sample sets, records with sample files, soft delete, replace, an outage, failures on demand, slowness, and terms changed meanwhile.
The simulator starts empty. A client asks for the samples it needs after each reset:
curl -X POST http://localhost:8180/_fake/reset
curl -X POST http://localhost:8180/_fake/seed -H 'Content-Type: application/json' -d '{"set": "bmu"}'
Failures on demand
POST /_fake/fail takes one JSON shape for both apps:
| Field | Meaning |
|---|---|
step |
One step of the BMU's work: media, upload, objectSearch, objectCreate, relation, mediaSearch, group, termRead. Without a step, the rule applies to every request whose path contains path. |
path |
Part of a request's path, e.g. /blob/content. |
match |
With a step: part of a name the step carries (identification number, file name, object number, group title, term). |
status |
The HTTP status answered (default 500). |
effect |
none or many, for objectSearch and mediaSearch: what the search finds, instead of a status. |
times |
How many requests fail (default 1; 0 until DELETE /_fake/fail). |
agent |
Only requests whose User-Agent starts with this, e.g. bmu-worker for the BMU's job runs. |
curl -X POST http://localhost:8180/_fake/fail -H 'Content-Type: application/json' \
-d '{"path": "/blob/content", "status": 503, "times": 2}'
Using it from an app
Pin a version from PyPI, with its hash, in the app's development requirements (pip install fakecspace for a quick
try). The extras: server (uvicorn, to run it) and bands (pyvips, for records with a size band).
In tests, call it in-process, with no server:
from fastapi.testclient import TestClient
from fakecspace.app import app, stores
stores.reset() # every museum's store empty
client = TestClient(app, base_url="http://pahma.fakecspace")
stores["pahma"].add("m-1") # a Media record with sample files
fakecspace.app.store is PAHMA's store. A Media record (store.media[csid]) can be read by attribute
(media.blob) or by CollectionSpace's field name (media["blobCsid"]).
To run it: uvicorn fakecspace.app:app --port 8180, or
docker build -t fakecspace . && docker run --rm -p 8180:8180 fakecspace.
Development
Python 3.11 (.python-version).
python3.11 -m venv .venv && . .venv/bin/activate
pip install --require-hashes -r requirements-dev.txt
pip install --no-deps --no-build-isolation -e .
pytest -q && ruff check . && mypy
Dependencies are listed in pyproject.toml and pinned with hashes in requirements.txt (the server, what the
Dockerfile installs), requirements-dev.txt and requirements-tools.txt, all made by ./pin-requirements.sh.
Never edit those files by hand. CLAUDE.md has the rules for changes.
Releasing
- In a pull request, set the new version in
src/fakecspace/__init__.pyand add it toCHANGELOG.md. - Optional dry run: Actions › Publish › Run workflow on the branch publishes it to TestPyPI.
- After the merge, tag the merge commit
vX.Y.Zand push the tag. The Publish workflow builds it and, once a reviewer approves thepypienvironment, publishes it to PyPI through trusted publishing (no stored token). - Dependabot then proposes the new version in each app.
License
Copyright The Regents of the University of California: educational, research and not-for-profit use. See LICENSE.
Metadata
Release files for fakecspace 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fakecspace-0.2.0.tar.gz | 37.1 kB | With provenance | Details for fakecspace-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fakecspace-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for fakecspace-0.2.0-py3-none-any.whl |
Total release size: 68.7 kB
Release files / fakecspace-0.2.0.tar.gz
| Download URL | fakecspace-0.2.0.tar.gz |
|---|---|
| Size | 37.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
28af0140e5bb6fa9b284e322a919fa0bfd690650b2d9e6d703aea89c51ea9fbe
|
|
BLAKE2b-256 checksum How to use checksums |
65182b7a5ff8d6458c097c4286e8308e00d78fc59d6abc1a857e35bdf75888fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logRelease files / fakecspace-0.2.0-py3-none-any.whl
| Download URL | fakecspace-0.2.0-py3-none-any.whl |
|---|---|
| Size | 31.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3853b1ab9dc80d8ae0531a0ccd7ca973c8bf06d83872de38453493d7e27e623c
|
|
BLAKE2b-256 checksum How to use checksums |
c89d2aa0beb272ab086a9d496376e3f3af411a1b37b30e469b5e7c05b1f54d3c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logSecurity for fakecspace 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fakecspace-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.