Skip to content

Fix auth bugs, improve error handling, and add integration tests#10

Merged
shenoyvvarun merged 4 commits into
oracle-samples:mainfrom
fede-kamel:fix/auth-improvements
May 4, 2026
Merged

Fix auth bugs, improve error handling, and add integration tests#10
shenoyvvarun merged 4 commits into
oracle-samples:mainfrom
fede-kamel:fix/auth-improvements

Conversation

@fede-kamel
Copy link
Copy Markdown
Contributor

@fede-kamel fede-kamel commented Apr 12, 2026

Summary

  • Fix 401 retry silently dropping responses — when token refresh fails during a 401 retry, the generator previously ended without yielding, causing the caller to receive no response at all. Now the generator ends gracefully and the original 401 is returned to the caller.
  • Fix incorrect return type on OciSessionAuth._load_private_key (strAny). The OCI SDK returns a cryptography key object, not a string.
  • Add OciAuthRefreshError exception with cause tracking and elapsed time since last successful refresh.
  • Track refresh failures via _last_refresh_error attribute for callers to inspect auth health.
  • Reduce logging noise — init and routine refresh checks moved from INFO to DEBUG. Scheduled refresh failures log at WARNING. 401 retry failures log at ERROR with actionable context.
  • Expand unit tests from 9 to 20 covering signing, refresh, error handling, and all auth subclasses.
  • Add integration test infrastructureconftest.py with env-based config, tests/.env.example template, and requires_oci skip marker.
  • Add 4 live integration tests — sync/async Responses API, raw httpx signing, and header stripping verification.

Test plan

  • All 20 unit tests pass (pytest tests/test_auth.py)
  • All 4 integration tests pass against live OCI endpoint (pytest tests/integration/test_auth_live.py)
  • Integration tests skip cleanly without OCI_GENAI_* env vars
  • No changes to existing public API — only additions (OciAuthRefreshError)

How to validate

# Unit tests (no credentials needed)
pytest tests/test_auth.py -v

# Integration tests (copy tests/.env.example to tests/.env, fill in values)
pytest tests/integration/test_auth_live.py -v

Fixes #12

Bug fixes:
- Fix 401 retry silently dropping responses when refresh fails. The
  generator now ends gracefully and the caller receives the original 401
  instead of no response at all.
- Fix incorrect return type on OciSessionAuth._load_private_key (str ->
  Any). The OCI SDK returns a cryptography key object, not a string.

Improvements:
- Add OciAuthRefreshError exception with cause tracking and elapsed time
  since last successful refresh, for better diagnostics.
- Track refresh failures via _last_refresh_error attribute (cleared on
  success, set on failure) so callers can inspect auth health.
- Reduce logging noise: init and routine refresh checks moved from INFO
  to DEBUG. Only actual refresh completions stay at INFO. Scheduled
  refresh failures log at WARNING (not silent exception). 401 retry
  failures log at ERROR with actionable context.

Testing:
- Expand unit tests from 9 to 20 covering: signing path with header
  stripping, POST body signing, 401 retry behavior, non-401 passthrough,
  refresh failure tracking, error clearing on success, OciAuthRefreshError
  formatting, session auth missing key_file, config reload on refresh.
- Add integration test infrastructure: conftest.py with env-based config
  loading, session-scoped fixtures, and requires_oci skip marker.
- Add tests/.env.example template for integration test configuration.
- Add 4 live integration tests: sync/async Responses API, raw httpx
  signing, and header stripping verification.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Apr 12, 2026
- Fix SIM105: replace try/except StopIteration/pass with
  contextlib.suppress(StopIteration)
- Fix B011: replace assert False with pytest.raises
- Apply isort + ruff format to all changed files
- Ensure isort → ruff format → ruff check --fix produces stable output

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
- Remove dependency on model echoing exact text -- assertions now
  verify non-empty responses and HTTP 200 status instead of specific
  content, since model output is non-deterministic.
