Skip to content

Commit b82a615

Browse files
romanlutzCopilot
andauthored
TEST: unify Entra and API-key integration tests (#2263)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 45a85ef3-3476-4c7f-bc9e-c1b8e1a20a45
1 parent c8a696e commit b82a615

8 files changed

Lines changed: 542 additions & 662 deletions

doc/contributing/10_release_process.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Before starting the release process, verify the codebase is in a healthy state.
1414
- **Check for pending changes.** Ask other PyRIT maintainers whether they have any in-flight changes that should land before the release.
1515
- **Verify build pipelines.** Confirm that all integration tests and end-to-end tests are passing in the CI pipelines. If any tests are failing, fix them before proceeding.
1616
- **Partner integration tests.** Ensure the partner integration tests are also passing. These tests validate that we are not breaking contracts with partner teams (e.g., Foundry). If any are failing, coordinate with the affected partner teams before proceeding with the release.
17-
- **Azure key-based auth is disabled in our tenant.** Our Azure subscription has API-key (local) auth turned off, so Azure targets authenticate with Microsoft Entra ID (Entra auth) only. Integration tests and notebooks that exercise Azure targets with API keys are deliberately skipped; otherwise they fail with HTTP 403 `AuthenticationTypeDisabled` ("Key based authentication is disabled for this resource"). The same endpoints are covered by the Entra-auth tests in `tests/integration/targets/test_entra_auth_targets.py`, so this is expected and not a coverage gap. Do not re-enable these for our tenant. When validating a release manually, authenticate Azure targets with Entra (`az login`) rather than API keys.
17+
- **Azure key-based auth is disabled in our tenant.** Our Azure subscription has API-key (local) auth turned off, so Azure target integration tests authenticate with Microsoft Entra ID. Tests that run notebooks requiring Azure API keys are deliberately skipped; otherwise they fail with HTTP 403 `AuthenticationTypeDisabled` ("Key based authentication is disabled for this resource"). Do not re-enable key auth for our tenant. When validating a release manually, authenticate Azure targets with Entra (`az login`) rather than API keys.
1818
- **Update scorer metrics.** Run `python .\build_scripts\evaluate_scorers.py` and commit the results so that scorer evaluation metrics are up to date.
1919

2020
## 2. Decide the Next Version

tests/integration/embeddings/test_openai_embedding.py

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,42 @@
88
from pyrit.auth import get_azure_openai_auth
99
from pyrit.embedding import OpenAITextEmbedding
1010

11-
_AZURE_KEY_AUTH_DISABLED_REASON = (
12-
"Azure key-based (local) auth is disabled in our tenant; "
13-
"covered by the Entra-auth tests (test_entra_auth_targets.py)."
14-
)
11+
_AZURE_KEY_AUTH_DISABLED_REASON = "Azure key-based (local) auth is disabled in our tenant."
1512

1613

17-
@pytest.mark.run_only_if_all_tests
1814
@pytest.mark.parametrize(
19-
"endpoint_env,key_env,model_env",
15+
("endpoint_env", "api_key_env", "model_env"),
2016
[
17+
pytest.param(
18+
"OPENAI_EMBEDDING_ENDPOINT",
19+
None,
20+
"OPENAI_EMBEDDING_MODEL",
21+
id="entra",
22+
),
2123
pytest.param(
2224
"OPENAI_EMBEDDING_ENDPOINT",
2325
"OPENAI_EMBEDDING_KEY",
2426
"OPENAI_EMBEDDING_MODEL",
2527
marks=pytest.mark.skip(reason=_AZURE_KEY_AUTH_DISABLED_REASON),
28+
id="azure-api-key",
29+
),
30+
pytest.param(
31+
"PLATFORM_OPENAI_EMBEDDING_ENDPOINT",
32+
"PLATFORM_OPENAI_EMBEDDING_KEY",
33+
"PLATFORM_OPENAI_EMBEDDING_MODEL",
34+
marks=pytest.mark.run_only_if_all_tests,
35+
id="api-key",
2636
),
27-
("PLATFORM_OPENAI_EMBEDDING_ENDPOINT", "PLATFORM_OPENAI_EMBEDDING_KEY", "PLATFORM_OPENAI_EMBEDDING_MODEL"),
2837
],
2938
)
30-
def test_openai_embedding_with_api_key(endpoint_env: str, key_env: str, model_env: str):
31-
"""Test OpenAI embedding with API key authentication."""
32-
api_key = os.environ[key_env]
39+
def test_openai_embedding(
40+
endpoint_env: str,
41+
api_key_env: str | None,
42+
model_env: str,
43+
) -> None:
3344
endpoint = os.environ[endpoint_env]
3445
model = os.environ[model_env]
46+
api_key = os.environ[api_key_env] if api_key_env else get_azure_openai_auth(endpoint)
3547

3648
embedding = OpenAITextEmbedding(
3749
api_key=api_key,
@@ -46,26 +58,3 @@ def test_openai_embedding_with_api_key(endpoint_env: str, key_env: str, model_en
4658
assert len(response.data) == 1
4759
assert len(response.data[0].embedding) > 0
4860
assert response.usage.total_tokens > 0
49-
50-
51-
def test_azure_openai_embedding_with_entra_auth():
52-
"""Test Azure OpenAI embedding with Entra (token provider) authentication."""
53-
endpoint = os.environ["OPENAI_EMBEDDING_ENDPOINT"]
54-
model = os.environ["OPENAI_EMBEDDING_MODEL"]
55-
56-
# Get token provider for Entra auth
57-
token_provider = get_azure_openai_auth(endpoint)
58-
59-
embedding = OpenAITextEmbedding(
60-
api_key=token_provider,
61-
endpoint=endpoint,
62-
model_name=model,
63-
)
64-
65-
test_text = "Testing embedding with Entra authentication."
66-
response = embedding.generate_text_embedding(text=test_text)
67-
68-
assert response is not None
69-
assert len(response.data) == 1
70-
assert len(response.data[0].embedding) > 0
71-
assert response.usage.total_tokens > 0

tests/integration/score/test_azure_content_filter_integration.py

Lines changed: 34 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,50 @@
1212
from pyrit.memory import CentralMemory, MemoryInterface
1313
from pyrit.score import AzureContentFilterScorer
1414

15-
_AZURE_KEY_AUTH_DISABLED_REASON = (
16-
"Azure key-based (local) auth is disabled in our tenant; "
17-
"covered by the Entra-auth tests (test_entra_auth_targets.py)."
18-
)
15+
_AZURE_KEY_AUTH_DISABLED_REASON = "Azure key-based (local) auth is disabled in our tenant."
1916

2017

2118
@pytest.fixture
2219
def memory() -> Generator[MemoryInterface, None, None]:
2320
yield from get_memory_interface()
2421

2522

26-
async def test_azure_content_filter_scorer_image_integration(memory) -> None:
23+
@pytest.fixture(
24+
params=[
25+
pytest.param(None, id="entra"),
26+
pytest.param(
27+
"AZURE_CONTENT_SAFETY_API_KEY",
28+
marks=[
29+
pytest.mark.run_only_if_all_tests,
30+
pytest.mark.skip(reason=_AZURE_KEY_AUTH_DISABLED_REASON),
31+
],
32+
id="api-key",
33+
),
34+
]
35+
)
36+
def content_filter_scorer(request: pytest.FixtureRequest) -> AzureContentFilterScorer:
37+
api_key_env: str | None = request.param
38+
api_key = os.getenv(api_key_env) if api_key_env else None
39+
if api_key_env:
40+
assert api_key, f"{api_key_env} must be set to test API key auth"
41+
42+
with patch.dict(os.environ, {"AZURE_CONTENT_SAFETY_API_KEY": ""}):
43+
return AzureContentFilterScorer(api_key=api_key)
44+
45+
46+
async def test_azure_content_filter_scorer_image_integration(
47+
memory: MemoryInterface,
48+
content_filter_scorer: AzureContentFilterScorer,
49+
) -> None:
2750
"""
2851
Integration test for Azure Content Filter Scorer with image input.
2952
3053
This test requires AZURE_CONTENT_SAFETY_API_ENDPOINT to be set.
31-
Authentication uses Entra ID by default (via `az login`). Alternatively,
32-
set AZURE_CONTENT_SAFETY_API_KEY for API key auth.
3354
Uses a sample image from the assets folder.
3455
"""
3556
with patch.object(CentralMemory, "get_memory_instance", return_value=memory):
36-
scorer = AzureContentFilterScorer()
37-
3857
image_path = HOME_PATH / "assets" / "architecture_components.png"
39-
scores = await scorer.score_image_async(image_path=str(image_path))
58+
scores = await content_filter_scorer.score_image_async(image_path=str(image_path))
4059

4160
# Should return scores for all harm categories
4261
assert len(scores) > 0, "Expected scores for harm categories"
@@ -53,21 +72,22 @@ async def test_azure_content_filter_scorer_image_integration(memory) -> None:
5372
assert max_score < 0.5, "Architecture diagram should have low harm scores"
5473

5574

56-
async def test_azure_content_filter_scorer_long_text_chunking_integration(memory) -> None:
75+
async def test_azure_content_filter_scorer_long_text_chunking_integration(
76+
memory: MemoryInterface,
77+
content_filter_scorer: AzureContentFilterScorer,
78+
) -> None:
5779
"""
5880
Integration test for Azure Content Filter Scorer with text longer than 10,000 characters.
5981
6082
This verifies that the chunking and aggregation logic works correctly with the real API.
6183
"""
6284
with patch.object(CentralMemory, "get_memory_instance", return_value=memory):
63-
scorer = AzureContentFilterScorer()
64-
6585
# This should be greater than the rate limit
6686
base_text = "I hate you " * 10000
6787

6888
assert len(base_text) > AzureContentFilterScorer.MAX_TEXT_LENGTH
6989

70-
scores = await scorer.score_text_async(text=base_text)
90+
scores = await content_filter_scorer.score_text_async(text=base_text)
7191

7292
# Should return aggregated scores (one per category)
7393
assert len(scores) > 0, "Expected aggregated scores for harm categories"
@@ -82,64 +102,3 @@ async def test_azure_content_filter_scorer_long_text_chunking_integration(memory
82102
# Long benign text should still have low scores
83103
max_score = max(float(s.score_value) for s in scores)
84104
assert max_score > 0, "text should have > 0 score"
85-
86-
87-
@pytest.mark.run_only_if_all_tests
88-
@pytest.mark.skip(reason=_AZURE_KEY_AUTH_DISABLED_REASON)
89-
async def test_azure_content_filter_scorer_image_with_api_key(memory) -> None:
90-
"""
91-
Integration test for Azure Content Filter Scorer image scoring with explicit API key auth.
92-
93-
This test requires AZURE_CONTENT_SAFETY_API_KEY to be set.
94-
"""
95-
api_key = os.getenv("AZURE_CONTENT_SAFETY_API_KEY")
96-
assert api_key, "AZURE_CONTENT_SAFETY_API_KEY must be set to test API key auth"
97-
98-
with patch.object(CentralMemory, "get_memory_instance", return_value=memory):
99-
scorer = AzureContentFilterScorer(api_key=api_key)
100-
101-
image_path = HOME_PATH / "assets" / "architecture_components.png"
102-
scores = await scorer.score_image_async(image_path=str(image_path))
103-
104-
assert len(scores) > 0, "Expected scores for harm categories"
105-
106-
for score in scores:
107-
assert score.score_type == "float_scale"
108-
assert score.score_category is not None
109-
assert 0.0 <= float(score.score_value) <= 1.0
110-
assert "azure_severity" in score.score_metadata
111-
112-
max_score = max(float(s.score_value) for s in scores)
113-
assert max_score < 0.5, "Architecture diagram should have low harm scores"
114-
115-
116-
@pytest.mark.run_only_if_all_tests
117-
@pytest.mark.skip(reason=_AZURE_KEY_AUTH_DISABLED_REASON)
118-
async def test_azure_content_filter_scorer_text_with_api_key(memory) -> None:
119-
"""
120-
Integration test for Azure Content Filter Scorer text scoring with explicit API key auth.
121-
122-
This test requires AZURE_CONTENT_SAFETY_API_KEY to be set.
123-
"""
124-
api_key = os.getenv("AZURE_CONTENT_SAFETY_API_KEY")
125-
assert api_key, "AZURE_CONTENT_SAFETY_API_KEY must be set to test API key auth"
126-
127-
with patch.object(CentralMemory, "get_memory_instance", return_value=memory):
128-
scorer = AzureContentFilterScorer(api_key=api_key)
129-
130-
base_text = "I hate you " * 10000
131-
132-
assert len(base_text) > AzureContentFilterScorer.MAX_TEXT_LENGTH
133-
134-
scores = await scorer.score_text_async(text=base_text)
135-
136-
assert len(scores) > 0, "Expected aggregated scores for harm categories"
137-
138-
for score in scores:
139-
assert score.score_type == "float_scale"
140-
assert score.score_category is not None
141-
assert 0.0 <= float(score.score_value) <= 1.0
142-
assert "azure_severity" in score.score_metadata
143-
144-
max_score = max(float(s.score_value) for s in scores)
145-
assert max_score > 0, "text should have > 0 score"

0 commit comments

Comments
 (0)