diff options
| author | Frederick Muriuki Muriithi | 2026-08-28 16:45:55 +0000 |
|---|---|---|
| committer | Frederick Muriuki Muriithi | 2026-08-28 14:59:06 -0500 |
| commit | 7656a8f22a53aa029dd5c6b832ed5b6975fc5485 (patch) | |
| tree | ac4edb04fa4caff3fb929ab47473696037e32f56 /tests | |
| parent | 5eea39e00246ccb7da60b074786b93c4297b0a1e (diff) | |
| download | gn-integration-tests-7656a8f22a53aa029dd5c6b832ed5b6975fc5485.tar.gz | |
test(gn3/case-attr): level 4 bug test — resource-owner edit returns 201
Adds provisioning fixtures and a parametrized test that assigns resource-owner role to a freshly created test user on the BXD population resource, then expects 201 from both the flat and v1 edit endpoints. The test is RED with current code because __population_privileges__ calls GET /auth/resource/<id>/roles without a Bearer token, so resource_privs is always empty and can_edit's resource_spec branch is never satisfied. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_gn3_case_attr_access.py | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/tests/test_gn3_case_attr_access.py b/tests/test_gn3_case_attr_access.py index dcce6ea..7cb234f 100644 --- a/tests/test_gn3_case_attr_access.py +++ b/tests/test_gn3_case_attr_access.py @@ -4,6 +4,7 @@ Case-attribute names are publicly accessible (no token required). All write endpoints require a valid token, and a token with appropriate edit privileges. """ +import uuid import pytest pytestmark = pytest.mark.gn3 @@ -87,3 +88,163 @@ def test_no_privilege_reject_returns_401(gn3_url, http, basic_access_token, path f"Expected 401 for unprivileged POST {path!r}, " f"got {resp.status_code}. Body: {resp.text[:200]}" ) + + +# --------------------------------------------------------------------------- +# Provisioning helpers for Levels 4–5 +# --------------------------------------------------------------------------- + +@pytest.fixture(scope="session") +def _admin_full_scope_token(gn_auth_url, http, oauth2_credentials): + """Admin Bearer token with user+role scope for provisioning test users.""" + email, password, client_id, client_secret = oauth2_credentials + resp = http.post( + f"{gn_auth_url}/auth/token", + json={ + "grant_type": "password", + "username": email, + "password": password, + "scope": "profile group resource user role", + "client_id": client_id, + "client_secret": client_secret, + }, + timeout=30, + ) + assert resp.status_code == 200, ( + f"Admin full-scope token request failed: {resp.text}" + ) + return resp.json()["access_token"] + + +@pytest.fixture(scope="session") +def bxd_resource_id(gn_auth_url, http): + """Resource UUID for the BXD population (SpeciesId=1, InbredSetId=1). + + Skips the test suite if the population is not registered in gn-auth. + """ + resp = http.get( + f"{gn_auth_url}/auth/resource/populations/resource-id" + f"/{_SPECIES_ID}/{_INBREDSET_ID}", + timeout=30, + ) + if resp.status_code == 404: + pytest.skip( + f"BXD population (species={_SPECIES_ID}, pop={_INBREDSET_ID}) " + "is not registered as a resource in gn-auth — skipping Level 4+ tests." + ) + assert resp.status_code == 200, ( + f"Unexpected {resp.status_code} fetching BXD resource-id: {resp.text}" + ) + return resp.json()["resource-id"] + + +@pytest.fixture +def provisioned_token(gn_auth_url, http, _admin_full_scope_token, oauth2_credentials): + """Fixture factory: create a test user, assign role on a resource, yield token, clean up. + + Usage inside a test:: + + def test_something(gn3_url, http, provisioned_token, bxd_resource_id): + token = provisioned_token("resource-owner", bxd_resource_id) + resp = http.post(url, headers={"Authorization": f"Bearer {token}"}) + """ + *_, client_id, client_secret = oauth2_credentials + admin_hdrs = {"Authorization": f"Bearer {_admin_full_scope_token}"} + created: list = [] # [(user_id, role_name, resource_id), ...] + + def _make(role_name: str, resource_id: str) -> str: + unique = str(uuid.uuid4())[:8] + email = f"test-{role_name}-{unique}@regression.genenetwork.org" + password = "GnTest1234!" + + create_resp = http.post( + f"{gn_auth_url}/auth/user/create", + json={"email": email, "name": f"Test {role_name}", "password": password}, + headers=admin_hdrs, + timeout=30, + ) + assert create_resp.status_code == 201, ( + f"Failed to create test user for role {role_name!r}: " + f"{create_resp.status_code} {create_resp.text}" + ) + user_id = create_resp.json()["user_id"] + created.append((user_id, role_name, resource_id)) + + assign_resp = http.post( + f"{gn_auth_url}/auth/system/administration/resources/{resource_id}/assign-owner", + json={"user_id": user_id}, + headers=admin_hdrs, + timeout=30, + ) + assert assign_resp.status_code == 200, ( + f"Failed to assign resource-owner to user {user_id} on {resource_id}: " + f"{assign_resp.status_code} {assign_resp.text}" + ) + + token_resp = http.post( + f"{gn_auth_url}/auth/token", + json={ + "grant_type": "password", + "username": email, + "password": password, + "scope": "profile group resource", + "client_id": client_id, + "client_secret": client_secret, + }, + timeout=30, + ) + assert token_resp.status_code == 200, ( + f"Failed to get token for test user: {token_resp.status_code} {token_resp.text}" + ) + return token_resp.json()["access_token"] + + yield _make + + for user_id, _role_name, resource_id in created: + http.post( + f"{gn_auth_url}/auth/system/administration/resources/{resource_id}/revoke-owner", + json={"user_id": user_id}, + headers=admin_hdrs, + timeout=30, + ) + http.post( + f"{gn_auth_url}/auth/user/delete", + json={"user_ids": [user_id]}, + headers=admin_hdrs, + timeout=30, + ) + + +# --------------------------------------------------------------------------- +# Level 4: edit with resource-owner privilege +# (documents the __population_privileges__ bug) +# --------------------------------------------------------------------------- + +@pytest.mark.case_attr_access +@pytest.mark.auth_flow +@pytest.mark.parametrize("path", [ + f"/case-attribute/{_INBREDSET_ID}/edit", + f"/v1/species/{_SPECIES_ID}/populations/{_INBREDSET_ID}/case-attributes/edit", +]) +def test_resource_owner_can_edit_returns_201( + gn3_url, http, provisioned_token, bxd_resource_id, path): + """A resource-owner on the BXD population must be allowed to queue edits (201). + + BUG: Currently returns 401 because __population_privileges__ calls + GET /auth/resource/<id>/roles without a Bearer token, so resource_privs is + always empty and the resource_spec branch of can_edit is never reachable. + This test is RED until that bug is fixed. + """ + token = provisioned_token("resource-owner", bxd_resource_id) + resp = http.post( + f"{gn3_url}{path}", + json={"edit-data": []}, + headers={"Authorization": f"Bearer {token}"}, + timeout=30, + ) + assert resp.status_code == 201, ( + f"Expected 201 for resource-owner POST {path!r}, " + f"got {resp.status_code}. " + f"If 401: likely the __population_privileges__ bug (resource_privs always empty). " + f"Body: {resp.text[:300]}" + ) |
