Skip to content

Commit a86fa7e

Browse files
authored
test: make tests successfully run from forks (#2203)
* use monkeypatch in Cohere unit tests * fix Langfuse * nvidia * fix * skip AWS authentication when PRs are from dependabot
1 parent 79231df commit a86fa7e

File tree

6 files changed

+89
-71
lines changed

6 files changed

+89
-71
lines changed

.github/workflows/amazon_bedrock.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ jobs:
6565
- name: Run unit tests
6666
run: hatch run test:unit
6767

68-
# Do not authenticate on pull requests from forks
68+
# Do not authenticate on PRs from forks and on PRs created by dependabot
6969
- name: AWS authentication
7070
id: aws-auth
71-
if: github.event.pull_request.head.repo.full_name == github.repository
71+
if: github.event.pull_request.head.repo.full_name == github.repository && !startsWith(github.event.pull_request.head.ref, 'dependabot/')
7272
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df
7373
with:
7474
aws-region: ${{ env.AWS_REGION }}

integrations/cohere/tests/test_document_embedder.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616

1717
class TestCohereDocumentEmbedder:
18-
def test_init_default(self):
18+
def test_init_default(self, monkeypatch):
19+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
1920
embedder = CohereDocumentEmbedder()
2021
assert embedder.api_key == Secret.from_env_var(["COHERE_API_KEY", "CO_API_KEY"])
2122
assert embedder.model == "embed-english-v2.0"
@@ -54,7 +55,8 @@ def test_init_with_parameters(self):
5455
assert embedder.embedding_separator == "-"
5556
assert embedder.embedding_type == EmbeddingTypes.FLOAT
5657

57-
def test_to_dict(self):
58+
def test_to_dict(self, monkeypatch):
59+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
5860
embedder_component = CohereDocumentEmbedder()
5961
component_dict = embedder_component.to_dict()
6062
assert component_dict == {
@@ -74,7 +76,8 @@ def test_to_dict(self):
7476
},
7577
}
7678

77-
def test_to_dict_with_custom_init_parameters(self):
79+
def test_to_dict_with_custom_init_parameters(self, monkeypatch):
80+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
7881
embedder_component = CohereDocumentEmbedder(
7982
api_key=Secret.from_env_var("ENV_VAR", strict=False),
8083
model="embed-multilingual-v2.0",
@@ -106,7 +109,8 @@ def test_to_dict_with_custom_init_parameters(self):
106109
},
107110
}
108111

109-
def test_from_dict(self):
112+
def test_from_dict(self, monkeypatch):
113+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
110114
component_dict = {
111115
"type": "haystack_integrations.components.embedders.cohere.document_embedder.CohereDocumentEmbedder",
112116
"init_parameters": {

integrations/cohere/tests/test_text_embedder.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313

1414

1515
class TestCohereTextEmbedder:
16-
def test_init_default(self):
16+
def test_init_default(self, monkeypatch):
1717
"""
1818
Test default initialization parameters for CohereTextEmbedder.
1919
"""
20+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
2021
embedder = CohereTextEmbedder()
2122

2223
assert embedder.api_key == Secret.from_env_var(["COHERE_API_KEY", "CO_API_KEY"])
@@ -46,10 +47,11 @@ def test_init_with_parameters(self):
4647
assert embedder.timeout == 60
4748
assert embedder.embedding_type == EmbeddingTypes.FLOAT
4849

49-
def test_to_dict(self):
50+
def test_to_dict(self, monkeypatch):
5051
"""
5152
Test serialization of this component to a dictionary, using default initialization parameters.
5253
"""
54+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
5355
embedder_component = CohereTextEmbedder()
5456
component_dict = embedder_component.to_dict()
5557
assert component_dict == {
@@ -65,10 +67,11 @@ def test_to_dict(self):
6567
},
6668
}
6769

68-
def test_to_dict_with_custom_init_parameters(self):
70+
def test_to_dict_with_custom_init_parameters(self, monkeypatch):
6971
"""
7072
Test serialization of this component to a dictionary, using custom initialization parameters.
7173
"""
74+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
7275
embedder_component = CohereTextEmbedder(
7376
api_key=Secret.from_env_var("ENV_VAR", strict=False),
7477
model="embed-multilingual-v2.0",
@@ -92,7 +95,8 @@ def test_to_dict_with_custom_init_parameters(self):
9295
},
9396
}
9497

