6
6
7
7
from docker import DockerClient
8
8
from docker .models .networks import Network , NetworkCollection
9
+ from docker .types import EndpointConfig
9
10
from mock_collector_client import MockCollectorClient
10
11
from testcontainers .core .container import DockerContainer
11
12
from testcontainers .core .waiting_utils import wait_for_logs
12
13
from typing_extensions import override
13
14
15
+ NETWORK_NAME : str = "aws-appsignals-network"
16
+
14
17
_logger : Logger = getLogger (__name__ )
15
18
_logger .setLevel (INFO )
16
-
17
19
_MOCK_COLLECTOR_ALIAS : str = "collector"
18
20
_MOCK_COLLECTOR_NAME : str = "aws-appsignals-mock-collector-python"
19
21
_MOCK_COLLECTOR_PORT : int = 4315
20
- _NETWORK_NAME : str = "aws-appsignals-network"
21
-
22
- _MOCK_COLLECTOR : DockerContainer = (
23
- DockerContainer (_MOCK_COLLECTOR_NAME ).with_exposed_ports (_MOCK_COLLECTOR_PORT ).with_name (_MOCK_COLLECTOR_NAME )
24
- )
25
- _NETWORK : Network = NetworkCollection (client = DockerClient ()).create (_NETWORK_NAME )
26
22
27
23
24
+ # pylint: disable=broad-exception-caught
28
25
class ContractTestBase (TestCase ):
29
26
"""Base class for implementing a contract test.
30
27
@@ -35,29 +32,53 @@ class ContractTestBase(TestCase):
35
32
Several methods are provided that can be overridden to customize the test scenario.
36
33
"""
37
34
38
- _mock_collector_client : MockCollectorClient
39
- _application : DockerContainer
35
+ application : DockerContainer
36
+ mock_collector : DockerContainer
37
+ mock_collector_client : MockCollectorClient
38
+ network : Network
40
39
41
40
@classmethod
42
41
@override
43
42
def setUpClass (cls ) -> None :
44
- _MOCK_COLLECTOR .start ()
45
- wait_for_logs (_MOCK_COLLECTOR , "Ready" , timeout = 20 )
46
- _NETWORK .connect (_MOCK_COLLECTOR_NAME , aliases = [_MOCK_COLLECTOR_ALIAS ])
43
+ cls .addClassCleanup (cls .class_tear_down )
44
+ cls .network = NetworkCollection (client = DockerClient ()).create (NETWORK_NAME )
45
+ mock_collector_networking_config : Dict [str , EndpointConfig ] = {
46
+ NETWORK_NAME : EndpointConfig (version = "1.22" , aliases = [_MOCK_COLLECTOR_ALIAS ])
47
+ }
48
+ cls .mock_collector : DockerContainer = (
49
+ DockerContainer (_MOCK_COLLECTOR_NAME )
50
+ .with_exposed_ports (_MOCK_COLLECTOR_PORT )
51
+ .with_name (_MOCK_COLLECTOR_NAME )
52
+ .with_kwargs (network = NETWORK_NAME , networking_config = mock_collector_networking_config )
53
+ )
54
+ cls .mock_collector .start ()
55
+ wait_for_logs (cls .mock_collector , "Ready" , timeout = 20 )
56
+ cls .set_up_dependency_container ()
47
57
48
58
@classmethod
49
- @override
50
- def tearDownClass (cls ) -> None :
51
- _logger .info ("MockCollector stdout" )
52
- _logger .info (_MOCK_COLLECTOR .get_logs ()[0 ].decode ())
53
- _logger .info ("MockCollector stderr" )
54
- _logger .info (_MOCK_COLLECTOR .get_logs ()[1 ].decode ())
55
- _MOCK_COLLECTOR .stop ()
56
- _NETWORK .remove ()
59
+ def class_tear_down (cls ) -> None :
60
+ try :
61
+ cls .tear_down_dependency_container ()
62
+ except Exception :
63
+ _logger .exception ("Failed to tear down dependency container" )
64
+
65
+ try :
66
+ _logger .info ("MockCollector stdout" )
67
+ _logger .info (cls .mock_collector .get_logs ()[0 ].decode ())
68
+ _logger .info ("MockCollector stderr" )
69
+ _logger .info (cls .mock_collector .get_logs ()[1 ].decode ())
70
+ cls .mock_collector .stop ()
71
+ except Exception :
72
+ _logger .exception ("Failed to tear down mock collector" )
73
+
74
+ cls .network .remove ()
57
75
58
76
@override
59
77
def setUp (self ) -> None :
60
- self ._application : DockerContainer = (
78
+ application_networking_config : Dict [str , EndpointConfig ] = {
79
+ NETWORK_NAME : EndpointConfig (version = "1.22" , aliases = self .get_application_network_aliases ())
80
+ }
81
+ self .application : DockerContainer = (
61
82
DockerContainer (self .get_application_image_name ())
62
83
.with_exposed_ports (self .get_application_port ())
63
84
.with_env ("OTEL_METRIC_EXPORT_INTERVAL" , "100" )
@@ -68,31 +89,42 @@ def setUp(self) -> None:
68
89
.with_env ("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT" , f"http://collector:{ _MOCK_COLLECTOR_PORT } " )
69
90
.with_env ("OTEL_RESOURCE_ATTRIBUTES" , self .get_application_otel_resource_attributes ())
70
91
.with_env ("OTEL_TRACES_SAMPLER" , "always_on" )
92
+ .with_kwargs (network = NETWORK_NAME , networking_config = application_networking_config )
71
93
.with_name (self .get_application_image_name ())
72
94
)
73
95
74
96
extra_env : Dict [str , str ] = self .get_application_extra_environment_variables ()
75
97
for key in extra_env :
76
- self ._application .with_env (key , extra_env .get (key ))
77
- self ._application .start ()
78
- wait_for_logs (self ._application , self .get_application_wait_pattern (), timeout = 20 )
79
- _NETWORK .connect (self .get_application_image_name (), aliases = self .get_application_network_aliases ())
80
-
81
- self ._mock_collector_client : MockCollectorClient = MockCollectorClient (
82
- _MOCK_COLLECTOR .get_container_host_ip (), _MOCK_COLLECTOR .get_exposed_port (_MOCK_COLLECTOR_PORT )
98
+ self .application .with_env (key , extra_env .get (key ))
99
+ self .application .start ()
100
+ wait_for_logs (self .application , self .get_application_wait_pattern (), timeout = 20 )
101
+ self .mock_collector_client : MockCollectorClient = MockCollectorClient (
102
+ self .mock_collector .get_container_host_ip (), self .mock_collector .get_exposed_port (_MOCK_COLLECTOR_PORT )
83
103
)
84
104
85
105
@override
86
106
def tearDown (self ) -> None :
87
- _logger .info ("Application stdout" )
88
- _logger .info (self ._application .get_logs ()[0 ].decode ())
89
- _logger .info ("Application stderr" )
90
- _logger .info (self ._application .get_logs ()[1 ].decode ())
91
- self ._application .stop ()
92
- self ._mock_collector_client .clear_signals ()
107
+ try :
108
+ _logger .info ("Application stdout" )
109
+ _logger .info (self .application .get_logs ()[0 ].decode ())
110
+ _logger .info ("Application stderr" )
111
+ _logger .info (self .application .get_logs ()[1 ].decode ())
112
+ self .application .stop ()
113
+ except Exception :
114
+ _logger .exception ("Failed to tear down application" )
115
+
116
+ self .mock_collector_client .clear_signals ()
93
117
94
118
# pylint: disable=no-self-use
95
119
# Methods that should be overridden in subclasses
120
+ @classmethod
121
+ def set_up_dependency_container (cls ):
122
+ return
123
+
124
+ @classmethod
125
+ def tear_down_dependency_container (cls ):
126
+ return
127
+
96
128
def get_application_port (self ) -> int :
97
129
return 8080
98
130
0 commit comments