20
20
ValidateAdheresToSchema ,
21
21
)
22
22
23
-
24
23
logger = logging .getLogger (__name__ )
25
24
26
- EXCLUDED_CONNECTORS = [
27
- ]
25
+ EXCLUDED_CONNECTORS : List [Tuple [str , str ]] = []
28
26
29
27
CONNECTOR_REGISTRY_URL = "https://connectors.airbyte.com/files/registries/v0/oss_registry.json"
30
- MANIFEST_URL_TEMPLATE = "https://connectors.airbyte.com/files/metadata/airbyte/{connector_name}/latest/manifest.yaml"
28
+ MANIFEST_URL_TEMPLATE = (
29
+ "https://connectors.airbyte.com/files/metadata/airbyte/{connector_name}/latest/manifest.yaml"
30
+ )
31
31
32
- VALIDATION_SUCCESSES = []
33
- VALIDATION_FAILURES = []
34
- DOWNLOAD_FAILURES = []
32
+ VALIDATION_SUCCESSES : List [ Tuple [ str , str ]] = []
33
+ VALIDATION_FAILURES : List [ Tuple [ str , str , str ]] = []
34
+ DOWNLOAD_FAILURES : List [ Tuple [ str , str ]] = []
35
35
36
36
37
37
def load_declarative_component_schema () -> Dict [str , Any ]:
@@ -41,29 +41,32 @@ def load_declarative_component_schema() -> Dict[str, Any]:
41
41
/ "airbyte_cdk/sources/declarative/declarative_component_schema.yaml"
42
42
)
43
43
with open (schema_path , "r" ) as file :
44
- return yaml .safe_load (file )
44
+ schema = yaml .safe_load (file )
45
+ if not isinstance (schema , dict ):
46
+ raise ValueError ("Schema must be a dictionary" )
47
+ return schema
45
48
46
49
47
50
def get_manifest_only_connectors () -> List [Tuple [str , str ]]:
48
51
"""
49
52
Fetch manifest-only connectors from the registry.
50
-
53
+
51
54
Returns:
52
- List of tuples (connector_name, cdk_version) where cdk_version will be
55
+ List of tuples (connector_name, cdk_version) where cdk_version will be
53
56
determined from the manifest.yaml file itself.
54
57
"""
55
58
try :
56
59
response = requests .get (CONNECTOR_REGISTRY_URL , timeout = 30 )
57
60
response .raise_for_status ()
58
61
registry = response .json ()
59
-
60
- manifest_connectors = []
62
+
63
+ manifest_connectors : List [ Tuple [ str , str ]] = []
61
64
for source in registry .get ("sources" , []):
62
65
if source .get ("language" ) == "manifest-only" :
63
66
connector_name = source .get ("dockerRepository" , "" ).replace ("airbyte/" , "" )
64
67
if connector_name :
65
- manifest_connectors .append ((connector_name , None ))
66
-
68
+ manifest_connectors .append ((connector_name , "unknown" ))
69
+
67
70
return manifest_connectors
68
71
except Exception as e :
69
72
pytest .fail (f"Failed to fetch connector registry: { e } " )
@@ -72,7 +75,7 @@ def get_manifest_only_connectors() -> List[Tuple[str, str]]:
72
75
def download_manifest (connector_name : str ) -> Tuple [str , str ]:
73
76
"""
74
77
Download manifest.yaml for a connector.
75
-
78
+
76
79
Returns:
77
80
Tuple of (manifest_content, cdk_version) where cdk_version is extracted
78
81
from the manifest's version field.
@@ -82,10 +85,10 @@ def download_manifest(connector_name: str) -> Tuple[str, str]:
82
85
response = requests .get (url , timeout = 30 )
83
86
response .raise_for_status ()
84
87
manifest_content = response .text
85
-
88
+
86
89
manifest_dict = yaml .safe_load (manifest_content )
87
90
cdk_version = manifest_dict .get ("version" , "unknown" )
88
-
91
+
89
92
return manifest_content , cdk_version
90
93
except Exception as e :
91
94
DOWNLOAD_FAILURES .append ((connector_name , str (e )))
@@ -95,7 +98,7 @@ def download_manifest(connector_name: str) -> Tuple[str, str]:
95
98
def get_manifest_only_connector_names () -> List [str ]:
96
99
"""
97
100
Get all manifest-only connector names from the registry.
98
-
101
+
99
102
Returns:
100
103
List of connector names (e.g., "source-hubspot")
101
104
"""
@@ -104,10 +107,10 @@ def get_manifest_only_connector_names() -> List[str]:
104
107
105
108
106
109
@pytest .mark .parametrize ("connector_name" , get_manifest_only_connector_names ())
107
- def test_manifest_validates_against_schema (connector_name : str ):
110
+ def test_manifest_validates_against_schema (connector_name : str ) -> None :
108
111
"""
109
112
Test that manifest.yaml files from the registry validate against the CDK schema.
110
-
113
+
111
114
Args:
112
115
connector_name: Name of the connector (e.g., "source-hubspot")
113
116
"""
@@ -116,23 +119,23 @@ def test_manifest_validates_against_schema(connector_name: str):
116
119
manifest_content , cdk_version = download_manifest (connector_name )
117
120
except Exception as e :
118
121
pytest .fail (f"Failed to download manifest for { connector_name } : { e } " )
119
-
122
+
120
123
if (connector_name , cdk_version ) in EXCLUDED_CONNECTORS :
121
124
pytest .skip (
122
125
f"Skipping { connector_name } - connector declares it is compatible with "
123
126
f"CDK version { cdk_version } but is known to fail validation"
124
127
)
125
-
128
+
126
129
try :
127
130
manifest_dict = yaml .safe_load (manifest_content )
128
131
except yaml .YAMLError as e :
129
132
error_msg = f"Invalid YAML in manifest for { connector_name } : { e } "
130
133
VALIDATION_FAILURES .append ((connector_name , cdk_version , error_msg ))
131
134
pytest .fail (error_msg )
132
-
135
+
133
136
schema = load_declarative_component_schema ()
134
137
validator = ValidateAdheresToSchema (schema = schema )
135
-
138
+
136
139
try :
137
140
validator .validate (manifest_dict )
138
141
VALIDATION_SUCCESSES .append ((connector_name , cdk_version ))
@@ -147,15 +150,15 @@ def test_manifest_validates_against_schema(connector_name: str):
147
150
pytest .fail (error_msg )
148
151
149
152
150
- def test_schema_loads_successfully ():
153
+ def test_schema_loads_successfully () -> None :
151
154
"""Test that the declarative component schema loads without errors."""
152
155
schema = load_declarative_component_schema ()
153
156
assert isinstance (schema , dict )
154
157
assert "type" in schema
155
158
assert schema ["type" ] == "object"
156
159
157
160
158
- def test_connector_registry_accessible ():
161
+ def test_connector_registry_accessible () -> None :
159
162
"""Test that the connector registry is accessible."""
160
163
response = requests .get (CONNECTOR_REGISTRY_URL , timeout = 30 )
161
164
assert response .status_code == 200
@@ -164,63 +167,65 @@ def test_connector_registry_accessible():
164
167
assert isinstance (registry ["sources" ], list )
165
168
166
169
167
- def test_manifest_only_connectors_found ():
170
+ def test_manifest_only_connectors_found () -> None :
168
171
"""Test that we can find manifest-only connectors in the registry."""
169
172
connectors = get_manifest_only_connectors ()
170
173
assert len (connectors ) > 0 , "No manifest-only connectors found in registry"
171
-
174
+
172
175
for connector_name , _ in connectors :
173
176
assert isinstance (connector_name , str )
174
177
assert len (connector_name ) > 0
175
178
assert connector_name .startswith ("source-" ) or connector_name .startswith ("destination-" )
176
179
177
180
178
- def test_sample_manifest_download ():
181
+ def test_sample_manifest_download () -> None :
179
182
"""Test that we can download a sample manifest file."""
180
183
connectors = get_manifest_only_connectors ()
181
184
if not connectors :
182
185
pytest .skip ("No manifest-only connectors available for testing" )
183
-
186
+
184
187
connector_name , _ = connectors [0 ]
185
188
try :
186
189
manifest_content , cdk_version = download_manifest (connector_name )
187
190
except Exception as e :
188
191
pytest .skip (f"Could not download sample manifest from { connector_name } : { e } " )
189
-
192
+
190
193
assert isinstance (manifest_content , str )
191
194
assert len (manifest_content ) > 0
192
195
assert isinstance (cdk_version , str )
193
196
assert len (cdk_version ) > 0
194
-
197
+
195
198
manifest_dict = yaml .safe_load (manifest_content )
196
199
assert isinstance (manifest_dict , dict )
197
200
assert "version" in manifest_dict
198
201
assert manifest_dict ["version" ] == cdk_version
199
202
200
203
201
- def log_test_results ():
204
+ def log_test_results () -> None :
202
205
"""Log comprehensive test results for analysis."""
203
- print ("\n " + "=" * 80 )
206
+ print ("\n " + "=" * 80 )
204
207
print ("MANIFEST VALIDATION TEST RESULTS SUMMARY" )
205
- print ("=" * 80 )
206
-
208
+ print ("=" * 80 )
209
+
207
210
print (f"\n ✓ SUCCESSFUL VALIDATIONS ({ len (VALIDATION_SUCCESSES )} ):" )
208
211
for connector_name , cdk_version in VALIDATION_SUCCESSES :
209
212
print (f" - { connector_name } (CDK { cdk_version } )" )
210
-
213
+
211
214
print (f"\n ✗ VALIDATION FAILURES ({ len (VALIDATION_FAILURES )} ):" )
212
215
for connector_name , cdk_version , error in VALIDATION_FAILURES :
213
216
print (f" - { connector_name } (CDK { cdk_version } ): { error } " )
214
-
217
+
215
218
print (f"\n ⚠ DOWNLOAD FAILURES ({ len (DOWNLOAD_FAILURES )} ):" )
216
219
for connector_name , error in DOWNLOAD_FAILURES :
217
220
print (f" - { connector_name } : { error } " )
218
-
219
- print ("\n " + "=" * 80 )
220
- print (f"TOTAL: { len (VALIDATION_SUCCESSES )} passed, { len (VALIDATION_FAILURES )} failed, { len (DOWNLOAD_FAILURES )} download errors" )
221
- print ("=" * 80 )
221
+
222
+ print ("\n " + "=" * 80 )
223
+ print (
224
+ f"TOTAL: { len (VALIDATION_SUCCESSES )} passed, { len (VALIDATION_FAILURES )} failed, { len (DOWNLOAD_FAILURES )} download errors"
225
+ )
226
+ print ("=" * 80 )
222
227
223
228
224
- def pytest_sessionfinish (session , exitstatus ) :
229
+ def pytest_sessionfinish (session : Any , exitstatus : Any ) -> None :
225
230
"""Called after whole test run finished, right before returning the exit status to the system."""
226
231
log_test_results ()
0 commit comments