Contentstack 2.0.0__tar.gz → 2.1.0__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.
- {contentstack-2.0.0 → contentstack-2.1.0}/Contentstack.egg-info/PKG-INFO +1 -1
- {contentstack-2.0.0 → contentstack-2.1.0}/Contentstack.egg-info/SOURCES.txt +4 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/PKG-INFO +1 -1
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/__init__.py +3 -3
- contentstack-2.1.0/contentstack/globalfields.py +73 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/stack.py +10 -0
- contentstack-2.1.0/tests/test_early_fetch.py +124 -0
- contentstack-2.1.0/tests/test_early_find.py +138 -0
- contentstack-2.1.0/tests/test_global_fields.py +98 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/Contentstack.egg-info/dependency_links.txt +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/Contentstack.egg-info/not-zip-safe +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/Contentstack.egg-info/requires.txt +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/Contentstack.egg-info/top_level.txt +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/LICENSE +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/README.md +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/asset.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/assetquery.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/basequery.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/contenttype.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/controller.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/deep_merge_lp.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/entry.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/entryqueryable.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/https_connection.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/image_transform.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/query.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/contentstack/utility.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/setup.cfg +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/setup.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/tests/test_assets.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/tests/test_entry.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/tests/test_live_preview.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/tests/test_query.py +0 -0
- {contentstack-2.0.0 → contentstack-2.1.0}/tests/test_stack.py +0 -0
|
@@ -16,13 +16,17 @@ contentstack/controller.py
|
|
|
16
16
|
contentstack/deep_merge_lp.py
|
|
17
17
|
contentstack/entry.py
|
|
18
18
|
contentstack/entryqueryable.py
|
|
19
|
+
contentstack/globalfields.py
|
|
19
20
|
contentstack/https_connection.py
|
|
20
21
|
contentstack/image_transform.py
|
|
21
22
|
contentstack/query.py
|
|
22
23
|
contentstack/stack.py
|
|
23
24
|
contentstack/utility.py
|
|
24
25
|
tests/test_assets.py
|
|
26
|
+
tests/test_early_fetch.py
|
|
27
|
+
tests/test_early_find.py
|
|
25
28
|
tests/test_entry.py
|
|
29
|
+
tests/test_global_fields.py
|
|
26
30
|
tests/test_live_preview.py
|
|
27
31
|
tests/test_query.py
|
|
28
32
|
tests/test_stack.py
|
|
@@ -22,8 +22,8 @@ __all__ = (
|
|
|
22
22
|
__title__ = 'contentstack-delivery-python'
|
|
23
23
|
__author__ = 'contentstack'
|
|
24
24
|
__status__ = 'debug'
|
|
25
|
-
__version__ = 'v2.
|
|
25
|
+
__version__ = 'v2.1.0'
|
|
26
26
|
__endpoint__ = 'cdn.contentstack.io'
|
|
27
|
-
__email__ = '
|
|
28
|
-
__developer_email__ = '
|
|
27
|
+
__email__ = 'support@contentstack.com'
|
|
28
|
+
__developer_email__ = 'mobile@contentstack.com'
|
|
29
29
|
__license__ = "MIT"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Global field defines the structure or schema of a page or a section of your web
|
|
3
|
+
or mobile property. To create content for your application, you are required
|
|
4
|
+
to first create a Global field, and then create entries using the
|
|
5
|
+
Global field.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from urllib import parse
|
|
10
|
+
|
|
11
|
+
class GlobalField:
|
|
12
|
+
"""
|
|
13
|
+
Global field defines the structure or schema of a page or a
|
|
14
|
+
section of your web or mobile property. To create
|
|
15
|
+
content for your application, you are required to
|
|
16
|
+
first create a Global field, and then create entries using the
|
|
17
|
+
Global field.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, http_instance, global_field_uid, logger=None):
|
|
21
|
+
self.http_instance = http_instance
|
|
22
|
+
self.__global_field_uid = global_field_uid
|
|
23
|
+
self.local_param = {}
|
|
24
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def fetch(self):
|
|
28
|
+
"""
|
|
29
|
+
This method is useful to fetch GlobalField of the of the stack.
|
|
30
|
+
:return:dict -- GlobalField response
|
|
31
|
+
------------------------------
|
|
32
|
+
Example:
|
|
33
|
+
|
|
34
|
+
>>> import contentstack
|
|
35
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
36
|
+
>>> global_field = stack.global_field('global_field_uid')
|
|
37
|
+
>>> some_dict = {'abc':'something'}
|
|
38
|
+
>>> response = global_field.fetch(some_dict)
|
|
39
|
+
------------------------------
|
|
40
|
+
"""
|
|
41
|
+
if self.__global_field_uid is None:
|
|
42
|
+
raise KeyError(
|
|
43
|
+
'global_field_uid can not be None to fetch GlobalField')
|
|
44
|
+
self.local_param['environment'] = self.http_instance.headers['environment']
|
|
45
|
+
uri = f'{self.http_instance.endpoint}/global_fields/{self.__global_field_uid}'
|
|
46
|
+
encoded_params = parse.urlencode(self.local_param)
|
|
47
|
+
url = f'{uri}?{encoded_params}'
|
|
48
|
+
result = self.http_instance.get(url)
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
def find(self, params=None):
|
|
52
|
+
"""
|
|
53
|
+
This method is useful to fetch GlobalField of the of the stack.
|
|
54
|
+
:param params: dictionary of params
|
|
55
|
+
:return:dict -- GlobalField response
|
|
56
|
+
------------------------------
|
|
57
|
+
Example:
|
|
58
|
+
|
|
59
|
+
>>> import contentstack
|
|
60
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
61
|
+
>>> global_field = stack.global_field()
|
|
62
|
+
>>> some_dict = {'abc':'something'}
|
|
63
|
+
>>> response = global_field.find(param=some_dict)
|
|
64
|
+
------------------------------
|
|
65
|
+
"""
|
|
66
|
+
self.local_param['environment'] = self.http_instance.headers['environment']
|
|
67
|
+
if params is not None:
|
|
68
|
+
self.local_param.update(params)
|
|
69
|
+
encoded_params = parse.urlencode(self.local_param)
|
|
70
|
+
endpoint = self.http_instance.endpoint
|
|
71
|
+
url = f'{endpoint}/global_fields?{encoded_params}'
|
|
72
|
+
result = self.http_instance.get(url)
|
|
73
|
+
return result
|
|
@@ -6,6 +6,7 @@ from urllib3.util import Retry
|
|
|
6
6
|
from contentstack.asset import Asset
|
|
7
7
|
from contentstack.assetquery import AssetQuery
|
|
8
8
|
from contentstack.contenttype import ContentType
|
|
9
|
+
from contentstack.globalfields import GlobalField
|
|
9
10
|
from contentstack.https_connection import HTTPSConnection
|
|
10
11
|
from contentstack.image_transform import ImageTransform
|
|
11
12
|
|
|
@@ -202,6 +203,15 @@ class Stack:
|
|
|
202
203
|
:return: ContentType
|
|
203
204
|
"""
|
|
204
205
|
return ContentType(self.http_instance, content_type_uid)
|
|
206
|
+
|
|
207
|
+
def global_field(self, global_field_uid=None):
|
|
208
|
+
"""
|
|
209
|
+
Global field defines the structure or schema of a page or a section
|
|
210
|
+
of your web or mobile property.
|
|
211
|
+
param global_field_uid:
|
|
212
|
+
:return: GlobalField
|
|
213
|
+
"""
|
|
214
|
+
return GlobalField(self.http_instance, global_field_uid)
|
|
205
215
|
|
|
206
216
|
def asset(self, uid):
|
|
207
217
|
"""
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for GlobalField.fetch method in contentstack.globalfields
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from unittest.mock import MagicMock
|
|
7
|
+
from contentstack.globalfields import GlobalField
|
|
8
|
+
from urllib.parse import urlencode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def mock_http_instance():
|
|
13
|
+
"""
|
|
14
|
+
Fixture to provide a mock http_instance with required attributes.
|
|
15
|
+
"""
|
|
16
|
+
mock = MagicMock()
|
|
17
|
+
mock.endpoint = "https://api.contentstack.io/v3"
|
|
18
|
+
mock.headers = {"environment": "test_env"}
|
|
19
|
+
mock.get = MagicMock(return_value={"global_field": "data"})
|
|
20
|
+
return mock
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def global_field_uid():
|
|
25
|
+
"""
|
|
26
|
+
Fixture to provide a sample global_field_uid.
|
|
27
|
+
"""
|
|
28
|
+
return "sample_uid"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def global_field(mock_http_instance, global_field_uid):
|
|
33
|
+
"""
|
|
34
|
+
Fixture to provide a GlobalField instance with a mock http_instance and uid.
|
|
35
|
+
"""
|
|
36
|
+
return GlobalField(mock_http_instance, global_field_uid)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestGlobalFieldFetch:
|
|
40
|
+
# ------------------- Happy Path Tests -------------------
|
|
41
|
+
|
|
42
|
+
def test_fetch_returns_expected_result(self, global_field):
|
|
43
|
+
"""
|
|
44
|
+
Test that fetch returns the result from http_instance.get with correct URL and params.
|
|
45
|
+
"""
|
|
46
|
+
result = global_field.fetch()
|
|
47
|
+
assert result == {"global_field": "data"}
|
|
48
|
+
assert global_field.local_param["environment"] == "test_env"
|
|
49
|
+
expected_params = urlencode({"environment": "test_env"})
|
|
50
|
+
expected_url = f"https://api.contentstack.io/v3/global_fields/sample_uid?{expected_params}"
|
|
51
|
+
global_field.http_instance.get.assert_called_once_with(expected_url)
|
|
52
|
+
|
|
53
|
+
def test_fetch_with_different_environment(self, mock_http_instance, global_field_uid):
|
|
54
|
+
"""
|
|
55
|
+
Test fetch with a different environment value in headers.
|
|
56
|
+
"""
|
|
57
|
+
mock_http_instance.headers["environment"] = "prod_env"
|
|
58
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
59
|
+
result = gf.fetch()
|
|
60
|
+
assert result == {"global_field": "data"}
|
|
61
|
+
expected_params = urlencode({"environment": "prod_env"})
|
|
62
|
+
expected_url = f"https://api.contentstack.io/v3/global_fields/sample_uid?{expected_params}"
|
|
63
|
+
mock_http_instance.get.assert_called_once_with(expected_url)
|
|
64
|
+
|
|
65
|
+
def test_fetch_preserves_existing_local_param(self, global_field):
|
|
66
|
+
"""
|
|
67
|
+
Test that fetch overwrites only the 'environment' key in local_param, preserving others.
|
|
68
|
+
"""
|
|
69
|
+
global_field.local_param = {"foo": "bar"}
|
|
70
|
+
result = global_field.fetch()
|
|
71
|
+
assert result == {"global_field": "data"}
|
|
72
|
+
assert global_field.local_param["foo"] == "bar"
|
|
73
|
+
assert global_field.local_param["environment"] == "test_env"
|
|
74
|
+
expected_params = urlencode({"foo": "bar", "environment": "test_env"})
|
|
75
|
+
expected_url = f"https://api.contentstack.io/v3/global_fields/sample_uid?{expected_params}"
|
|
76
|
+
global_field.http_instance.get.assert_called_once_with(expected_url)
|
|
77
|
+
|
|
78
|
+
# ------------------- Edge Case Tests -------------------
|
|
79
|
+
|
|
80
|
+
def test_fetch_raises_keyerror_when_uid_is_none(self, mock_http_instance):
|
|
81
|
+
"""
|
|
82
|
+
Test that fetch raises KeyError if global_field_uid is None.
|
|
83
|
+
"""
|
|
84
|
+
gf = GlobalField(mock_http_instance, None)
|
|
85
|
+
with pytest.raises(KeyError, match="global_field_uid can not be None"):
|
|
86
|
+
gf.fetch()
|
|
87
|
+
|
|
88
|
+
def test_fetch_raises_keyerror_when_uid_is_explicitly_set_to_none(self, mock_http_instance):
|
|
89
|
+
"""
|
|
90
|
+
Test that fetch raises KeyError if global_field_uid is explicitly set to None after init.
|
|
91
|
+
"""
|
|
92
|
+
gf = GlobalField(mock_http_instance, "not_none")
|
|
93
|
+
gf._GlobalField__global_field_uid = None # forcibly set to None
|
|
94
|
+
with pytest.raises(KeyError, match="global_field_uid can not be None"):
|
|
95
|
+
gf.fetch()
|
|
96
|
+
|
|
97
|
+
def test_fetch_handles_special_characters_in_params(self, global_field):
|
|
98
|
+
"""
|
|
99
|
+
Test that fetch correctly encodes special characters in local_param.
|
|
100
|
+
"""
|
|
101
|
+
global_field.local_param = {"foo": "bar baz", "qux": "a&b"}
|
|
102
|
+
result = global_field.fetch()
|
|
103
|
+
assert result == {"global_field": "data"}
|
|
104
|
+
expected_params = urlencode({"foo": "bar baz", "qux": "a&b", "environment": "test_env"})
|
|
105
|
+
expected_url = f"https://api.contentstack.io/v3/global_fields/sample_uid?{expected_params}"
|
|
106
|
+
global_field.http_instance.get.assert_called_once_with(expected_url)
|
|
107
|
+
|
|
108
|
+
def test_fetch_raises_keyerror_if_environment_header_missing(self, mock_http_instance, global_field_uid):
|
|
109
|
+
"""
|
|
110
|
+
Test that fetch raises KeyError if 'environment' is missing from http_instance.headers.
|
|
111
|
+
"""
|
|
112
|
+
del mock_http_instance.headers["environment"]
|
|
113
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
114
|
+
with pytest.raises(KeyError):
|
|
115
|
+
gf.fetch()
|
|
116
|
+
|
|
117
|
+
def test_fetch_propagates_http_instance_get_exception(self, global_field):
|
|
118
|
+
"""
|
|
119
|
+
Test that fetch propagates exceptions raised by http_instance.get.
|
|
120
|
+
"""
|
|
121
|
+
global_field.http_instance.get.side_effect = RuntimeError("Network error")
|
|
122
|
+
with pytest.raises(RuntimeError, match="Network error"):
|
|
123
|
+
global_field.fetch()
|
|
124
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# test_globalfields_find.py
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
from contentstack.globalfields import GlobalField
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
def mock_http_instance():
|
|
9
|
+
"""
|
|
10
|
+
Fixture to provide a mock http_instance with headers and endpoint.
|
|
11
|
+
"""
|
|
12
|
+
mock = MagicMock()
|
|
13
|
+
mock.headers = {"environment": "test_env"}
|
|
14
|
+
mock.endpoint = "https://api.contentstack.io/v3"
|
|
15
|
+
mock.get = MagicMock(return_value={"global_fields": "data"})
|
|
16
|
+
return mock
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def global_field_uid():
|
|
20
|
+
"""
|
|
21
|
+
Fixture to provide a sample global_field_uid.
|
|
22
|
+
"""
|
|
23
|
+
return "sample_uid"
|
|
24
|
+
|
|
25
|
+
class TestGlobalFieldFind:
|
|
26
|
+
"""
|
|
27
|
+
Unit tests for GlobalField.find method, covering happy paths and edge cases.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# -------------------- Happy Path Tests --------------------
|
|
31
|
+
|
|
32
|
+
def test_find_with_no_params(self, mock_http_instance, global_field_uid):
|
|
33
|
+
"""
|
|
34
|
+
Test that find() with no params returns expected result and constructs correct URL.
|
|
35
|
+
"""
|
|
36
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
37
|
+
result = gf.find()
|
|
38
|
+
assert result == {"global_fields": "data"}
|
|
39
|
+
expected_url = (
|
|
40
|
+
"https://api.contentstack.io/v3/global_fields?environment=test_env"
|
|
41
|
+
)
|
|
42
|
+
mock_http_instance.get.assert_called_once_with(expected_url)
|
|
43
|
+
|
|
44
|
+
def test_find_with_params(self, mock_http_instance, global_field_uid):
|
|
45
|
+
"""
|
|
46
|
+
Test that find() with additional params merges them and encodes URL correctly.
|
|
47
|
+
"""
|
|
48
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
49
|
+
params = {"limit": 10, "skip": 5}
|
|
50
|
+
result = gf.find(params=params)
|
|
51
|
+
# The order of query params in the URL is not guaranteed, so check both possibilities
|
|
52
|
+
called_url = mock_http_instance.get.call_args[0][0]
|
|
53
|
+
assert result == {"global_fields": "data"}
|
|
54
|
+
assert called_url.startswith("https://api.contentstack.io/v3/global_fields?")
|
|
55
|
+
# All params must be present in the URL
|
|
56
|
+
for k, v in {"environment": "test_env", "limit": "10", "skip": "5"}.items():
|
|
57
|
+
assert f"{k}={v}" in called_url
|
|
58
|
+
|
|
59
|
+
def test_find_with_empty_params_dict(self, mock_http_instance, global_field_uid):
|
|
60
|
+
"""
|
|
61
|
+
Test that find() with an empty params dict behaves like no params.
|
|
62
|
+
"""
|
|
63
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
64
|
+
result = gf.find(params={})
|
|
65
|
+
assert result == {"global_fields": "data"}
|
|
66
|
+
expected_url = (
|
|
67
|
+
"https://api.contentstack.io/v3/global_fields?environment=test_env"
|
|
68
|
+
)
|
|
69
|
+
mock_http_instance.get.assert_called_once_with(expected_url)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_find_with_special_characters_in_params(self, mock_http_instance, global_field_uid):
|
|
74
|
+
"""
|
|
75
|
+
Test that find() correctly URL-encodes special characters in params.
|
|
76
|
+
"""
|
|
77
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
78
|
+
params = {"q": "name:foo/bar&baz", "limit": 1}
|
|
79
|
+
result = gf.find(params=params)
|
|
80
|
+
called_url = mock_http_instance.get.call_args[0][0]
|
|
81
|
+
# Check that special characters are URL-encoded
|
|
82
|
+
assert "q=name%3Afoo%2Fbar%26baz" in called_url
|
|
83
|
+
assert "limit=1" in called_url
|
|
84
|
+
assert result == {"global_fields": "data"}
|
|
85
|
+
|
|
86
|
+
def test_find_with_none_environment_in_headers(self, mock_http_instance, global_field_uid):
|
|
87
|
+
"""
|
|
88
|
+
Test that find() handles the case where 'environment' in headers is None.
|
|
89
|
+
"""
|
|
90
|
+
mock_http_instance.headers["environment"] = None
|
|
91
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
92
|
+
result = gf.find()
|
|
93
|
+
called_url = mock_http_instance.get.call_args[0][0]
|
|
94
|
+
# Should include 'environment=None' in the query string
|
|
95
|
+
assert "environment=None" in called_url
|
|
96
|
+
assert result == {"global_fields": "data"}
|
|
97
|
+
|
|
98
|
+
def test_find_with_non_string_param_values(self, mock_http_instance, global_field_uid):
|
|
99
|
+
"""
|
|
100
|
+
Test that find() handles non-string param values (e.g., int, bool, None).
|
|
101
|
+
"""
|
|
102
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
103
|
+
params = {"int_val": 42, "bool_val": True, "none_val": None}
|
|
104
|
+
result = gf.find(params=params)
|
|
105
|
+
called_url = mock_http_instance.get.call_args[0][0]
|
|
106
|
+
# int and bool should be stringified, None should be 'None'
|
|
107
|
+
assert "int_val=42" in called_url
|
|
108
|
+
assert "bool_val=True" in called_url
|
|
109
|
+
assert "none_val=None" in called_url
|
|
110
|
+
assert result == {"global_fields": "data"}
|
|
111
|
+
|
|
112
|
+
def test_find_with_empty_headers(self, global_field_uid):
|
|
113
|
+
"""
|
|
114
|
+
Test that find() raises KeyError if 'environment' is missing from headers.
|
|
115
|
+
"""
|
|
116
|
+
mock_http_instance = MagicMock()
|
|
117
|
+
mock_http_instance.headers = {}
|
|
118
|
+
mock_http_instance.endpoint = "https://api.contentstack.io/v3"
|
|
119
|
+
mock_http_instance.get = MagicMock(return_value={"global_fields": "data"})
|
|
120
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
121
|
+
with pytest.raises(KeyError):
|
|
122
|
+
gf.find()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_find_with_mutable_local_param(self, mock_http_instance, global_field_uid):
|
|
126
|
+
"""
|
|
127
|
+
Test that local_param is updated and persists between calls.
|
|
128
|
+
"""
|
|
129
|
+
gf = GlobalField(mock_http_instance, global_field_uid)
|
|
130
|
+
# First call with a param
|
|
131
|
+
gf.find(params={"foo": "bar"})
|
|
132
|
+
# Second call with a different param
|
|
133
|
+
gf.find(params={"baz": "qux"})
|
|
134
|
+
# local_param should have been updated with the last call's params
|
|
135
|
+
assert gf.local_param["baz"] == "qux"
|
|
136
|
+
assert gf.local_param["environment"] == "test_env"
|
|
137
|
+
# The previous param 'foo' should still be present (since update is cumulative)
|
|
138
|
+
assert gf.local_param["foo"] == "bar"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# test_globalfields_init.py
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import logging
|
|
5
|
+
from contentstack.globalfields import GlobalField
|
|
6
|
+
|
|
7
|
+
class DummyHttpInstance:
|
|
8
|
+
"""A dummy HTTP instance for testing purposes."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def dummy_http():
|
|
13
|
+
"""Fixture to provide a dummy http_instance."""
|
|
14
|
+
return DummyHttpInstance()
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def dummy_logger():
|
|
18
|
+
"""Fixture to provide a dummy logger."""
|
|
19
|
+
return logging.getLogger("dummy_logger")
|
|
20
|
+
|
|
21
|
+
@pytest.mark.usefixtures("dummy_http")
|
|
22
|
+
class TestGlobalFieldInit:
|
|
23
|
+
"""
|
|
24
|
+
Unit tests for GlobalField.__init__ method.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# -------------------- Happy Path Tests --------------------
|
|
28
|
+
|
|
29
|
+
def test_init_with_all_arguments(self, dummy_http, dummy_logger):
|
|
30
|
+
"""
|
|
31
|
+
Test that __init__ correctly assigns all arguments when all are provided.
|
|
32
|
+
"""
|
|
33
|
+
uid = "global_field_123"
|
|
34
|
+
gf = GlobalField(dummy_http, uid, logger=dummy_logger)
|
|
35
|
+
assert gf.http_instance is dummy_http
|
|
36
|
+
# Accessing the private variable via name mangling
|
|
37
|
+
assert gf._GlobalField__global_field_uid == uid
|
|
38
|
+
assert gf.local_param == {}
|
|
39
|
+
assert gf.logger is dummy_logger
|
|
40
|
+
|
|
41
|
+
def test_init_without_logger_uses_default(self, dummy_http):
|
|
42
|
+
"""
|
|
43
|
+
Test that __init__ assigns a default logger if none is provided.
|
|
44
|
+
"""
|
|
45
|
+
uid = "gf_uid"
|
|
46
|
+
gf = GlobalField(dummy_http, uid)
|
|
47
|
+
assert gf.http_instance is dummy_http
|
|
48
|
+
assert gf._GlobalField__global_field_uid == uid
|
|
49
|
+
assert gf.local_param == {}
|
|
50
|
+
# Should be a logger instance, and not None
|
|
51
|
+
assert isinstance(gf.logger, logging.Logger)
|
|
52
|
+
# Should be the logger for the module
|
|
53
|
+
assert gf.logger.name == "contentstack.globalfields"
|
|
54
|
+
|
|
55
|
+
# -------------------- Edge Case Tests --------------------
|
|
56
|
+
|
|
57
|
+
def test_init_with_none_uid(self, dummy_http):
|
|
58
|
+
"""
|
|
59
|
+
Test that __init__ accepts None as global_field_uid.
|
|
60
|
+
"""
|
|
61
|
+
gf = GlobalField(dummy_http, None)
|
|
62
|
+
assert gf._GlobalField__global_field_uid is None
|
|
63
|
+
|
|
64
|
+
def test_init_with_empty_string_uid(self, dummy_http):
|
|
65
|
+
"""
|
|
66
|
+
Test that __init__ accepts empty string as global_field_uid.
|
|
67
|
+
"""
|
|
68
|
+
gf = GlobalField(dummy_http, "")
|
|
69
|
+
assert gf._GlobalField__global_field_uid == ""
|
|
70
|
+
|
|
71
|
+
def test_init_with_non_string_uid(self, dummy_http):
|
|
72
|
+
"""
|
|
73
|
+
Test that __init__ accepts non-string types for global_field_uid.
|
|
74
|
+
"""
|
|
75
|
+
for val in [123, 45.6, {"a": 1}, [1, 2, 3], (4, 5), True, object()]:
|
|
76
|
+
gf = GlobalField(dummy_http, val)
|
|
77
|
+
assert gf._GlobalField__global_field_uid == val
|
|
78
|
+
|
|
79
|
+
def test_init_with_none_http_instance(self):
|
|
80
|
+
"""
|
|
81
|
+
Test that __init__ accepts None as http_instance.
|
|
82
|
+
"""
|
|
83
|
+
uid = "gf_uid"
|
|
84
|
+
gf = GlobalField(None, uid)
|
|
85
|
+
assert gf.http_instance is None
|
|
86
|
+
assert gf._GlobalField__global_field_uid == uid
|
|
87
|
+
|
|
88
|
+
def test_init_with_custom_logger_object(self, dummy_http):
|
|
89
|
+
"""
|
|
90
|
+
Test that __init__ accepts any object as logger.
|
|
91
|
+
"""
|
|
92
|
+
class DummyLogger:
|
|
93
|
+
def info(self, msg): pass
|
|
94
|
+
dummy = DummyLogger()
|
|
95
|
+
gf = GlobalField(dummy_http, "uid", logger=dummy)
|
|
96
|
+
assert gf.logger is dummy
|
|
97
|
+
|
|
98
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|