julee 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

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.
Files changed (155) hide show
  1. julee/api/app.py +9 -8
  2. julee/api/dependencies.py +15 -15
  3. julee/api/requests.py +10 -9
  4. julee/api/responses.py +2 -1
  5. julee/api/routers/__init__.py +5 -5
  6. julee/api/routers/assembly_specifications.py +5 -4
  7. julee/api/routers/documents.py +1 -1
  8. julee/api/routers/knowledge_service_configs.py +4 -3
  9. julee/api/routers/knowledge_service_queries.py +7 -6
  10. julee/api/routers/system.py +4 -3
  11. julee/api/routers/workflows.py +4 -5
  12. julee/api/services/system_initialization.py +6 -6
  13. julee/api/tests/routers/test_assembly_specifications.py +4 -3
  14. julee/api/tests/routers/test_documents.py +5 -4
  15. julee/api/tests/routers/test_knowledge_service_configs.py +7 -6
  16. julee/api/tests/routers/test_knowledge_service_queries.py +4 -3
  17. julee/api/tests/routers/test_system.py +5 -4
  18. julee/api/tests/routers/test_workflows.py +5 -4
  19. julee/api/tests/test_app.py +5 -4
  20. julee/api/tests/test_dependencies.py +3 -2
  21. julee/api/tests/test_requests.py +2 -1
  22. julee/contrib/__init__.py +15 -0
  23. julee/contrib/polling/__init__.py +47 -0
  24. julee/contrib/polling/domain/__init__.py +17 -0
  25. julee/contrib/polling/domain/models/__init__.py +13 -0
  26. julee/contrib/polling/domain/models/polling_config.py +39 -0
  27. julee/contrib/polling/domain/services/__init__.py +11 -0
  28. julee/contrib/polling/domain/services/poller.py +39 -0
  29. julee/contrib/polling/infrastructure/__init__.py +15 -0
  30. julee/contrib/polling/infrastructure/services/__init__.py +12 -0
  31. julee/contrib/polling/infrastructure/services/polling/__init__.py +12 -0
  32. julee/contrib/polling/infrastructure/services/polling/http/__init__.py +12 -0
  33. julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +80 -0
  34. julee/contrib/polling/infrastructure/temporal/__init__.py +20 -0
  35. julee/contrib/polling/infrastructure/temporal/activities.py +42 -0
  36. julee/contrib/polling/infrastructure/temporal/activity_names.py +20 -0
  37. julee/contrib/polling/infrastructure/temporal/proxies.py +45 -0
  38. julee/contrib/polling/tests/__init__.py +6 -0
  39. julee/contrib/polling/tests/unit/__init__.py +6 -0
  40. julee/contrib/polling/tests/unit/infrastructure/__init__.py +7 -0
  41. julee/contrib/polling/tests/unit/infrastructure/services/__init__.py +6 -0
  42. julee/contrib/polling/tests/unit/infrastructure/services/polling/__init__.py +6 -0
  43. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/__init__.py +7 -0
  44. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +163 -0
  45. julee/docs/__init__.py +5 -0
  46. julee/docs/sphinx_hcd/__init__.py +82 -0
  47. julee/docs/sphinx_hcd/accelerators.py +1078 -0
  48. julee/docs/sphinx_hcd/apps.py +499 -0
  49. julee/docs/sphinx_hcd/config.py +148 -0
  50. julee/docs/sphinx_hcd/epics.py +448 -0
  51. julee/docs/sphinx_hcd/integrations.py +306 -0
  52. julee/docs/sphinx_hcd/journeys.py +783 -0
  53. julee/docs/sphinx_hcd/personas.py +435 -0
  54. julee/docs/sphinx_hcd/stories.py +932 -0
  55. julee/docs/sphinx_hcd/utils.py +180 -0
  56. julee/domain/models/__init__.py +5 -6
  57. julee/domain/models/assembly/assembly.py +7 -7
  58. julee/domain/models/assembly/tests/factories.py +2 -1
  59. julee/domain/models/assembly/tests/test_assembly.py +16 -13
  60. julee/domain/models/assembly_specification/assembly_specification.py +11 -10
  61. julee/domain/models/assembly_specification/knowledge_service_query.py +7 -6
  62. julee/domain/models/assembly_specification/tests/factories.py +2 -1
  63. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +9 -6
  64. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +3 -1
  65. julee/domain/models/custom_fields/content_stream.py +3 -2
  66. julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -1
  67. julee/domain/models/document/document.py +12 -10
  68. julee/domain/models/document/tests/factories.py +3 -2
  69. julee/domain/models/document/tests/test_document.py +6 -3
  70. julee/domain/models/knowledge_service_config/knowledge_service_config.py +4 -4
  71. julee/domain/models/policy/__init__.py +4 -4
  72. julee/domain/models/policy/document_policy_validation.py +17 -17
  73. julee/domain/models/policy/policy.py +10 -10
  74. julee/domain/models/policy/tests/factories.py +2 -1
  75. julee/domain/models/policy/tests/test_document_policy_validation.py +3 -1
  76. julee/domain/models/policy/tests/test_policy.py +2 -1
  77. julee/domain/repositories/__init__.py +3 -3
  78. julee/domain/repositories/assembly.py +3 -1
  79. julee/domain/repositories/assembly_specification.py +2 -0
  80. julee/domain/repositories/base.py +5 -4
  81. julee/domain/repositories/document.py +3 -1
  82. julee/domain/repositories/document_policy_validation.py +3 -1
  83. julee/domain/repositories/knowledge_service_config.py +2 -0
  84. julee/domain/repositories/knowledge_service_query.py +1 -0
  85. julee/domain/repositories/policy.py +3 -1
  86. julee/domain/use_cases/decorators.py +3 -2
  87. julee/domain/use_cases/extract_assemble_data.py +13 -12
  88. julee/domain/use_cases/initialize_system_data.py +13 -13
  89. julee/domain/use_cases/tests/test_extract_assemble_data.py +10 -10
  90. julee/domain/use_cases/tests/test_initialize_system_data.py +2 -2
  91. julee/domain/use_cases/tests/test_validate_document.py +11 -11
  92. julee/domain/use_cases/validate_document.py +14 -14
  93. julee/maintenance/__init__.py +1 -0
  94. julee/maintenance/release.py +188 -0
  95. julee/repositories/memory/assembly.py +6 -5
  96. julee/repositories/memory/assembly_specification.py +8 -9
  97. julee/repositories/memory/base.py +12 -11
  98. julee/repositories/memory/document.py +8 -7
  99. julee/repositories/memory/document_policy_validation.py +7 -6
  100. julee/repositories/memory/knowledge_service_config.py +8 -7
  101. julee/repositories/memory/knowledge_service_query.py +8 -7
  102. julee/repositories/memory/policy.py +6 -5
  103. julee/repositories/memory/tests/test_document.py +6 -4
  104. julee/repositories/memory/tests/test_document_policy_validation.py +2 -1
  105. julee/repositories/memory/tests/test_policy.py +2 -1
  106. julee/repositories/minio/assembly.py +4 -4
  107. julee/repositories/minio/assembly_specification.py +6 -8
  108. julee/repositories/minio/client.py +22 -25
  109. julee/repositories/minio/document.py +11 -11
  110. julee/repositories/minio/document_policy_validation.py +5 -5
  111. julee/repositories/minio/knowledge_service_config.py +6 -6
  112. julee/repositories/minio/knowledge_service_query.py +6 -9
  113. julee/repositories/minio/policy.py +4 -4
  114. julee/repositories/minio/tests/fake_client.py +11 -9
  115. julee/repositories/minio/tests/test_assembly.py +3 -1
  116. julee/repositories/minio/tests/test_assembly_specification.py +2 -1
  117. julee/repositories/minio/tests/test_client_protocol.py +5 -5
  118. julee/repositories/minio/tests/test_document.py +7 -6
  119. julee/repositories/minio/tests/test_document_policy_validation.py +3 -1
  120. julee/repositories/minio/tests/test_knowledge_service_config.py +4 -2
  121. julee/repositories/minio/tests/test_knowledge_service_query.py +3 -2
  122. julee/repositories/minio/tests/test_policy.py +3 -1
  123. julee/repositories/temporal/activities.py +5 -5
  124. julee/repositories/temporal/proxies.py +5 -5
  125. julee/services/knowledge_service/__init__.py +1 -2
  126. julee/services/knowledge_service/anthropic/knowledge_service.py +8 -7
  127. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +11 -10
  128. julee/services/knowledge_service/factory.py +8 -8
  129. julee/services/knowledge_service/knowledge_service.py +12 -14
  130. julee/services/knowledge_service/memory/knowledge_service.py +13 -12
  131. julee/services/knowledge_service/memory/test_knowledge_service.py +10 -7
  132. julee/services/knowledge_service/test_factory.py +11 -10
  133. julee/services/temporal/activities.py +10 -10
  134. julee/services/temporal/proxies.py +2 -2
  135. julee/util/domain.py +6 -6
  136. julee/util/repos/minio/file_storage.py +8 -9
  137. julee/util/repos/temporal/client_proxies/file_storage.py +3 -4
  138. julee/util/repos/temporal/data_converter.py +6 -6
  139. julee/util/repos/temporal/minio_file_storage.py +1 -1
  140. julee/util/repos/temporal/proxies/file_storage.py +2 -3
  141. julee/util/repositories.py +4 -3
  142. julee/util/temporal/decorators.py +20 -18
  143. julee/util/tests/test_decorators.py +13 -15
  144. julee/util/validation/repository.py +3 -3
  145. julee/util/validation/type_guards.py +12 -11
  146. julee/worker.py +9 -8
  147. julee/workflows/__init__.py +2 -2
  148. julee/workflows/extract_assemble.py +2 -1
  149. julee/workflows/validate_document.py +3 -2
  150. {julee-0.1.2.dist-info → julee-0.1.3.dist-info}/METADATA +2 -1
  151. julee-0.1.3.dist-info/RECORD +197 -0
  152. julee-0.1.2.dist-info/RECORD +0 -161
  153. {julee-0.1.2.dist-info → julee-0.1.3.dist-info}/WHEEL +0 -0
  154. {julee-0.1.2.dist-info → julee-0.1.3.dist-info}/licenses/LICENSE +0 -0
  155. {julee-0.1.2.dist-info → julee-0.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,17 @@
1
+ """
2
+ Domain layer for the polling contrib module.
3
+
4
+ This module contains the core domain models, services, and business rules
5
+ for the polling contrib module. It defines the fundamental concepts and
6
+ protocols that govern polling operations.
7
+ """
8
+
9
+ from .models import PollingConfig, PollingProtocol, PollingResult
10
+ from .services import PollerService
11
+
12
+ __all__ = [
13
+ "PollingConfig",
14
+ "PollingProtocol",
15
+ "PollingResult",
16
+ "PollerService",
17
+ ]
@@ -0,0 +1,13 @@
1
+ """
2
+ Polling domain models.
3
+
4
+ This module contains the core domain models for the polling contrib module.
5
+ """
6
+
7
+ from .polling_config import PollingConfig, PollingProtocol, PollingResult
8
+
9
+ __all__ = [
10
+ "PollingConfig",
11
+ "PollingProtocol",
12
+ "PollingResult",
13
+ ]
@@ -0,0 +1,39 @@
1
+ """
2
+ Polling domain models.
3
+
4
+ This module contains the core domain models for polling operations,
5
+ including configuration and result models.
6
+ """
7
+
8
+ from datetime import datetime, timezone
9
+ from enum import Enum
10
+ from typing import Any
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+
15
+ class PollingProtocol(str, Enum):
16
+ """Supported polling protocols."""
17
+
18
+ HTTP = "http"
19
+
20
+
21
+ class PollingConfig(BaseModel):
22
+ """Configuration for a polling operation."""
23
+
24
+ endpoint_identifier: str = Field(description="Unique identifier for this endpoint")
25
+ polling_protocol: PollingProtocol
26
+ connection_params: dict[str, Any] = Field(default_factory=dict)
27
+ polling_params: dict[str, Any] = Field(default_factory=dict)
28
+ timeout_seconds: int | None = Field(default=30)
29
+
30
+
31
+ class PollingResult(BaseModel):
32
+ """Result of a polling operation."""
33
+
34
+ success: bool
35
+ content: bytes
36
+ metadata: dict[str, Any] = Field(default_factory=dict)
37
+ polled_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
38
+ content_hash: str | None = None
39
+ error_message: str | None = None
@@ -0,0 +1,11 @@
1
+ """
2
+ Polling domain services.
3
+
4
+ This module contains the service protocols for the polling contrib module.
5
+ """
6
+
7
+ from .poller import PollerService
8
+
9
+ __all__ = [
10
+ "PollerService",
11
+ ]
@@ -0,0 +1,39 @@
1
+ """
2
+ PollerService protocol for external endpoint polling operations.
3
+
4
+ This module defines the PollerService protocol that handles interactions
5
+ with various types of external endpoints for data polling and change detection.
6
+
7
+ Concrete implementations of this protocol are provided for different polling
8
+ mechanisms and are created via factory functions.
9
+ """
10
+
11
+ from typing import Protocol, runtime_checkable
12
+
13
+ from ..models import PollingConfig, PollingResult
14
+
15
+
16
+ @runtime_checkable
17
+ class PollerService(Protocol):
18
+ """
19
+ Protocol for polling external endpoints for data.
20
+
21
+ This protocol defines the interface for a poller service that can perform
22
+ individual poll operations on different endpoint types. Implementations
23
+ handle the specifics of different polling mechanisms.
24
+ """
25
+
26
+ async def poll_endpoint(self, config: PollingConfig) -> PollingResult:
27
+ """
28
+ Poll an endpoint according to the provided configuration.
29
+
30
+ Args:
31
+ config: PollingConfig containing endpoint details and parameters
32
+
33
+ Returns:
34
+ PollingResult with success status, content, and metadata
35
+
36
+ Raises:
37
+ PollingError: When polling operation fails
38
+ """
39
+ ...
@@ -0,0 +1,15 @@
1
+ """
2
+ Infrastructure layer for the polling contrib module.
3
+
4
+ This module contains the concrete implementations of domain protocols
5
+ and external system integrations for the polling contrib module.
6
+ """
7
+
8
+ from .services import HttpPollerService
9
+ from .temporal import TemporalPollerService, WorkflowPollerServiceProxy
10
+
11
+ __all__ = [
12
+ "HttpPollerService",
13
+ "TemporalPollerService",
14
+ "WorkflowPollerServiceProxy",
15
+ ]
@@ -0,0 +1,12 @@
1
+ """
2
+ Infrastructure services for the polling contrib module.
3
+
4
+ This module contains the concrete implementations of domain services
5
+ for the polling contrib module.
6
+ """
7
+
8
+ from .polling import HttpPollerService
9
+
10
+ __all__ = [
11
+ "HttpPollerService",
12
+ ]
@@ -0,0 +1,12 @@
1
+ """
2
+ Polling infrastructure services.
3
+
4
+ This module contains the concrete implementations of polling services
5
+ for different protocols and mechanisms.
6
+ """
7
+
8
+ from .http import HttpPollerService
9
+
10
+ __all__ = [
11
+ "HttpPollerService",
12
+ ]
@@ -0,0 +1,12 @@
1
+ """
2
+ HTTP polling implementation.
3
+
4
+ This module provides HTTP-specific polling functionality for the polling
5
+ contrib module.
6
+ """
7
+
8
+ from .http_poller_service import HttpPollerService
9
+
10
+ __all__ = [
11
+ "HttpPollerService",
12
+ ]
@@ -0,0 +1,80 @@
1
+ """
2
+ HTTP implementation of the PollerService protocol.
3
+
4
+ This module provides HTTP-specific polling functionality including
5
+ REST API endpoints, webhooks, and other HTTP-based data sources.
6
+ """
7
+
8
+ import hashlib
9
+ from datetime import datetime, timezone
10
+ from typing import Any
11
+
12
+ import httpx
13
+
14
+ from julee.contrib.polling.domain.models import PollingConfig, PollingResult
15
+ from julee.contrib.polling.domain.services import PollerService
16
+
17
+
18
+ class HttpPollerService(PollerService):
19
+ """HTTP implementation of PollerService protocol."""
20
+
21
+ def __init__(self) -> None:
22
+ self.client = httpx.AsyncClient()
23
+
24
+ async def poll_endpoint(self, config: PollingConfig) -> PollingResult:
25
+ """Poll an HTTP endpoint."""
26
+ try:
27
+ # Extract HTTP-specific params
28
+ url = config.connection_params["url"]
29
+ headers = config.connection_params.get("headers", {})
30
+ method = config.polling_params.get("method", "GET")
31
+ auth_params = config.connection_params.get("auth", {})
32
+
33
+ # Make HTTP request
34
+ response = await self.client.request(
35
+ method=method,
36
+ url=url,
37
+ headers=headers,
38
+ timeout=config.timeout_seconds,
39
+ **auth_params,
40
+ )
41
+
42
+ content = response.content
43
+ content_hash = hashlib.sha256(content).hexdigest()
44
+
45
+ # Only consider 2xx status codes as successful
46
+ success = 200 <= response.status_code < 300
47
+
48
+ return PollingResult(
49
+ success=success,
50
+ content=content if success else b"",
51
+ content_hash=content_hash if success else None,
52
+ polled_at=datetime.now(timezone.utc),
53
+ metadata={
54
+ "status_code": response.status_code,
55
+ "response_headers": dict(response.headers),
56
+ "url": url,
57
+ "method": method,
58
+ },
59
+ )
60
+
61
+ except Exception as e:
62
+ return PollingResult(
63
+ success=False,
64
+ content=b"",
65
+ error_message=str(e),
66
+ polled_at=datetime.now(timezone.utc),
67
+ metadata={"error_type": type(e).__name__},
68
+ )
69
+
70
+ async def close(self) -> None:
71
+ """Close the HTTP client connection."""
72
+ await self.client.aclose()
73
+
74
+ async def __aenter__(self) -> "HttpPollerService":
75
+ """Async context manager entry."""
76
+ return self
77
+
78
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
79
+ """Async context manager exit."""
80
+ await self.close()
@@ -0,0 +1,20 @@
1
+ """
2
+ Temporal infrastructure for the polling contrib module.
3
+
4
+ This module contains the temporal-specific implementations for the polling
5
+ contrib module, including activity registrations, workflow proxies, and
6
+ activity name constants.
7
+
8
+ This keeps all polling-temporal integration within the contrib module,
9
+ maintaining proper dependency direction (contrib imports from core, not vice versa).
10
+ """
11
+
12
+ from .activities import TemporalPollerService
13
+ from .activity_names import POLLING_SERVICE_ACTIVITY_BASE
14
+ from .proxies import WorkflowPollerServiceProxy
15
+
16
+ __all__ = [
17
+ "TemporalPollerService",
18
+ "POLLING_SERVICE_ACTIVITY_BASE",
19
+ "WorkflowPollerServiceProxy",
20
+ ]
@@ -0,0 +1,42 @@
1
+ """
2
+ Temporal activities for the polling contrib module.
3
+
4
+ This module contains the temporal activity implementations for polling operations.
5
+ These activities are registered with Temporal and can be called from workflows
6
+ to perform durable polling operations.
7
+
8
+ By keeping these activities within the contrib module, we maintain proper
9
+ dependency direction - contrib modules define their own temporal integration
10
+ without requiring the core framework to know about specific contrib modules.
11
+ """
12
+
13
+ import logging
14
+
15
+ from julee.util.temporal.decorators import temporal_activity_registration
16
+
17
+ from ..services.polling.http import HttpPollerService
18
+ from .activity_names import POLLING_SERVICE_ACTIVITY_BASE
19
+
20
+
21
+ @temporal_activity_registration(POLLING_SERVICE_ACTIVITY_BASE)
22
+ class TemporalPollerService(HttpPollerService):
23
+ """
24
+ Temporal activity wrapper for PollerService operations.
25
+
26
+ This class wraps the HttpPollerService to make it compatible with Temporal
27
+ activities. It inherits all the polling functionality while being registered
28
+ as a Temporal activity that can be called from workflows.
29
+
30
+ The temporal activity registration provides durability, retries, and
31
+ observability for polling operations within workflow contexts.
32
+ """
33
+
34
+ def __init__(self) -> None:
35
+ super().__init__()
36
+ self.logger: logging.Logger = logging.getLogger(__name__)
37
+
38
+
39
+ # Export the temporal activity class
40
+ __all__ = [
41
+ "TemporalPollerService",
42
+ ]
@@ -0,0 +1,20 @@
1
+ """
2
+ Activity name constants for the polling contrib module.
3
+
4
+ This module contains activity name base constants specific to the polling
5
+ contrib module. These constants are used for registering polling activities
6
+ and calling them from workflow proxies.
7
+
8
+ By keeping these constants within the contrib module, we maintain proper
9
+ dependency direction - contrib modules define their own temporal integration
10
+ without requiring the core framework to know about specific contrib modules.
11
+ """
12
+
13
+ # Activity name base for polling service activities
14
+ POLLING_SERVICE_ACTIVITY_BASE = "julee.contrib.polling"
15
+
16
+
17
+ # Export all constants
18
+ __all__ = [
19
+ "POLLING_SERVICE_ACTIVITY_BASE",
20
+ ]
@@ -0,0 +1,45 @@
1
+ """
2
+ Workflow-safe proxy classes for the polling contrib module.
3
+
4
+ This module contains @temporal_workflow_proxy decorated classes that
5
+ delegate to Temporal activities from within workflows. These classes are
6
+ isolated from backend imports to avoid Temporal's workflow sandbox
7
+ restrictions.
8
+
9
+ The proxy classes automatically generate methods that call
10
+ workflow.execute_activity() with the appropriate activity names, timeouts,
11
+ and retry policies.
12
+
13
+ This keeps all polling-temporal integration within the contrib module,
14
+ maintaining proper dependency direction (contrib imports from core, not vice versa).
15
+ """
16
+
17
+ from julee.util.temporal.decorators import temporal_workflow_proxy
18
+
19
+ from ...domain.services import PollerService
20
+ from .activity_names import POLLING_SERVICE_ACTIVITY_BASE
21
+
22
+
23
+ @temporal_workflow_proxy(
24
+ activity_base=POLLING_SERVICE_ACTIVITY_BASE,
25
+ default_timeout_seconds=60, # Shorter timeout for HTTP calls
26
+ retry_methods=["poll_endpoint"], # Only poll_endpoint needs retries
27
+ )
28
+ class WorkflowPollerServiceProxy(PollerService):
29
+ """
30
+ Workflow implementation of PollerService that calls activities.
31
+ All methods are automatically generated by the @temporal_workflow_proxy
32
+ decorator.
33
+
34
+ This proxy allows workflows to call polling operations through Temporal
35
+ activities, providing durability and retry capabilities for polling
36
+ operations within workflow contexts.
37
+ """
38
+
39
+ pass
40
+
41
+
42
+ # Export the workflow proxy class
43
+ __all__ = [
44
+ "WorkflowPollerServiceProxy",
45
+ ]
@@ -0,0 +1,6 @@
1
+ """
2
+ Tests for the polling contrib module.
3
+
4
+ This module contains the test suite for the polling contrib module,
5
+ including unit tests, integration tests, and test fixtures.
6
+ """
@@ -0,0 +1,6 @@
1
+ """
2
+ Unit tests for the polling contrib module.
3
+
4
+ This module contains unit tests for the polling contrib module,
5
+ testing individual components in isolation.
6
+ """
@@ -0,0 +1,7 @@
1
+ """
2
+ Infrastructure layer tests for the polling contrib module.
3
+
4
+ This module contains unit tests for the infrastructure implementations
5
+ of the polling contrib module, including service implementations and
6
+ external system integrations.
7
+ """
@@ -0,0 +1,6 @@
1
+ """
2
+ Services tests for the polling contrib module infrastructure.
3
+
4
+ This module contains unit tests for service implementations in the
5
+ infrastructure layer of the polling contrib module.
6
+ """
@@ -0,0 +1,6 @@
1
+ """
2
+ Polling services tests for the polling contrib module infrastructure.
3
+
4
+ This module contains unit tests for polling service implementations in the
5
+ infrastructure layer, including HTTP and other protocol-specific pollers.
6
+ """
@@ -0,0 +1,7 @@
1
+ """
2
+ HTTP polling service tests for the polling contrib module infrastructure.
3
+
4
+ This module contains unit tests for the HTTP-specific polling service
5
+ implementation in the infrastructure layer, testing HTTP endpoint polling
6
+ functionality, error handling, and response processing.
7
+ """
@@ -0,0 +1,163 @@
1
+ """
2
+ Unit tests for HttpPollerService.
3
+
4
+ This module tests the HTTP poller service implementation using httpx's
5
+ built-in MockTransport for mocking HTTP responses. Tests use table-based
6
+ parametrization for comprehensive coverage of different scenarios.
7
+ """
8
+
9
+ import hashlib
10
+
11
+ import httpx
12
+ import pytest
13
+
14
+ from julee.contrib.polling.domain.models import PollingConfig, PollingProtocol
15
+ from julee.contrib.polling.infrastructure.services.polling.http import (
16
+ HttpPollerService,
17
+ )
18
+
19
+
20
+ class TestHttpPollerServicePollEndpoint:
21
+ """Test the poll_endpoint method of HttpPollerService."""
22
+
23
+ @pytest.mark.parametrize(
24
+ "status_code,content,expected_success,description",
25
+ [
26
+ (200, b"test content", True, "HTTP 200 with content"),
27
+ (201, b"created", True, "HTTP 201 created"),
28
+ (204, b"", True, "HTTP 204 no content"),
29
+ (299, b"success boundary", True, "HTTP 299 (2xx boundary)"),
30
+ (300, b"redirect", False, "HTTP 300 redirect (not 2xx)"),
31
+ (404, b"not found", False, "HTTP 404 client error"),
32
+ (418, b"teapot", False, "HTTP 418 client error"),
33
+ (500, b"server error", False, "HTTP 500 server error"),
34
+ (502, b"bad gateway", False, "HTTP 502 bad gateway"),
35
+ (503, b"unavailable", False, "HTTP 503 service unavailable"),
36
+ ],
37
+ )
38
+ @pytest.mark.asyncio
39
+ async def test_poll_endpoint_status_codes(
40
+ self, status_code: int, content: bytes, expected_success: bool, description: str
41
+ ):
42
+ """Test poll_endpoint with various HTTP status codes."""
43
+
44
+ def handler(request):
45
+ return httpx.Response(status_code=status_code, content=content)
46
+
47
+ mock_transport = httpx.MockTransport(handler)
48
+
49
+ async with HttpPollerService() as service:
50
+ service.client = httpx.AsyncClient(transport=mock_transport)
51
+
52
+ config = PollingConfig(
53
+ endpoint_identifier="test-api",
54
+ polling_protocol=PollingProtocol.HTTP,
55
+ connection_params={"url": "https://example.com/api"},
56
+ )
57
+
58
+ result = await service.poll_endpoint(config)
59
+
60
+ assert result.success == expected_success, f"Failed for {description}"
61
+ if expected_success:
62
+ assert result.content == content
63
+ assert result.content_hash is not None
64
+ else:
65
+ assert result.content == b""
66
+ assert result.content_hash is None
67
+ assert result.metadata["status_code"] == status_code
68
+
69
+ @pytest.mark.parametrize(
70
+ "exception_type,description",
71
+ [
72
+ (httpx.ConnectTimeout, "Connection timeout"),
73
+ (httpx.ReadTimeout, "Read timeout"),
74
+ (httpx.ConnectError, "Connection error"),
75
+ (httpx.RequestError, "Generic request error"),
76
+ ],
77
+ )
78
+ @pytest.mark.asyncio
79
+ async def test_poll_endpoint_network_errors(
80
+ self, exception_type: type, description: str
81
+ ):
82
+ """Test poll_endpoint with various network errors."""
83
+
84
+ def handler(request):
85
+ raise exception_type("Mock network error")
86
+
87
+ mock_transport = httpx.MockTransport(handler)
88
+
89
+ async with HttpPollerService() as service:
90
+ service.client = httpx.AsyncClient(transport=mock_transport)
91
+
92
+ config = PollingConfig(
93
+ endpoint_identifier="test-api",
94
+ polling_protocol=PollingProtocol.HTTP,
95
+ connection_params={"url": "https://example.com/api"},
96
+ )
97
+
98
+ result = await service.poll_endpoint(config)
99
+
100
+ assert result.success is False, f"Should fail for {description}"
101
+ assert result.content == b""
102
+ assert "Mock network error" in result.error_message
103
+ assert result.metadata["error_type"] == exception_type.__name__
104
+
105
+ @pytest.mark.asyncio
106
+ async def test_poll_endpoint_content_hash_generation(self):
107
+ """Test that poll_endpoint generates correct content hash."""
108
+ test_content = b"test content for hashing"
109
+ expected_hash = hashlib.sha256(test_content).hexdigest()
110
+
111
+ def handler(request):
112
+ return httpx.Response(status_code=200, content=test_content)
113
+
114
+ mock_transport = httpx.MockTransport(handler)
115
+
116
+ async with HttpPollerService() as service:
117
+ service.client = httpx.AsyncClient(transport=mock_transport)
118
+
119
+ config = PollingConfig(
120
+ endpoint_identifier="test-api",
121
+ polling_protocol=PollingProtocol.HTTP,
122
+ connection_params={"url": "https://example.com/api"},
123
+ )
124
+
125
+ result = await service.poll_endpoint(config)
126
+
127
+ assert result.success is True
128
+ assert result.content == test_content
129
+ assert result.content_hash == expected_hash
130
+
131
+ @pytest.mark.asyncio
132
+ async def test_poll_endpoint_uses_config_parameters(self):
133
+ """Test that poll_endpoint uses configuration parameters correctly."""
134
+ captured_request = None
135
+
136
+ def handler(request):
137
+ nonlocal captured_request
138
+ captured_request = request
139
+ return httpx.Response(status_code=200, content=b"success")
140
+
141
+ mock_transport = httpx.MockTransport(handler)
142
+
143
+ async with HttpPollerService() as service:
144
+ service.client = httpx.AsyncClient(transport=mock_transport)
145
+
146
+ config = PollingConfig(
147
+ endpoint_identifier="test-api",
148
+ polling_protocol=PollingProtocol.HTTP,
149
+ connection_params={
150
+ "url": "https://api.example.com/data",
151
+ "headers": {"Authorization": "Bearer token123"},
152
+ },
153
+ polling_params={"method": "POST"},
154
+ timeout_seconds=30,
155
+ )
156
+
157
+ result = await service.poll_endpoint(config)
158
+
159
+ assert result.success is True
160
+ assert captured_request is not None
161
+ assert str(captured_request.url) == "https://api.example.com/data"
162
+ assert captured_request.method == "POST"
163
+ assert captured_request.headers["Authorization"] == "Bearer token123"
julee/docs/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Julee documentation utilities.
2
+
3
+ This package provides Sphinx extensions and utilities for documenting
4
+ Julee-based solutions using Human-Centered Design patterns.
5
+ """