about summary refs log tree commit diff
path: root/tests/test_gn3_smoke.py
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2026-05-27 13:49:30 -0500
committerFrederick Muriuki Muriithi2026-05-27 13:49:30 -0500
commit1655d8e45bbf8b0524af6529c142ada98ddace54 (patch)
tree3016c16af0bb4f2e57fffb22264e22ac6b1fc6bb /tests/test_gn3_smoke.py
downloadgn-integration-tests-1655d8e45bbf8b0524af6529c142ada98ddace54.tar.gz
Initialise with smoke tests for various GeneNetwork services.
The code in this commit was written by claude code, and is yet to be
reviewed. Expect changes once the code has been reviewed.
Diffstat (limited to 'tests/test_gn3_smoke.py')
-rw-r--r--tests/test_gn3_smoke.py215
1 files changed, 215 insertions, 0 deletions
diff --git a/tests/test_gn3_smoke.py b/tests/test_gn3_smoke.py
new file mode 100644
index 0000000..b3e4e9f
--- /dev/null
+++ b/tests/test_gn3_smoke.py
@@ -0,0 +1,215 @@
+"""
+Smoke tests for the genenetwork3 REST API.
+
+All tests hit public, unauthenticated endpoints.  The gn3 API is proxied
+at /api3/ on the gn2 domain; nginx rewrites /api3/<path> → /api/<path>
+before forwarding to gn3, so the URL seen by the caller is:
+
+    https://cd.genenetwork.org/api3/metadata/species
+    (→ gn3 internal: /api/metadata/species)
+
+Response format note: gn3 metadata endpoints return JSON-LD, shaped as:
+
+    {"@context": {...}, "data": [...]}
+
+The actual payload is always in the "data" key.  On the CD environment the
+RDF/SPARQL store may be empty, so "data" can be []; production will have
+real entries.  Content assertions (e.g. "contains mouse") are therefore
+skipped when the data list is empty.
+
+Run with:
+
+    pytest -m "gn3 and smoke"
+
+Known CD/production bugs (as of 2026-05-27):
+  - POST /api3/metadata/datasets/edit returns 500 on both CD and production
+    due to TypeError in privileges_fulfill_specs() — tracked separately.
+"""
+
+import pytest
+
+
+pytestmark = [pytest.mark.gn3, pytest.mark.smoke]
+
+# ---------------------------------------------------------------------------
+# Known-good test data (sourced from existing gn2/gn3 test fixtures)
+# ---------------------------------------------------------------------------
+_KNOWN_DATASET = "HC_M2_0606_P"   # Hippocampus Consortium M430v2 Jun06 PDNN
+_KNOWN_SPECIES = "mouse"
+_KNOWN_GROUP = "BXD"
+_KNOWN_PROBESET = "1435395_s_at"
+_KNOWN_INBREDSET_ID = 1           # BXD inbredset_id in the database
+
+
+def _jsonld_data(resp):
+    """Return the 'data' list from a JSON-LD gn3 response."""
+    body = resp.json()
+    assert isinstance(body, dict), f"Expected JSON object, got: {type(body)}"
+    assert "data" in body, f"Missing 'data' key in JSON-LD response: {list(body.keys())}"
+    assert isinstance(body["data"], list), (
+        f"'data' should be a list, got {type(body['data'])}"
+    )
+    return body["data"]
+
+
+# ---------------------------------------------------------------------------
+# GET /api3/metadata/species
+# ---------------------------------------------------------------------------
+
+class TestMetadataSpecies:
+    def test_returns_200(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/species", timeout=30)
+        assert resp.status_code == 200
+
+    def test_response_is_jsonld_with_data_key(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/species", timeout=30)
+        data = _jsonld_data(resp)  # asserts structure internally
+        assert isinstance(data, list)
+
+    def test_contains_mouse_when_data_populated(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/species", timeout=30)
+        items = _jsonld_data(resp)
+        if not items:
+            pytest.skip("Species data is empty in this environment (RDF store not populated)")
+        all_names = [
+            s.get("fullName", s.get("name", s.get("shortName", "")))
+            for s in items
+        ]
+        assert any("mouse" in n.lower() or "mus" in n.lower() for n in all_names), (
+            f"Mouse not found in species list: {all_names}"
+        )
+
+    def test_species_entry_schema_when_data_populated(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/species", timeout=30)
+        items = _jsonld_data(resp)
+        if not items:
+            pytest.skip("Species data is empty in this environment")
+        entry = items[0]
+        assert any(k in entry for k in ("fullName", "name", "shortName")), (
+            f"Species entry has no recognisable name field: {entry}"
+        )
+
+
+# ---------------------------------------------------------------------------
+# GET /api3/metadata/species/<name>
+# ---------------------------------------------------------------------------
+
+class TestMetadataSpeciesByName:
+    def test_known_species_returns_200(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/species/{_KNOWN_SPECIES}", timeout=30)
+        assert resp.status_code == 200, (
+            f"Expected 200 for species '{_KNOWN_SPECIES}', "
+            f"got {resp.status_code}: {resp.text}"
+        )
+
+    def test_unknown_species_does_not_500(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/species/nonexistent_xyz", timeout=30)
+        assert resp.status_code != 500, (
+            f"Unknown species caused 500: {resp.text}"
+        )
+
+
+# ---------------------------------------------------------------------------
+# GET /api3/metadata/groups
+# ---------------------------------------------------------------------------
+
+class TestMetadataGroups:
+    def test_returns_200(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/groups", timeout=30)
+        assert resp.status_code == 200
+
+    def test_response_is_jsonld_with_data_key(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/groups", timeout=30)
+        data = _jsonld_data(resp)
+        assert isinstance(data, list)
+
+    def test_contains_bxd_when_data_populated(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/groups", timeout=30)
+        items = _jsonld_data(resp)
+        if not items:
+            pytest.skip("Groups data is empty in this environment")
+        names = [g.get("name", g.get("shortName", "")) for g in items]
+        assert "BXD" in names, f"BXD not found in groups: {names[:20]}"
+
+
+# ---------------------------------------------------------------------------
+# GET /api3/metadata/datasets/<name>
+# ---------------------------------------------------------------------------
+
+class TestMetadataDatasets:
+    def test_known_dataset_returns_200(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/datasets/{_KNOWN_DATASET}", timeout=30)
+        assert resp.status_code == 200, (
+            f"Expected 200 for dataset '{_KNOWN_DATASET}', "
+            f"got {resp.status_code}: {resp.text}"
+        )
+
+    def test_known_dataset_response_is_jsonld(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/datasets/{_KNOWN_DATASET}", timeout=30)
+        body = resp.json()
+        assert isinstance(body, dict), f"Expected JSON object, got {type(body)}"
+        assert "@context" in body or "data" in body, (
+            f"Response does not look like JSON-LD: {list(body.keys())}"
+        )
+
+    def test_unknown_dataset_does_not_500(self, gn3_url, http):
+        resp = http.get(f"{gn3_url}/metadata/datasets/NONEXISTENT_XYZ", timeout=30)
+        assert resp.status_code != 500, (
+            f"Unknown dataset caused 500: {resp.text}"
+        )
+
+
+# ---------------------------------------------------------------------------
+# GET /api3/metadata/probesets/<dataset>/<name>
+# ---------------------------------------------------------------------------
+
+class TestMetadataProbesets:
+    def test_known_probeset_returns_200(self, gn3_url, http):
+        resp = http.get(
+            f"{gn3_url}/metadata/probesets/{_KNOWN_DATASET}/{_KNOWN_PROBESET}",
+            timeout=30,
+        )
+        assert resp.status_code == 200, (
+            f"Expected 200 for probeset '{_KNOWN_DATASET}/{_KNOWN_PROBESET}', "
+            f"got {resp.status_code}: {resp.text}"
+        )
+
+
+# ---------------------------------------------------------------------------
+# GET /api3/case-attribute/<inbredset_id>
+# ---------------------------------------------------------------------------
+
+class TestCaseAttributes:
+    def test_known_inbredset_does_not_500(self, gn3_url, http):
+        resp = http.get(
+            f"{gn3_url}/case-attribute/{_KNOWN_INBREDSET_ID}",
+            timeout=30,
+        )
+        assert resp.status_code != 500, (
+            f"case-attribute inbredset_id={_KNOWN_INBREDSET_ID} caused 500: {resp.text}"
+        )
+
+
+# ---------------------------------------------------------------------------
+# Protected endpoints — known bug
+# ---------------------------------------------------------------------------
+
+@pytest.mark.xfail(
+    reason=(
+        "BUG: POST /api3/metadata/datasets/edit returns 500 on both CD and "
+        "production due to TypeError: privileges_fulfill_specs() missing 1 "
+        "required positional argument: 'system_privileges' "
+        "(gn3/api/metadata.py:279). Should return 401 without a token."
+    ),
+    strict=True,
+)
+def test_metadata_edit_without_token_returns_401(gn3_url, http):
+    resp = http.post(
+        f"{gn3_url}/metadata/datasets/edit",
+        json={},
+        timeout=30,
+    )
+    assert resp.status_code == 401, (
+        f"Expected 401 on /metadata/datasets/edit without token, "
+        f"got {resp.status_code}: {resp.text}"
+    )