- Expand .env.example with prerequisites: how to create a GenAI Project,
  which models are known to work/not work on /openai/v1, and auth setup
  instructions.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel
Copy link
Copy Markdown
Contributor Author

@shenoyvvarun Would appreciate your review on this. Fixes a bug where the 401 retry path silently drops responses, improves logging levels, adds OciAuthRefreshError for refresh diagnostics, and expands test coverage from 9 to 20 unit tests plus 4 integration tests.

@fede-kamel
Copy link
Copy Markdown
Contributor Author

Friendly ping — this PR has been open for ~11 days without a review. Happy to address feedback or rebase further if needed. Let me know if there's anything I can clarify. Thanks!

Comment thread src/oci_genai_auth/auth.py Outdated
OciAuthSigner: TypeAlias = oci.signer.AbstractBaseSigner


class OciAuthRefreshError(Exception):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this.

Copy link
Copy Markdown
Contributor Author

@fede-kamel fede-kamel Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed the class, its export from __init__.py, and the two tests that constructed it.

You're right that it's dead code: nothing in the production path actually raises it. The two failure sites both swallow + log:

  • scheduled refresh in _refresh_if_needed stores the raw exc on self._last_refresh_error and keeps serving the previous signer (graceful degradation)
  • 401-retry refresh logs and lets the original 401 bubble up to the caller

So the class was unreachable. I'd originally added it thinking we might want a structured error (with last_success elapsed time) for observability/metrics, but since we never raise it, it just bloats the public API. Dropping it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it still exists... Maybe missed commit push?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — sorry, the deletion commit landed on my framework-helpers branch by mistake. Just cherry-picked it onto fix/auth-improvements (a3523ce) and pushed. Should be gone now.

@fede-kamel
Copy link
Copy Markdown
Contributor Author

Integration test results (post-fix)

Ran the full integration suite (tests/integration/) against live OCI GenAI in us-chicago-1 after removing OciAuthRefreshError:

tests/integration/test_auth_live.py::TestOciAuthSigningLive::test_sync_responses_api PASSED
tests/integration/test_auth_live.py::TestOciAuthSigningLive::test_async_responses_api PASSED
tests/integration/test_auth_live.py::TestOciAuthSigningLive::test_raw_httpx_request PASSED
tests/integration/test_auth_live.py::TestOciAuthSigningLive::test_auth_headers_stripped PASSED
tests/integration/test_langchain.py::TestLangChain::test_build_langchain_chat PASSED
tests/integration/test_openai_sdk.py::TestOpenAISDK::test_build_openai_client PASSED
tests/integration/test_openai_sdk.py::TestOpenAISDK::test_build_openai_async_client PASSED
tests/integration/test_openai_sdk.py::TestOpenAIAgentsSDK::test_configure_openai_agents PASSED
tests/integration/test_pydantic_ai.py::TestPydanticAI::test_build_pydantic_ai_model PASSED

9 passed in 11.14s

Coverage:

  • Sync + async OpenAI /responses calls signed by OciSessionAuth and OciUserPrincipalAuth (both auth types verified end-to-end against live OCI)
  • Raw httpx POST through HttpxOciAuth.auth_flow returns 200 from the GenAI inference endpoint
  • SDK-injected Authorization / X-Api-Key headers correctly stripped before OCI signing
  • LangChain ChatOpenAI, OpenAI Agents SDK, and Pydantic-AI all complete real model calls through the framework helpers

Plus 33/33 unit tests pass and ruff/isort are clean.

@fede-kamel fede-kamel requested a review from shenoyvvarun April 26, 2026 07:04
The class was never raised — both refresh failure paths swallow the raw
exception and store it on _last_refresh_error. Drop the class, its
public re-export, and the two tests that constructed it directly.
Copy link
Copy Markdown
Contributor

@shenoyvvarun shenoyvvarun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for making the change.

@shenoyvvarun shenoyvvarun merged commit 4b9bfb3 into oracle-samples:main May 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

401 retry silently drops response when token refresh fails

2 participants