95-
def test_from_dict(self):
98+
def test_from_dict(self, monkeypatch):
99+
monkeypatch.setenv("COHERE_API_KEY", "test-api-key")
96100
component_dict = {
97101
"type": "haystack_integrations.components.embedders.cohere.text_embedder.CohereTextEmbedder",
98102
"init_parameters": {

integrations/langfuse/tests/test_tracer.py

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
import datetime
6-
import json
76
import logging
87
import sys
98
from typing import Optional
109
from unittest.mock import MagicMock, Mock, patch
1110

1211
import pytest
13-
from haystack import Pipeline, component
1412
from haystack.dataclasses import ChatMessage, ToolCall
1513

16-
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
1714
from haystack_integrations.tracing.langfuse.tracer import (
1815
_COMPONENT_OUTPUT_KEY, DefaultSpanHandler, LangfuseSpan, LangfuseTracer,
1916
SpanContext)
@@ -403,58 +400,3 @@ def test_init_with_tracing_disabled(self, monkeypatch, caplog):
403400

404401
LangfuseTracer(tracer=MockTracer(), name="Haystack", public=False)
405402
assert "tracing is disabled" in caplog.text
406-
407-
def test_context_cleanup_after_nested_failures(self):
408-
"""
409-
Test that tracer context is properly cleaned up even when nested operations fail.
410-
411-
This test addresses a critical bug where failing nested operations (like inner pipelines)
412-
could corrupt the tracing context, leaving stale spans that affect subsequent operations.
413-
The fix ensures proper cleanup through try/finally blocks.
414-
415-
Before the fix: context would retain spans after failures (length > 0)
416-
After the fix: context is always cleaned up (length == 0)
417-
"""
418-
419-
420-
@component
421-
class FailingParser:
422-
@component.output_types(result=str)
423-
def run(self, data: str):
424-
# This will fail with ValueError when data is not valid JSON
425-
parsed = json.loads(data)
426-
return {"result": parsed["key"]}
427-
428-
@component
429-
class ComponentWithNestedPipeline:
430-
def __init__(self):
431-
# This simulates IntentClassifier's internal pipeline
432-
self.internal_pipeline = Pipeline()
433-
self.internal_pipeline.add_component("parser", FailingParser())
434-
435-
@component.output_types(result=str)
436-
def run(self, input_data: str):
437-
# Run nested pipeline - this is where corruption occurs
438-
result = self.internal_pipeline.run({"parser": {"data": input_data}})
439-
return {"result": result["parser"]["result"]}
440-
441-
tracer = LangfuseConnector("test")
442-
443-
main_pipeline = Pipeline()
444-
main_pipeline.add_component("nested_component", ComponentWithNestedPipeline())
445-
main_pipeline.add_component("tracer", tracer)
446-
447-
# Test 1: First run will fail and should clean up context
448-
try:
449-
main_pipeline.run({"nested_component": {"input_data": "invalid json"}})
450-
except Exception:
451-
pass # Expected to fail
452-
453-
# Critical assertion: context should be empty after failed operation
454-
assert len(tracer.tracer._context) == 0
455-
456-
# Test 2: Second run should work normally with clean context
457-
main_pipeline.run({"nested_component": {"input_data": '{"key": "valid"}'}})
458-
459-
# Critical assertion: context should be empty after successful operation
460-
assert len(tracer.tracer._context) == 0

integrations/langfuse/tests/test_tracing.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import time
77
from typing import Any, Dict, List
88
from urllib.parse import urlparse
9+
import json
910

1011
import pytest
1112
import requests
@@ -189,3 +190,67 @@ def run(self, messages: List[ChatMessage]) -> Dict[str, Any]:
189190
component_names = [key for obs in haystack_pipeline_run_observations for key in obs["input"].keys()]
190191
assert "prompt_builder" in component_names
191192
assert "llm" in component_names
193+
194+
@pytest.mark.skipif(
195+
not all(
196+
[
197+
os.environ.get("LANGFUSE_SECRET_KEY"),
198+
os.environ.get("LANGFUSE_PUBLIC_KEY"),
199+
]
200+
),
201+
reason="Missing required environment variables: LANGFUSE_SECRET_KEY and LANGFUSE_PUBLIC_KEY",
202+
)
203+
@pytest.mark.integration
204+
def test_context_cleanup_after_nested_failures():
205+
"""
206+
Test that tracer context is properly cleaned up even when nested operations fail.
207+
208+
This test addresses a critical bug where failing nested operations (like inner pipelines)
209+
could corrupt the tracing context, leaving stale spans that affect subsequent operations.
210+
The fix ensures proper cleanup through try/finally blocks.
211+
212+
Before the fix: context would retain spans after failures (length > 0)
213+
After the fix: context is always cleaned up (length == 0)
214+
"""
215+
216+
@component
217+
class FailingParser:
218+
@component.output_types(result=str)
219+
def run(self, data: str):
220+
# This will fail with ValueError when data is not valid JSON
221+
parsed = json.loads(data)
222+
return {"result": parsed["key"]}
223+
224+
@component
225+
class ComponentWithNestedPipeline:
226+
def __init__(self):
227+
# This simulates IntentClassifier's internal pipeline
228+
self.internal_pipeline = Pipeline()
229+
self.internal_pipeline.add_component("parser", FailingParser())
230+
231+
@component.output_types(result=str)
232+
def run(self, input_data: str):
233+
# Run nested pipeline - this is where corruption occurs
234+
result = self.internal_pipeline.run({"parser": {"data": input_data}})
235+
return {"result": result["parser"]["result"]}
236+
237+
tracer = LangfuseConnector("test")
238+
239+
main_pipeline = Pipeline()
240+
main_pipeline.add_component("nested_component", ComponentWithNestedPipeline())
241+
main_pipeline.add_component("tracer", tracer)
242+
243+
# Test 1: First run will fail and should clean up context
244+
try:
245+
main_pipeline.run({"nested_component": {"input_data": "invalid json"}})
246+
except Exception:
247+
pass # Expected to fail
248+
249+
# Critical assertion: context should be empty after failed operation
250+
assert len(tracer.tracer._context) == 0
251+
252+
# Test 2: Second run should work normally with clean context
253+
main_pipeline.run({"nested_component": {"input_data": '{"key": "valid"}'}})
254+
255+
# Critical assertion: context should be empty after successful operation
256+
assert len(tracer.tracer._context) == 0

integrations/nvidia/tests/test_nim_backend.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,13 @@ def test_init_default(self, monkeypatch):
8686
assert backend.session.headers["authorization"] == "Bearer fake-api-key"
8787
assert backend.timeout == REQUEST_TIMEOUT
8888

89-
def test_init_with_client_enum(self):
89+
def test_init_with_client_enum(self, monkeypatch):
90+
monkeypatch.setenv("NVIDIA_API_KEY", "fake-api-key")
9091
backend = NimBackend(model="custom-model", api_url="http://localhost:8000", client=Client.NVIDIA_TEXT_EMBEDDER)
9192
assert backend.client == Client.NVIDIA_TEXT_EMBEDDER
9293

93-
def test_init_without_client(self):
94+
def test_init_without_client(self, monkeypatch):
95+
monkeypatch.setenv("NVIDIA_API_KEY", "fake-api-key")
9496
backend = NimBackend(model="custom-model", api_url="http://localhost:8000")
9597
assert backend.client is None
9698
assert backend.model_type is None
@@ -145,7 +147,8 @@ def test_init_with_incompatible_client_raises_error(self, monkeypatch):
145147
client="NvidiaGenerator", # chat client
146148
)
147149

148-
def test_init_with_non_hosted_model(self):
150+
def test_init_with_non_hosted_model(self, monkeypatch):
151+
monkeypatch.setenv("NVIDIA_API_KEY", "fake-api-key")
149152
backend = NimBackend(model="unknown-model", api_url="http://localhost:8000", client="NvidiaTextEmbedder")
150153

151154
# validation is skipped for non-hosted models

0 commit comments

Comments
 (0)