From b94f9b79571c644682f3c0b61596a86244b426ff Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Sat, 27 Jun 2026 14:37:34 +0200 Subject: [PATCH 1/3] chore: add SDK compliance harness 0.8.0 --- .github/workflows/sdk-compliance.yml | 4 ++-- posthog/request.py | 4 +++- posthog/test/test_request.py | 12 ++++++++++++ sdk_compliance_adapter/CONTRIBUTING.md | 2 +- sdk_compliance_adapter/adapter.py | 6 +++--- sdk_compliance_adapter/docker-compose.yml | 2 +- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/sdk-compliance.yml b/.github/workflows/sdk-compliance.yml index 99a214ed..22853555 100644 --- a/.github/workflows/sdk-compliance.yml +++ b/.github/workflows/sdk-compliance.yml @@ -14,8 +14,8 @@ on: jobs: compliance: name: PostHog SDK compliance tests - uses: PostHog/posthog-sdk-test-harness/.github/workflows/test-sdk-action.yml@85e2901ea3260a28e07a83086d59b4fb4dfc814f + uses: PostHog/posthog-sdk-test-harness/.github/workflows/test-sdk-action.yml@be8b8d5a3f94a249659844e94832e874f049c1e4 with: adapter-dockerfile: "sdk_compliance_adapter/Dockerfile" adapter-context: "." - test-harness-version: "main-85e2901@sha256:4c8eac34e7ff66554a2c6947788c0a42b82456bc949c03bd8f6b9a10bef23ef5" + test-harness-version: "0.8.0" diff --git a/posthog/request.py b/posthog/request.py index de16f33a..a233dede 100644 --- a/posthog/request.py +++ b/posthog/request.py @@ -227,6 +227,7 @@ def post( gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, + api_key_field: str = "api_key", **kwargs, ) -> requests.Response: """Post the `kwargs` to the API""" @@ -235,7 +236,7 @@ def post( body["sent_at"] = datetime.now(tz=timezone.utc).isoformat() trimmed_host = remove_trailing_slash(normalize_host(host)) url = trimmed_host + cast(str, path) - body["api_key"] = api_key + body[api_key_field] = api_key data: str | bytes = json.dumps(body, cls=DatetimeSerializer) log.debug("making request: %s to url: %s", data, url) headers = {"Content-Type": "application/json", "User-Agent": USER_AGENT} @@ -321,6 +322,7 @@ def flags( gzip, timeout, session=_get_flags_session(), + api_key_field="token", **kwargs, ) return _process_response( diff --git a/posthog/test/test_request.py b/posthog/test/test_request.py index 4cae68f9..ffedd8a9 100644 --- a/posthog/test/test_request.py +++ b/posthog/test/test_request.py @@ -85,6 +85,18 @@ def test_post_sends_snake_case_sent_at(key, expected_present): assert (key in data) is expected_present +def test_flags_request_uses_token_field_for_project_api_key(): + mock_response = mock.MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"featureFlags": {}} + + with mock.patch.object(request_module, "post", return_value=mock_response) as mock_post: + flags(TEST_API_KEY, host="https://test.posthog.com", distinct_id="user_1") + + mock_post.assert_called_once() + assert mock_post.call_args.kwargs["api_key_field"] == "token" + + def test_message_only_debug_logs_include_posthog_prefix(): mock_response = requests.Response() mock_response.status_code = 200 diff --git a/sdk_compliance_adapter/CONTRIBUTING.md b/sdk_compliance_adapter/CONTRIBUTING.md index f6ef9ef3..25bcb424 100644 --- a/sdk_compliance_adapter/CONTRIBUTING.md +++ b/sdk_compliance_adapter/CONTRIBUTING.md @@ -35,7 +35,7 @@ docker run -d --name sdk-adapter --network test-network -p 8080:8080 posthog-pyt docker run --rm \ --name test-harness \ --network test-network \ - ghcr.io/posthog/sdk-test-harness:latest \ + ghcr.io/posthog/sdk-test-harness:0.8.0 \ run --adapter-url http://sdk-adapter:8080 --mock-url http://test-harness:8081 # Cleanup diff --git a/sdk_compliance_adapter/adapter.py b/sdk_compliance_adapter/adapter.py index 032dea68..88dbb691 100644 --- a/sdk_compliance_adapter/adapter.py +++ b/sdk_compliance_adapter/adapter.py @@ -20,7 +20,7 @@ # Configure logging logging.basicConfig( - level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" + level=logging.WARNING, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) @@ -225,7 +225,7 @@ def init(): api_key = data.get("api_key") host = data.get("host") flush_at = data.get("flush_at", 100) - flush_interval_ms = data.get("flush_interval_ms", 5000) + flush_interval_ms = data.get("flush_interval_ms", 500) max_retries = data.get("max_retries", 3) enable_compression = data.get("enable_compression", False) @@ -245,7 +245,7 @@ def init(): flush_interval=flush_interval, gzip=enable_compression, max_retries=max_retries, - debug=True, + debug=False, # Compliance tests assert the request-level default when callers omit # disable_geoip. Configure the adapter to exercise geoip-enabled # /flags requests by default while still allowing per-call overrides. diff --git a/sdk_compliance_adapter/docker-compose.yml b/sdk_compliance_adapter/docker-compose.yml index 138bf4a8..bace8849 100644 --- a/sdk_compliance_adapter/docker-compose.yml +++ b/sdk_compliance_adapter/docker-compose.yml @@ -13,7 +13,7 @@ services: # Test harness test-harness: - image: ghcr.io/posthog/sdk-test-harness:latest + image: ghcr.io/posthog/sdk-test-harness:0.8.0 command: ["run", "--adapter-url", "http://sdk-adapter:8080", "--mock-url", "http://test-harness:8081"] networks: - test-network From 1079e8d633ef4795ba92bdf99290ef37b95e97d0 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 29 Jun 2026 13:11:50 +0200 Subject: [PATCH 2/3] fix: update request test formatting and API snapshot --- posthog/test/test_request.py | 42 ++++++++++++++++++++++++------ references/public_api_snapshot.txt | 2 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/posthog/test/test_request.py b/posthog/test/test_request.py index 51e04cf3..c03c81d0 100644 --- a/posthog/test/test_request.py +++ b/posthog/test/test_request.py @@ -86,16 +86,42 @@ def test_post_sends_snake_case_sent_at(key, expected_present): assert (key in data) is expected_present -def test_flags_request_uses_token_field_for_project_api_key(): - mock_response = mock.MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"featureFlags": {}} +@pytest.mark.parametrize( + "api_call, call_kwargs, expected_api_key_field", + [ + ("post", {"path": "/batch/", "batch": []}, "api_key"), + ("flags", {"distinct_id": "user_1"}, "token"), + ], +) +def test_project_api_key_field_name(api_call, call_kwargs, expected_api_key_field): + if api_call == "post": + mock_response = requests.Response() + mock_response.status_code = 200 + mock_session = mock.MagicMock() + mock_session.post.return_value = mock_response + + request_module.post( + TEST_API_KEY, + host="https://test.posthog.com", + session=mock_session, + **call_kwargs, + ) + + data = json.loads(mock_session.post.call_args.kwargs["data"]) + assert data[expected_api_key_field] == TEST_API_KEY + assert "token" not in data + else: + mock_response = mock.MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"featureFlags": {}} - with mock.patch.object(request_module, "post", return_value=mock_response) as mock_post: - flags(TEST_API_KEY, host="https://test.posthog.com", distinct_id="user_1") + with mock.patch.object( + request_module, "post", return_value=mock_response + ) as mock_post: + flags(TEST_API_KEY, host="https://test.posthog.com", **call_kwargs) - mock_post.assert_called_once() - assert mock_post.call_args.kwargs["api_key_field"] == "token" + mock_post.assert_called_once() + assert mock_post.call_args.kwargs["api_key_field"] == expected_api_key_field def test_message_only_debug_logs_include_posthog_prefix(): diff --git a/references/public_api_snapshot.txt b/references/public_api_snapshot.txt index 772cf18d..5c59deff 100644 --- a/references/public_api_snapshot.txt +++ b/references/public_api_snapshot.txt @@ -1047,7 +1047,7 @@ function posthog.request.flags(api_key: str, host: Optional[str] = None, gzip: b function posthog.request.get(api_key: str, url: str, host: Optional[str] = None, timeout: Optional[int] = None, etag: Optional[str] = None) -> GetResponse function posthog.request.is_ai_event(event_name) -> bool function posthog.request.normalize_host(host: Optional[str]) -> str -function posthog.request.post(api_key: str, host: Optional[str] = None, path: Optional[str] = None, gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, **kwargs) -> requests.Response +function posthog.request.post(api_key: str, host: Optional[str] = None, path: Optional[str] = None, gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, api_key_field: str = 'api_key', **kwargs) -> requests.Response function posthog.request.remote_config(personal_api_key: str, project_api_key: str, host: Optional[str] = None, key: str = '', timeout: int = 15) -> Any function posthog.request.reset_sessions() -> None function posthog.request.set_socket_options(socket_options: Optional[SocketOptions]) -> None From c53418f6d54ffa84e983ec93e29acb35f163ab81 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 29 Jun 2026 16:26:20 +0200 Subject: [PATCH 3/3] chore: keep flags api key field unchanged --- posthog/request.py | 4 +-- posthog/test/test_request.py | 49 +++++++++--------------------- references/public_api_snapshot.txt | 2 +- 3 files changed, 17 insertions(+), 38 deletions(-) diff --git a/posthog/request.py b/posthog/request.py index 1eeffc7a..714fdf85 100644 --- a/posthog/request.py +++ b/posthog/request.py @@ -228,7 +228,6 @@ def post( gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, - api_key_field: str = "api_key", **kwargs, ) -> requests.Response: """Post the `kwargs` to the API""" @@ -237,7 +236,7 @@ def post( body["sent_at"] = datetime.now(tz=timezone.utc).isoformat() trimmed_host = remove_trailing_slash(normalize_host(host)) url = trimmed_host + cast(str, path) - body[api_key_field] = api_key + body["api_key"] = api_key data: str | bytes = json.dumps(body, cls=DatetimeSerializer) log.debug("making request: %s to url: %s", data, url) headers = {"Content-Type": "application/json", "User-Agent": USER_AGENT} @@ -326,7 +325,6 @@ def flags( gzip, timeout, session=_get_flags_session(), - api_key_field="token", **kwargs, ) return _process_response( diff --git a/posthog/test/test_request.py b/posthog/test/test_request.py index c03c81d0..eb44991e 100644 --- a/posthog/test/test_request.py +++ b/posthog/test/test_request.py @@ -86,42 +86,23 @@ def test_post_sends_snake_case_sent_at(key, expected_present): assert (key in data) is expected_present -@pytest.mark.parametrize( - "api_call, call_kwargs, expected_api_key_field", - [ - ("post", {"path": "/batch/", "batch": []}, "api_key"), - ("flags", {"distinct_id": "user_1"}, "token"), - ], -) -def test_project_api_key_field_name(api_call, call_kwargs, expected_api_key_field): - if api_call == "post": - mock_response = requests.Response() - mock_response.status_code = 200 - mock_session = mock.MagicMock() - mock_session.post.return_value = mock_response - - request_module.post( - TEST_API_KEY, - host="https://test.posthog.com", - session=mock_session, - **call_kwargs, - ) - - data = json.loads(mock_session.post.call_args.kwargs["data"]) - assert data[expected_api_key_field] == TEST_API_KEY - assert "token" not in data - else: - mock_response = mock.MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"featureFlags": {}} +def test_post_sends_project_api_key_field(): + mock_response = requests.Response() + mock_response.status_code = 200 + mock_session = mock.MagicMock() + mock_session.post.return_value = mock_response - with mock.patch.object( - request_module, "post", return_value=mock_response - ) as mock_post: - flags(TEST_API_KEY, host="https://test.posthog.com", **call_kwargs) + request_module.post( + TEST_API_KEY, + host="https://test.posthog.com", + path="/batch/", + session=mock_session, + batch=[], + ) - mock_post.assert_called_once() - assert mock_post.call_args.kwargs["api_key_field"] == expected_api_key_field + data = json.loads(mock_session.post.call_args.kwargs["data"]) + assert data["api_key"] == TEST_API_KEY + assert "token" not in data def test_message_only_debug_logs_include_posthog_prefix(): diff --git a/references/public_api_snapshot.txt b/references/public_api_snapshot.txt index 5c59deff..772cf18d 100644 --- a/references/public_api_snapshot.txt +++ b/references/public_api_snapshot.txt @@ -1047,7 +1047,7 @@ function posthog.request.flags(api_key: str, host: Optional[str] = None, gzip: b function posthog.request.get(api_key: str, url: str, host: Optional[str] = None, timeout: Optional[int] = None, etag: Optional[str] = None) -> GetResponse function posthog.request.is_ai_event(event_name) -> bool function posthog.request.normalize_host(host: Optional[str]) -> str -function posthog.request.post(api_key: str, host: Optional[str] = None, path: Optional[str] = None, gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, api_key_field: str = 'api_key', **kwargs) -> requests.Response +function posthog.request.post(api_key: str, host: Optional[str] = None, path: Optional[str] = None, gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, **kwargs) -> requests.Response function posthog.request.remote_config(personal_api_key: str, project_api_key: str, host: Optional[str] = None, key: str = '', timeout: int = 15) -> Any function posthog.request.reset_sessions() -> None function posthog.request.set_socket_options(socket_options: Optional[SocketOptions]) -> None