1+ import unittest
2+ from unittest .mock import patch , MagicMock
3+ import re
4+ import sys
5+
6+ # Import the modules package, which will load the clone module.
7+ # The 'clone' name in the package is the command object, so we get the module from sys.modules.
8+ import cloudinary_cli .modules
9+ clone_module = sys .modules ['cloudinary_cli.modules.clone' ]
10+
11+ from cloudinary_cli .defaults import logger
12+
13+
14+ class TestCLIClone (unittest .TestCase ):
15+
16+ def setUp (self ):
17+ self .mock_search_result = {
18+ 'resources' : [
19+ {
20+ 'public_id' : 'sample' ,
21+ 'type' : 'upload' ,
22+ 'resource_type' : 'image' ,
23+ 'format' : 'jpg' ,
24+ 'secure_url' : 'https://res.cloudinary.com/demo/image/upload/v1234567890/sample.jpg' ,
25+ 'tags' : ['tag1' , 'tag2' ],
26+ 'context' : {'key' : 'value' },
27+ 'folder' : 'test_folder' ,
28+ 'display_name' : 'Test Asset'
29+ }
30+ ]
31+ }
32+
33+ @patch ('cloudinary.api.metadata_fields' )
34+ def test_list_metadata_items (self , mock_metadata_fields ):
35+ """Test listing metadata fields"""
36+ mock_metadata_fields .return_value = {
37+ 'metadata_fields' : [
38+ {
39+ 'external_id' : 'test_field' ,
40+ 'type' : 'string' ,
41+ 'label' : 'Test Field' ,
42+ 'mandatory' : False
43+ }
44+ ]
45+ }
46+
47+ result = clone_module .list_metadata_items ()
48+
49+ mock_metadata_fields .assert_called_once ()
50+ self .assertEqual (result , mock_metadata_fields .return_value ['metadata_fields' ])
51+
52+ @patch ('cloudinary.api.add_metadata_field' )
53+ def test_create_metadata_item (self , mock_add_metadata_field ):
54+ """Test creating a single metadata field"""
55+ metadata_field = {
56+ 'external_id' : 'test_field' ,
57+ 'type' : 'string' ,
58+ 'label' : 'Test Field' ,
59+ 'mandatory' : False
60+ }
61+
62+ clone_module .create_metadata_item (metadata_field )
63+
64+ mock_add_metadata_field .assert_called_once_with (metadata_field )
65+
66+ @patch ('cloudinary.api.add_metadata_field' )
67+ def test_create_metadata_item_with_error (self , mock_add_metadata_field ):
68+ """Test creating metadata field with API error"""
69+ metadata_field = {
70+ 'external_id' : 'test_field' ,
71+ 'type' : 'string' ,
72+ 'label' : 'Test Field' ,
73+ 'mandatory' : False
74+ }
75+
76+ mock_add_metadata_field .side_effect = Exception ("API Error" )
77+
78+ with self .assertLogs (logger , level = 'ERROR' ) as log :
79+ clone_module .create_metadata_item (metadata_field )
80+ self .assertIn ('Error creating metadata field' , log .output [0 ])
81+
82+ @patch .object (clone_module , 'create_metadata_item' )
83+ @patch .object (clone_module , 'list_metadata_items' )
84+ def test_compare_create_metadata_items_new_fields (self , mock_list , mock_create ):
85+ """Test comparing and creating new metadata fields"""
86+ source_fields = [
87+ {
88+ 'external_id' : 'field1' ,
89+ 'type' : 'string' ,
90+ 'label' : 'Field 1'
91+ },
92+ {
93+ 'external_id' : 'field2' ,
94+ 'type' : 'integer' ,
95+ 'label' : 'Field 2'
96+ }
97+ ]
98+
99+ # Simulate destination having no fields
100+ mock_list .return_value = []
101+
102+ clone_module .compare_create_metadata_items (source_fields )
103+
104+ # Both fields should be created
105+ self .assertEqual (mock_create .call_count , 2 )
106+ mock_create .assert_any_call (source_fields [0 ])
107+ mock_create .assert_any_call (source_fields [1 ])
108+
109+ @patch .object (clone_module , 'create_metadata_item' )
110+ @patch .object (clone_module , 'list_metadata_items' )
111+ def test_compare_create_metadata_items_existing_fields (self , mock_list , mock_create ):
112+ """Test comparing when fields already exist"""
113+ source_fields = [
114+ {
115+ 'external_id' : 'field1' ,
116+ 'type' : 'string' ,
117+ 'label' : 'Field 1'
118+ }
119+ ]
120+
121+ # Simulate destination already having the field
122+ mock_list .return_value = [
123+ {
124+ 'external_id' : 'field1' ,
125+ 'type' : 'string' ,
126+ 'label' : 'Field 1'
127+ }
128+ ]
129+
130+ clone_module .compare_create_metadata_items (source_fields )
131+
132+ # No fields should be created
133+ mock_create .assert_not_called ()
134+
135+ @patch .object (clone_module , 'create_metadata_item' )
136+ @patch .object (clone_module , 'list_metadata_items' )
137+ def test_compare_create_metadata_items_mixed_scenario (self , mock_list , mock_create ):
138+ """Test comparing with mix of new and existing fields"""
139+ source_fields = [
140+ {
141+ 'external_id' : 'existing_field' ,
142+ 'type' : 'string' ,
143+ 'label' : 'Existing Field'
144+ },
145+ {
146+ 'external_id' : 'new_field' ,
147+ 'type' : 'integer' ,
148+ 'label' : 'New Field'
149+ }
150+ ]
151+
152+ # Simulate destination having only one field
153+ mock_list .return_value = [
154+ {
155+ 'external_id' : 'existing_field' ,
156+ 'type' : 'string' ,
157+ 'label' : 'Existing Field'
158+ }
159+ ]
160+
161+ clone_module .compare_create_metadata_items (source_fields )
162+
163+ # Only new_field should be created
164+ mock_create .assert_called_once_with (source_fields [1 ])
165+
166+
167+ if __name__ == '__main__' :
168+ unittest .main ()
0 commit comments