Modern QA2026Testing Each Method — tiles
Log inJoin
27 / 65 · 14 API Testing Fundamentals · Authentication Testing← prev⊞ allnext →☰ Read as one page

4.3Testing Each Method

API Key Testing

def test_valid_api_key(base_url):
    r = requests.get(f"{base_url}/data",
        headers={"X-API-Key": os.environ["VALID_API_KEY"]})
    assert r.status_code == 200

def test_missing_api_key(base_url):
    r = requests.get(f"{base_url}/data")
    assert r.status_code == 401

def test_invalid_api_key(base_url):
    r = requests.get(f"{base_url}/data",
        headers={"X-API-Key": "invalid-key-12345"})
    assert r.status_code == 401
    # Should not reveal whether the key format is wrong vs not found
    assert "valid key" not in r.text.lower()

def test_revoked_api_key(base_url, revoked_key):
    r = requests.get(f"{base_url}/data",
        headers={"X-API-Key": revoked_key})
    assert r.status_code == 401

JWT Testing

def test_valid_jwt(base_url, valid_token):
    r = requests.get(f"{base_url}/users/me",
        headers={"Authorization": f"Bearer {valid_token}"})
    assert r.status_code == 200

def test_expired_jwt(base_url, expired_token):
    r = requests.get(f"{base_url}/users/me",
        headers={"Authorization": f"Bearer {expired_token}"})
    assert r.status_code == 401

def test_tampered_jwt(base_url, valid_token):
    """Modify the payload of a valid JWT — should be rejected."""
    import base64
    parts = valid_token.split(".")
    # Decode payload, modify it, re-encode (signature will be invalid)
    payload = base64.urlsafe_b64decode(parts[1] + "==")
    tampered = valid_token.replace(parts[1], base64.urlsafe_b64encode(
        payload.replace(b'"role":"viewer"', b'"role":"admin"')
    ).decode().rstrip("="))
    r = requests.get(f"{base_url}/users/me",
        headers={"Authorization": f"Bearer {tampered}"})
    assert r.status_code == 401

def test_missing_auth_returns_401(base_url):
    r = requests.get(f"{base_url}/users/me")
    assert r.status_code == 401
    assert "password" not in r.text.lower()  # No info leakage

def test_malformed_auth_header(base_url):
    r = requests.get(f"{base_url}/users/me",
        headers={"Authorization": "NotBearer token123"})
    assert r.status_code == 401

Token Refresh Flow

def test_token_refresh(base_url, refresh_token):
    """Refresh token should return a new access token."""
    r = requests.post(f"{base_url}/auth/refresh",
        json={"refresh_token": refresh_token})
    assert r.status_code == 200
    new_token = r.json()["access_token"]
    assert new_token != ""

    # New token should work
    r = requests.get(f"{base_url}/users/me",
        headers={"Authorization": f"Bearer {new_token}"})
    assert r.status_code == 200

def test_used_refresh_token_invalidated(base_url, refresh_token):
    """After using a refresh token, it should be invalidated (one-time use)."""
    r1 = requests.post(f"{base_url}/auth/refresh",
        json={"refresh_token": refresh_token})
    assert r1.status_code == 200

    r2 = requests.post(f"{base_url}/auth/refresh",
        json={"refresh_token": refresh_token})
    assert r2.status_code == 401  # Reuse should fail

Session Cookie Testing

def test_session_cookie_attributes(base_url):
    r = requests.post(f"{base_url}/auth/login", json={
        "email": "test@example.com", "password": "pass123"
    })
    assert r.status_code == 200

    # Verify cookie security attributes
    session_cookie = r.cookies.get("session_id")
    assert session_cookie is not None

    # Check Set-Cookie header for attributes
    set_cookie = r.headers.get("Set-Cookie", "")
    assert "HttpOnly" in set_cookie     # Not accessible via JavaScript
    assert "Secure" in set_cookie       # Only sent over HTTPS
    assert "SameSite" in set_cookie     # CSRF protection