vectorwave 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vectorwave-0.1.4/src/vectorwave.egg-info → vectorwave-0.1.6}/PKG-INFO +30 -2
- {vectorwave-0.1.4 → vectorwave-0.1.6}/Readme.md +28 -1
- {vectorwave-0.1.4 → vectorwave-0.1.6}/pyproject.toml +3 -2
- vectorwave-0.1.6/src/tests/conftest.py +27 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/database/test_db.py +3 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/models/test_db_config.py +36 -1
- vectorwave-0.1.6/src/tests/monitoring/alert/test_alerter.py +70 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/monitoring/test_async_trace.py +24 -1
- vectorwave-0.1.6/src/tests/monitoring/test_tracer.py +353 -0
- vectorwave-0.1.6/src/tests/search/test_execution_search.py +125 -0
- vectorwave-0.1.6/src/tests/utils/test_function_cahe.py +126 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/__init__.py +2 -1
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/core/decorator.py +41 -22
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/database/db.py +15 -6
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/database/db_search.py +100 -6
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/models/db_config.py +20 -1
- vectorwave-0.1.6/src/vectorwave/monitoring/alert/base.py +8 -0
- vectorwave-0.1.6/src/vectorwave/monitoring/alert/factory.py +19 -0
- vectorwave-0.1.6/src/vectorwave/monitoring/alert/null_alerter.py +7 -0
- vectorwave-0.1.6/src/vectorwave/monitoring/alert/webhook_alerter.py +69 -0
- vectorwave-0.1.6/src/vectorwave/monitoring/monitoring.py +0 -0
- vectorwave-0.1.6/src/vectorwave/monitoring/tracer.py +395 -0
- vectorwave-0.1.6/src/vectorwave/prediction/__init__.py +0 -0
- vectorwave-0.1.6/src/vectorwave/prediction/predictor.py +0 -0
- vectorwave-0.1.6/src/vectorwave/search/__init__.py +0 -0
- vectorwave-0.1.6/src/vectorwave/search/execution_search.py +129 -0
- vectorwave-0.1.6/src/vectorwave/search/extended_search.py +0 -0
- vectorwave-0.1.6/src/vectorwave/search/rag_search.py +0 -0
- vectorwave-0.1.6/src/vectorwave/utils/__init__.py +0 -0
- vectorwave-0.1.6/src/vectorwave/utils/function_cache.py +73 -0
- vectorwave-0.1.6/src/vectorwave/vectorizer/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6/src/vectorwave.egg-info}/PKG-INFO +30 -2
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave.egg-info/SOURCES.txt +18 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave.egg-info/requires.txt +1 -0
- vectorwave-0.1.4/src/tests/monitoring/test_tracer.py +0 -256
- vectorwave-0.1.4/src/vectorwave/monitoring/tracer.py +0 -246
- {vectorwave-0.1.4 → vectorwave-0.1.6}/LICENSE +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/MANIFEST.in +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/NOTICE +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/setup.cfg +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/batch/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/batch/test_batch.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/core/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/core/test_decorator.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/database/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/database/test_db_search.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/exception/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/models/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/tests/monitoring/__init__.py +0 -0
- {vectorwave-0.1.4/src/tests/prediction → vectorwave-0.1.6/src/tests/monitoring/alert}/__init__.py +0 -0
- {vectorwave-0.1.4/src/tests/vectorizer → vectorwave-0.1.6/src/tests/prediction}/__init__.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/batch → vectorwave-0.1.6/src/tests/search}/__init__.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/core → vectorwave-0.1.6/src/tests/utils}/__init__.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/database → vectorwave-0.1.6/src/tests/vectorizer}/__init__.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/exception → vectorwave-0.1.6/src/vectorwave/batch}/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/batch/batch.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/models → vectorwave-0.1.6/src/vectorwave/core}/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/core/core.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/monitoring → vectorwave-0.1.6/src/vectorwave/database}/__init__.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/prediction → vectorwave-0.1.6/src/vectorwave/exception}/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/exception/exceptions.py +0 -0
- {vectorwave-0.1.4/src/vectorwave/vectorizer → vectorwave-0.1.6/src/vectorwave/models}/__init__.py +0 -0
- /vectorwave-0.1.4/src/vectorwave/monitoring/monitoring.py → /vectorwave-0.1.6/src/vectorwave/monitoring/__init__.py +0 -0
- /vectorwave-0.1.4/src/vectorwave/prediction/predictor.py → /vectorwave-0.1.6/src/vectorwave/monitoring/alert/__init__.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/vectorizer/base.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/vectorizer/factory.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/vectorizer/huggingface_vectorizer.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave/vectorizer/openai_vectorizer.py +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave.egg-info/dependency_links.txt +0 -0
- {vectorwave-0.1.4 → vectorwave-0.1.6}/src/vectorwave.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vectorwave
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: VectorWave: Seamless Auto-Vectorization Framework
|
|
5
5
|
Author-email: junyeonggim <junyeonggim5@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -20,6 +20,7 @@ License-File: NOTICE
|
|
|
20
20
|
Requires-Dist: weaviate-client>=4.0.0
|
|
21
21
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
22
22
|
Requires-Dist: sentence-transformers
|
|
23
|
+
Requires-Dist: requests
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
|
|
@@ -395,7 +396,6 @@ def other_function():
|
|
|
395
396
|
pass
|
|
396
397
|
```
|
|
397
398
|
|
|
398
|
-
**Tag Merging and Validation Rules**
|
|
399
399
|
|
|
400
400
|
1. **Validation (Important):** Tags (global or function-specific) will **only** be saved to Weaviate if their key (e.g., `run_id`, `team`, `priority`) was first defined in the `.weaviate_properties` file (Step 1). Tags not defined in the schema are **ignored**, and a warning is logged at startup.
|
|
401
401
|
|
|
@@ -408,6 +408,34 @@ def other_function():
|
|
|
408
408
|
|
|
409
409
|
-----
|
|
410
410
|
|
|
411
|
+
### 🚀 Real-time Error Alerting (Webhook)
|
|
412
|
+
|
|
413
|
+
Beyond just logging, `VectorWave` can send **real-time notifications via webhook** the instant an error occurs. This functionality is built directly into the tracer and can be activated simply by updating your `.env` file.
|
|
414
|
+
|
|
415
|
+
**How it Works:**
|
|
416
|
+
1. An exception is raised within a function decorated by `@trace_span` or `@vectorize`.
|
|
417
|
+
2. The tracer catches the exception in its `except` block and immediately calls the `alerter` object.
|
|
418
|
+
3. The alerter reads the `.env` configuration and uses the `WebhookAlerter` to dispatch the error details to your specified URL.
|
|
419
|
+
4. The notification is optimized for **Discord Embeds**, sending a rich report including the error code, trace ID, captured attributes (`user_id`, etc.), and the full stack trace.
|
|
420
|
+
|
|
421
|
+
**How to Enable:**
|
|
422
|
+
Add the following two variables to your `test_ex/.env` file (or environment variables):
|
|
423
|
+
|
|
424
|
+
```ini
|
|
425
|
+
# .env file
|
|
426
|
+
|
|
427
|
+
# 1. Set the alerter strategy to 'webhook'. (Default: "none")
|
|
428
|
+
ALERTER_STRATEGY="webhook"
|
|
429
|
+
|
|
430
|
+
# 2. Provide your webhook URL from Discord, Slack, etc.
|
|
431
|
+
ALERTER_WEBHOOK_URL="[https://discord.com/api/webhooks/YOUR_HOOK_ID/](https://discord.com/api/webhooks/YOUR_HOOK_ID/)..."
|
|
432
|
+
With just these two lines, running test_ex/example.py will now instantly send a Discord alert when the CustomValueError is raised.
|
|
433
|
+
|
|
434
|
+
Extensibility (Strategy Pattern): The alerting system is built on a Strategy Pattern. You can easily extend it by implementing the BaseAlerter interface to support other channels like email, PagerDuty, or more.
|
|
435
|
+
|
|
436
|
+
**Tag Merging and Validation Rules**
|
|
437
|
+
```
|
|
438
|
+
|
|
411
439
|
## 🤝 Contributing
|
|
412
440
|
|
|
413
441
|
Bug reports, feature requests, and code contributions are all welcome. For details, please see [CONTRIBUTING.md](https://www.google.com/search?q=httpsS://www.google.com/search%3Fq%3DCONTRIBUTING.md).
|
|
@@ -371,7 +371,6 @@ def other_function():
|
|
|
371
371
|
pass
|
|
372
372
|
```
|
|
373
373
|
|
|
374
|
-
**Tag Merging and Validation Rules**
|
|
375
374
|
|
|
376
375
|
1. **Validation (Important):** Tags (global or function-specific) will **only** be saved to Weaviate if their key (e.g., `run_id`, `team`, `priority`) was first defined in the `.weaviate_properties` file (Step 1). Tags not defined in the schema are **ignored**, and a warning is logged at startup.
|
|
377
376
|
|
|
@@ -384,6 +383,34 @@ def other_function():
|
|
|
384
383
|
|
|
385
384
|
-----
|
|
386
385
|
|
|
386
|
+
### 🚀 Real-time Error Alerting (Webhook)
|
|
387
|
+
|
|
388
|
+
Beyond just logging, `VectorWave` can send **real-time notifications via webhook** the instant an error occurs. This functionality is built directly into the tracer and can be activated simply by updating your `.env` file.
|
|
389
|
+
|
|
390
|
+
**How it Works:**
|
|
391
|
+
1. An exception is raised within a function decorated by `@trace_span` or `@vectorize`.
|
|
392
|
+
2. The tracer catches the exception in its `except` block and immediately calls the `alerter` object.
|
|
393
|
+
3. The alerter reads the `.env` configuration and uses the `WebhookAlerter` to dispatch the error details to your specified URL.
|
|
394
|
+
4. The notification is optimized for **Discord Embeds**, sending a rich report including the error code, trace ID, captured attributes (`user_id`, etc.), and the full stack trace.
|
|
395
|
+
|
|
396
|
+
**How to Enable:**
|
|
397
|
+
Add the following two variables to your `test_ex/.env` file (or environment variables):
|
|
398
|
+
|
|
399
|
+
```ini
|
|
400
|
+
# .env file
|
|
401
|
+
|
|
402
|
+
# 1. Set the alerter strategy to 'webhook'. (Default: "none")
|
|
403
|
+
ALERTER_STRATEGY="webhook"
|
|
404
|
+
|
|
405
|
+
# 2. Provide your webhook URL from Discord, Slack, etc.
|
|
406
|
+
ALERTER_WEBHOOK_URL="[https://discord.com/api/webhooks/YOUR_HOOK_ID/](https://discord.com/api/webhooks/YOUR_HOOK_ID/)..."
|
|
407
|
+
With just these two lines, running test_ex/example.py will now instantly send a Discord alert when the CustomValueError is raised.
|
|
408
|
+
|
|
409
|
+
Extensibility (Strategy Pattern): The alerting system is built on a Strategy Pattern. You can easily extend it by implementing the BaseAlerter interface to support other channels like email, PagerDuty, or more.
|
|
410
|
+
|
|
411
|
+
**Tag Merging and Validation Rules**
|
|
412
|
+
```
|
|
413
|
+
|
|
387
414
|
## 🤝 Contributing
|
|
388
415
|
|
|
389
416
|
Bug reports, feature requests, and code contributions are all welcome. For details, please see [CONTRIBUTING.md](https://www.google.com/search?q=httpsS://www.google.com/search%3Fq%3DCONTRIBUTING.md).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vectorwave"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "junyeonggim", email = "junyeonggim5@gmail.com" },
|
|
10
10
|
]
|
|
@@ -26,7 +26,8 @@ classifiers = [
|
|
|
26
26
|
dependencies = [
|
|
27
27
|
"weaviate-client>=4.0.0",
|
|
28
28
|
"pydantic-settings>=2.0.0",
|
|
29
|
-
"sentence-transformers"
|
|
29
|
+
"sentence-transformers",
|
|
30
|
+
"requests"
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
[project.urls]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
CACHE_FILE_PATH = ".vectorwave_functions_cache.json"
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _delete_cache():
|
|
10
|
+
"""Helper function to remove the cache file if it exists."""
|
|
11
|
+
if os.path.exists(CACHE_FILE_PATH):
|
|
12
|
+
try:
|
|
13
|
+
os.remove(CACHE_FILE_PATH)
|
|
14
|
+
except OSError as e:
|
|
15
|
+
print(f"\n[CacheFixture Error] Failed to remove {CACHE_FILE_PATH}: {e}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture(autouse=True, scope="function")
|
|
19
|
+
def atomic_function_cache():
|
|
20
|
+
# --- SETUP (Before Test) ---
|
|
21
|
+
_delete_cache()
|
|
22
|
+
|
|
23
|
+
# --- Run the test ---
|
|
24
|
+
yield
|
|
25
|
+
|
|
26
|
+
# --- TEARDOWN (After Test) ---
|
|
27
|
+
_delete_cache()
|
|
@@ -355,6 +355,9 @@ def test_create_execution_schema_new(mock_vectorizer_none, test_settings):
|
|
|
355
355
|
assert "status" in passed_props_map
|
|
356
356
|
assert "duration_ms" in passed_props_map
|
|
357
357
|
|
|
358
|
+
assert "return_value" in passed_props_map
|
|
359
|
+
assert passed_props_map["return_value"].dataType == wvc.DataType.TEXT
|
|
360
|
+
|
|
358
361
|
assert collection == mock_new_collection
|
|
359
362
|
|
|
360
363
|
|
|
@@ -148,4 +148,39 @@ def test_get_settings_loads_global_custom_values(mock_env_get, mock_open_file, m
|
|
|
148
148
|
assert settings.global_custom_values["run_id"] == "test-run-123"
|
|
149
149
|
|
|
150
150
|
# "EXPERIMENT_ID" should not be included as os.environ.get returned None.
|
|
151
|
-
assert "experiment_id" not in settings.global_custom_values
|
|
151
|
+
assert "experiment_id" not in settings.global_custom_values
|
|
152
|
+
|
|
153
|
+
@patch('os.path.exists', return_value=False)
|
|
154
|
+
def test_get_settings_parses_sensitive_keys(mock_exists, monkeypatch):
|
|
155
|
+
"""
|
|
156
|
+
Tests whether the SENSITIVE_FIELD_NAMES environment variable is correctly parsed
|
|
157
|
+
and converted into a lowercase set (sensitive_keys).
|
|
158
|
+
"""
|
|
159
|
+
monkeypatch.setenv("SENSITIVE_FIELD_NAMES", "password, Token, api_key,, secret ")
|
|
160
|
+
|
|
161
|
+
get_weaviate_settings.cache_clear()
|
|
162
|
+
|
|
163
|
+
# 2. Act
|
|
164
|
+
settings = get_weaviate_settings()
|
|
165
|
+
|
|
166
|
+
# 3. Assert
|
|
167
|
+
assert isinstance(settings.sensitive_keys, set)
|
|
168
|
+
assert len(settings.sensitive_keys) == 4 # 이제 4개가 맞습니다.
|
|
169
|
+
assert settings.sensitive_keys == {"password", "token", "api_key", "secret"}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# [MODIFIED] @patch('os.environ.get', return_value=None) 제거, monkeypatch 인자 추가
|
|
173
|
+
@patch('os.path.exists', return_value=False)
|
|
174
|
+
def test_get_settings_sensitive_keys_default(mock_exists, monkeypatch): # <-- FIX: monkeypatch 사용
|
|
175
|
+
"""
|
|
176
|
+
Test when there is no SENSITIVE_FIELD_NAMES environment variable set default
|
|
177
|
+
"""
|
|
178
|
+
get_weaviate_settings.cache_clear()
|
|
179
|
+
monkeypatch.delenv("SENSITIVE_FIELD_NAMES", raising=False)
|
|
180
|
+
|
|
181
|
+
# 2. Act
|
|
182
|
+
settings = get_weaviate_settings()
|
|
183
|
+
|
|
184
|
+
# 3. Assert: check default for 5
|
|
185
|
+
assert settings.sensitive_keys == {"password", "api_key", "token", "secret", "auth_token"}
|
|
186
|
+
assert len(settings.sensitive_keys) == 5
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
|
|
3
|
+
from vectorwave.monitoring.alert.factory import get_alerter
|
|
4
|
+
from vectorwave.monitoring.alert.null_alerter import NullAlerter
|
|
5
|
+
from vectorwave.monitoring.alert.webhook_alerter import WebhookAlerter
|
|
6
|
+
from vectorwave.models.db_config import WeaviateSettings
|
|
7
|
+
|
|
8
|
+
@patch('vectorwave.monitoring.alert.factory.get_weaviate_settings')
|
|
9
|
+
def test_get_alerter_returns_null_alerter_by_default(mock_get_settings):
|
|
10
|
+
"""
|
|
11
|
+
Case 1: When .env settings are missing or ALERTER_STRATEGY="none"
|
|
12
|
+
- Should return NullAlerter.
|
|
13
|
+
"""
|
|
14
|
+
# 1. Arrange
|
|
15
|
+
# Clear lru_cache to bypass caching
|
|
16
|
+
get_alerter.cache_clear()
|
|
17
|
+
|
|
18
|
+
# Mock default settings (strategy="none")
|
|
19
|
+
mock_get_settings.return_value = WeaviateSettings()
|
|
20
|
+
|
|
21
|
+
# 2. Act
|
|
22
|
+
alerter = get_alerter()
|
|
23
|
+
|
|
24
|
+
# 3. Assert
|
|
25
|
+
assert isinstance(alerter, NullAlerter)
|
|
26
|
+
|
|
27
|
+
@patch('vectorwave.monitoring.alert.factory.get_weaviate_settings')
|
|
28
|
+
def test_get_alerter_returns_webhook_alerter_when_configured(mock_get_settings):
|
|
29
|
+
"""
|
|
30
|
+
Case 2: When ALERTER_STRATEGY="webhook" and URL is provided
|
|
31
|
+
- Should return WebhookAlerter.
|
|
32
|
+
"""
|
|
33
|
+
# 1. Arrange
|
|
34
|
+
get_alerter.cache_clear()
|
|
35
|
+
|
|
36
|
+
# Mock settings object with webhook strategy and URL
|
|
37
|
+
settings = WeaviateSettings(
|
|
38
|
+
ALERTER_STRATEGY="webhook",
|
|
39
|
+
ALERTER_WEBHOOK_URL="https://test.webhook.url/..."
|
|
40
|
+
)
|
|
41
|
+
mock_get_settings.return_value = settings
|
|
42
|
+
|
|
43
|
+
# 2. Act
|
|
44
|
+
alerter = get_alerter()
|
|
45
|
+
|
|
46
|
+
# 3. Assert
|
|
47
|
+
assert isinstance(alerter, WebhookAlerter)
|
|
48
|
+
assert alerter.url == "https://test.webhook.url/..."
|
|
49
|
+
|
|
50
|
+
@patch('vectorwave.monitoring.alert.factory.get_weaviate_settings')
|
|
51
|
+
def test_get_alerter_returns_null_alerter_if_webhook_url_missing(mock_get_settings):
|
|
52
|
+
"""
|
|
53
|
+
Case 3: When ALERTER_STRATEGY="webhook" but URL is missing
|
|
54
|
+
- Should log a warning and return NullAlerter.
|
|
55
|
+
"""
|
|
56
|
+
# 1. Arrange
|
|
57
|
+
get_alerter.cache_clear()
|
|
58
|
+
|
|
59
|
+
# Mock settings with missing URL
|
|
60
|
+
settings = WeaviateSettings(
|
|
61
|
+
ALERTER_STRATEGY="webhook",
|
|
62
|
+
ALERTER_WEBHOOK_URL=None # URL is missing
|
|
63
|
+
)
|
|
64
|
+
mock_get_settings.return_value = settings
|
|
65
|
+
|
|
66
|
+
# 2. Act
|
|
67
|
+
alerter = get_alerter()
|
|
68
|
+
|
|
69
|
+
# 3. Assert
|
|
70
|
+
assert isinstance(alerter, NullAlerter)
|
|
@@ -23,6 +23,7 @@ async def test_trace_root_and_span_async_success(mock_tracer_deps):
|
|
|
23
23
|
- ContextVar (trace_id) must be correctly propagated across 'await' boundaries
|
|
24
24
|
'''
|
|
25
25
|
mock_batch = mock_tracer_deps["batch"]
|
|
26
|
+
mock_alerter = mock_tracer_deps["alerter"]
|
|
26
27
|
|
|
27
28
|
@trace_span(attributes_to_capture=['x'])
|
|
28
29
|
async def my_async_inner_span(x):
|
|
@@ -37,6 +38,7 @@ async def test_trace_root_and_span_async_success(mock_tracer_deps):
|
|
|
37
38
|
return result
|
|
38
39
|
|
|
39
40
|
mock_batch.reset_mock()
|
|
41
|
+
mock_alerter.reset_mock()
|
|
40
42
|
|
|
41
43
|
# --- Execution ---
|
|
42
44
|
result = await my_async_workflow_root()
|
|
@@ -47,6 +49,8 @@ async def test_trace_root_and_span_async_success(mock_tracer_deps):
|
|
|
47
49
|
# 2 calls expected: 1 (inner_span) + 1 (workflow_root)
|
|
48
50
|
assert mock_batch.add_object.call_count == 2
|
|
49
51
|
|
|
52
|
+
mock_alerter.notify.assert_not_called()
|
|
53
|
+
|
|
50
54
|
call_args_list = mock_batch.add_object.call_args_list
|
|
51
55
|
props_map = {
|
|
52
56
|
call.kwargs["properties"]["function_name"]: call.kwargs["properties"]
|
|
@@ -80,9 +84,12 @@ async def test_trace_span_async_failure(mock_tracer_deps):
|
|
|
80
84
|
Case 2: Async span failure test
|
|
81
85
|
'''
|
|
82
86
|
mock_batch = mock_tracer_deps["batch"]
|
|
87
|
+
mock_alerter = mock_tracer_deps["alerter"]
|
|
83
88
|
|
|
84
89
|
class AsyncTestError(Exception):
|
|
85
|
-
|
|
90
|
+
@property
|
|
91
|
+
def error_code(self):
|
|
92
|
+
return "ASYNC_TEST_FAILURE"
|
|
86
93
|
|
|
87
94
|
@trace_span
|
|
88
95
|
async def my_failing_async_span():
|
|
@@ -97,6 +104,20 @@ async def test_trace_span_async_failure(mock_tracer_deps):
|
|
|
97
104
|
with pytest.raises(AsyncTestError, match="Async failure test"):
|
|
98
105
|
await my_async_workflow_fail()
|
|
99
106
|
|
|
107
|
+
mock_alerter.notify.assert_called_once()
|
|
108
|
+
alert_props = mock_alerter.notify.call_args.args[0]
|
|
109
|
+
|
|
110
|
+
assert alert_props["status"] == "ERROR"
|
|
111
|
+
assert "AsyncTestError: Async failure test" in alert_props["error_message"]
|
|
112
|
+
assert alert_props["function_name"] == "my_failing_async_span"
|
|
113
|
+
assert alert_props["error_code"] == "ASYNC_TEST_FAILURE"
|
|
114
|
+
|
|
115
|
+
mock_batch.add_object.assert_called_once()
|
|
116
|
+
db_props = mock_batch.add_object.call_args.kwargs["properties"]
|
|
117
|
+
|
|
118
|
+
assert db_props == alert_props
|
|
119
|
+
assert db_props["span_id"] == alert_props["span_id"]
|
|
120
|
+
|
|
100
121
|
# --- Verification (Log) ---
|
|
101
122
|
# Check if the failed span log was recorded
|
|
102
123
|
failing_span_props = None
|
|
@@ -116,6 +137,7 @@ async def test_span_without_root_async_does_nothing(mock_tracer_deps):
|
|
|
116
137
|
Case 3: Async span called without a root (@trace_root) should not be logged
|
|
117
138
|
'''
|
|
118
139
|
mock_batch = mock_tracer_deps["batch"]
|
|
140
|
+
mock_alerter = mock_tracer_deps["alerter"]
|
|
119
141
|
|
|
120
142
|
@trace_span
|
|
121
143
|
async def my_lonely_async_span():
|
|
@@ -126,6 +148,7 @@ async def test_span_without_root_async_does_nothing(mock_tracer_deps):
|
|
|
126
148
|
|
|
127
149
|
assert result == "lonely_result"
|
|
128
150
|
mock_batch.add_object.assert_not_called()
|
|
151
|
+
mock_alerter.notify.assert_not_called()
|
|
129
152
|
|
|
130
153
|
|
|
131
154
|
# ==================================
|