1from unittest import TestCase
2
3from ddtrace import config
4from ddtrace.pin import Pin
5from ddtrace.settings import IntegrationConfig
6
7
8class InstanceConfigTestCase(TestCase):
9    """TestCase for the Configuration API that is used to define
10    global settings and for each `Pin` instance.
11    """
12
13    def setUp(self):
14        class Klass(object):
15            """Helper class where a Pin is always attached"""
16
17            pass
18
19        # define the Class and attach a Pin to it
20        self.Klass = Klass
21        Pin(service="metrics").onto(Klass)
22
23    def test_configuration_get_from(self):
24        # ensure a dictionary is returned
25        cfg = config.get_from(self.Klass)
26        assert isinstance(cfg, dict)
27
28    def test_configuration_get_from_twice(self):
29        # ensure the configuration is the same if `get_from` is used
30        # in the same instance
31        instance = self.Klass()
32        cfg1 = config.get_from(instance)
33        cfg2 = config.get_from(instance)
34        assert cfg1 is cfg2
35
36    def test_configuration_set(self):
37        # ensure the configuration can be updated in the Pin
38        instance = self.Klass()
39        cfg = config.get_from(instance)
40        cfg["distributed_tracing"] = True
41        assert config.get_from(instance)["distributed_tracing"] is True
42
43    def test_global_configuration_inheritance(self):
44        # ensure global configuration is inherited when it's set
45        cfg = config.get_from(self.Klass)
46        cfg["distributed_tracing"] = True
47        instance = self.Klass()
48        assert config.get_from(instance)["distributed_tracing"] is True
49
50    def test_configuration_override_instance(self):
51        # ensure instance configuration doesn't override global settings
52        global_cfg = config.get_from(self.Klass)
53        global_cfg["distributed_tracing"] = True
54        instance = self.Klass()
55        cfg = config.get_from(instance)
56        cfg["distributed_tracing"] = False
57        assert config.get_from(self.Klass)["distributed_tracing"] is True
58        assert config.get_from(instance)["distributed_tracing"] is False
59
60    def test_service_name_for_pin(self):
61        # ensure for backward compatibility that changing the service
62        # name via the Pin object also updates integration config
63        Pin(service="intake").onto(self.Klass)
64        instance = self.Klass()
65        cfg = config.get_from(instance)
66        assert cfg["service_name"] == "intake"
67
68    def test_service_attribute_priority(self):
69        # ensure the `service` arg has highest priority over configuration
70        # for backward compatibility
71        global_config = {
72            "service_name": "primary_service",
73        }
74        Pin(service="service", _config=global_config).onto(self.Klass)
75        instance = self.Klass()
76        cfg = config.get_from(instance)
77        assert cfg["service_name"] == "service"
78
79    def test_configuration_copy(self):
80        # ensure when a Pin is used, the given configuration is copied
81        global_config = {
82            "service_name": "service",
83        }
84        Pin(service="service", _config=global_config).onto(self.Klass)
85        instance = self.Klass()
86        cfg = config.get_from(instance)
87        cfg["service_name"] = "metrics"
88        assert global_config["service_name"] == "service"
89
90    def test_configuration_copy_upside_down(self):
91        # ensure when a Pin is created, it does not copy the given configuration
92        # until it's used for at least once
93        global_config = {
94            "service_name": "service",
95        }
96        Pin(service="service", _config=global_config).onto(self.Klass)
97        # override the global config: users do that before using the integration
98        global_config["service_name"] = "metrics"
99        # use the Pin via `get_from`
100        instance = self.Klass()
101        cfg = config.get_from(instance)
102        # it should have users updated value
103        assert cfg["service_name"] == "metrics"
104
105    def test_config_attr_and_key(self):
106        """
107        This is a regression test for when mixing attr attribute and key
108        access we would set the value of the attribute but not the key
109        """
110        integration_config = IntegrationConfig(config, "test")
111
112        # Our key and attribute do not exist
113        self.assertFalse(hasattr(integration_config, "distributed_tracing"))
114        self.assertNotIn("distributed_tracing", integration_config)
115
116        # Initially set and access
117        integration_config["distributed_tracing"] = True
118        self.assertTrue(integration_config["distributed_tracing"])
119        self.assertTrue(integration_config.get("distributed_tracing"))
120        self.assertTrue(integration_config.distributed_tracing)
121
122        # Override by key and access
123        integration_config["distributed_tracing"] = False
124        self.assertFalse(integration_config["distributed_tracing"])
125        self.assertFalse(integration_config.get("distributed_tracing"))
126        self.assertFalse(integration_config.distributed_tracing)
127
128        # Override by attr and access
129        integration_config.distributed_tracing = None
130        self.assertIsNone(integration_config["distributed_tracing"])
131        self.assertIsNone(integration_config.get("distributed_tracing"))
132        self.assertIsNone(integration_config.distributed_tracing)
133