from __future__ import annotations import pytest from django.test import RequestFactory from yksa_kit.auth import ( extract_token, request_can_ingest, request_has_valid_token, resolve_token, store_session_token, ) from yksa_kit.models import ApiToken, TokenKind @pytest.fixture def rf() -> RequestFactory: return RequestFactory() @pytest.mark.django_db def test_returns_none_when_no_credentials(rf): assert extract_token(rf.get("/api/v1/things/")) is None assert request_has_valid_token(rf.get("/api/v1/things/")) is False @pytest.mark.django_db def test_authorization_bearer_header(rf): token = ApiToken.objects.create(name="ci-job") resolved = extract_token(rf.get("/", HTTP_AUTHORIZATION=f"Bearer {token.token}")) assert resolved is not None and resolved.pk == token.pk @pytest.mark.django_db def test_authorization_raw_header_accepted(rf): """Plain header value without the 'Bearer ' prefix is tolerated.""" token = ApiToken.objects.create(name="legacy-client") assert extract_token(rf.get("/", HTTP_AUTHORIZATION=token.token)) is not None @pytest.mark.django_db def test_query_string_token(rf): token = ApiToken.objects.create(name="browser") assert extract_token(rf.get("/", {"api_token": token.token})) is not None @pytest.mark.django_db def test_revoked_token_is_anonymous(rf): token = ApiToken.objects.create(name="revoked", is_active=False) assert extract_token(rf.get("/", HTTP_AUTHORIZATION=f"Bearer {token.token}")) is None @pytest.mark.django_db def test_unknown_token_is_anonymous(rf): assert extract_token(rf.get("/", HTTP_AUTHORIZATION="Bearer nope")) is None @pytest.mark.django_db def test_extract_token_updates_last_used(rf): token = ApiToken.objects.create(name="track-me") assert token.last_used_at is None extract_token(rf.get("/", HTTP_AUTHORIZATION=f"Bearer {token.token}")) token.refresh_from_db() assert token.last_used_at is not None @pytest.mark.django_db def test_only_ingest_tokens_may_push(rf): read = ApiToken.objects.create(name="reader") ingest = ApiToken.objects.create(name="station", kind=TokenKind.INGEST) assert request_can_ingest(rf.get("/", {"api_token": read.token})) is False assert request_can_ingest(rf.get("/", {"api_token": ingest.token})) is True @pytest.mark.django_db def test_session_token_is_ignored_by_the_api_path(rf): """The session carrier is for browser pages only; an API controller asking request_has_valid_token() must not be authenticated by a stale session.""" token = ApiToken.objects.create(name="browser") request = rf.get("/") request.session = {} store_session_token(request, token) assert request_has_valid_token(request) is False assert resolve_token(request) is not None