Skip to content

Commit 060e656

Browse files
author
Matt Howlett
authored
Merge pull request confluentinc#953 from choogeboom/hoogamaphone_test_compatibility
Add Test Compatibility to Schema Registry Client
2 parents c3dd07c + 34f360c commit 060e656

File tree

3 files changed

+157
-35
lines changed

3 files changed

+157
-35
lines changed

src/confluent_kafka/schema_registry/schema_registry_client.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,41 @@ def get_compatibility(self, subject_name=None):
628628
result = self._rest_client.get(url)
629629
return result['compatibilityLevel']
630630

631+
def test_compatibility(self, subject_name, schema, version="latest"):
632+
"""Test the compatibility of a candidate schema for a given subject and version
633+
634+
Args:
635+
subject_name (str): Subject name the schema is registered under
636+
637+
schema (Schema): Schema instance.
638+
639+
version (int or str, optional): Version number, or the string "latest". Defaults to "latest".
640+
641+
Returns:
642+
bool: True if the schema is compatible with the specified version
643+
644+
Raises:
645+
SchemaRegistryError: if the request was unsuccessful.
646+
647+
See Also:
648+
`POST Test Compatibility API Reference <https://docs.confluent.io/current/schema-registry/develop/api.html#post--compatibility-subjects-(string-%20subject)-versions-(versionId-%20version)>`_
649+
""" # noqa: E501
650+
request = {"schema": schema.schema_str}
651+
if schema.schema_type != "AVRO":
652+
request['schemaType'] = schema.schema_type
653+
654+
if schema.references:
655+
request['references'] = [
656+
{'name': ref.name, 'subject': ref.subject, 'version': ref.version}
657+
for ref in schema.references
658+
]
659+
660+
response = self._rest_client.post(
661+
'compatibility/subjects/{}/versions/{}'.format(subject_name, version), body=request
662+
)
663+
664+
return response['is_compatible']
665+
631666

632667
class Schema(object):
633668
"""

tests/schema_registry/conftest.py

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -74,41 +74,49 @@ class MockSchemaRegistryClient(SchemaRegistryClient):
7474
not match MockSchemaRegistryClient.USERINFO.
7575
7676
Request paths to trigger exceptions:
77-
+--------+----------------------------------+-------+------------------------------+
78-
| Method | Request Path | Code | Description |
79-
+========+==================================+=======+==============================+
80-
| GET | /schemas/ids/404 | 40403 | Schema not found |
81-
+--------+----------------------------------+-------+------------------------------+
82-
| GET | /subjects/notfound/versions | 40401 | Subject not found |
83-
+--------+----------------------------------+-------+------------------------------+
84-
| GET | /subjects/notfound/versions/[0-9]| 40401 | Subject not found |
85-
+--------+----------------------------------+-------+------------------------------+
86-
| GET | /subjects/notfound/versions/404 | 40402 | Version not found |
87-
+--------+----------------------------------+-------+------------------------------+
88-
| GET | /subjects/notfound/versions/422 | 42202 | Invalid version |
89-
+--------+----------------------------------+-------+------------------------------+
90-
| DELETE | /subjects/notfound | 40401 | Subject not found |
91-
+--------+----------------------------------+-------+------------------------------+
92-
| POST | /subjects/conflict/versions | 409* | Incompatible Schema |
93-
+--------+----------------------------------+-------+------------------------------+
94-
| POST | /subjects/invalid/versions | 42201 | Invalid Schema |
95-
+--------+----------------------------------+-------+------------------------------+
96-
| POST | /subjects/notfound | 40401 | Subject not found |
97-
+--------+----------------------------------+-------+------------------------------+
98-
| POST | /subjects/schemanotfound | 40403 | Schema not found |
99-
+--------+----------------------------------+-------+------------------------------+
100-
| DELETE | /subjects/notfound | 40401 | Subject not found |
101-
+--------+----------------------------------+-------+------------------------------+
102-
| DELETE | /subjects/notfound/versions/[0-9]| 40401 | Subject not found |
103-
+--------+----------------------------------+-------+------------------------------+
104-
| DELETE | /subjects/notfound/versions/404 | 40402 | Version not found |
105-
+--------+----------------------------------+-------+------------------------------+
106-
| DELETE | /subjects/notfound/versions/422 | 42202 | Invalid version |
107-
+--------+----------------------------------+-------+------------------------------+
108-
| GET | /config/notconfig | 40401 | Subject not found |
109-
+--------+----------------------------------+-------+------------------------------+
110-
| PUT | /config** | 42203 | Invalid compatibility level |
111-
+--------+----------------------------------+-------+------------------------------+
77+
+--------+-------------------------------------------------+-------+------------------------------+
78+
| Method | Request Path | Code | Description |
79+
+========+=================================================+=======+==============================+
80+
| GET | /schemas/ids/404 | 40403 | Schema not found |
81+
+--------+-------------------------------------------------+-------+------------------------------+
82+
| GET | /subjects/notfound/versions | 40401 | Subject not found |
83+
+--------+-------------------------------------------------+-------+------------------------------+
84+
| GET | /subjects/notfound/versions/[0-9] | 40401 | Subject not found |
85+
+--------+-------------------------------------------------+-------+------------------------------+
86+
| GET | /subjects/notfound/versions/404 | 40402 | Version not found |
87+
+--------+-------------------------------------------------+-------+------------------------------+
88+
| GET | /subjects/notfound/versions/422 | 42202 | Invalid version |
89+
+--------+-------------------------------------------------+-------+------------------------------+
90+
| DELETE | /subjects/notfound | 40401 | Subject not found |
91+
+--------+-------------------------------------------------+-------+------------------------------+
92+
| POST | /subjects/conflict/versions | 409* | Incompatible Schema |
93+
+--------+-------------------------------------------------+-------+------------------------------+
94+
| POST | /subjects/invalid/versions | 42201 | Invalid Schema |
95+
+--------+-------------------------------------------------+-------+------------------------------+
96+
| POST | /subjects/notfound | 40401 | Subject not found |
97+
+--------+-------------------------------------------------+-------+------------------------------+
98+
| POST | /subjects/schemanotfound | 40403 | Schema not found |
99+
+--------+-------------------------------------------------+-------+------------------------------+
100+
| DELETE | /subjects/notfound | 40401 | Subject not found |
101+
+--------+-------------------------------------------------+-------+------------------------------+
102+
| DELETE | /subjects/notfound/versions/[0-9] | 40401 | Subject not found |
103+
+--------+-------------------------------------------------+-------+------------------------------+
104+
| DELETE | /subjects/notfound/versions/404 | 40402 | Version not found |
105+
+--------+-------------------------------------------------+-------+------------------------------+
106+
| DELETE | /subjects/notfound/versions/422 | 42202 | Invalid version |
107+
+--------+-------------------------------------------------+-------+------------------------------+
108+
| GET | /config/notconfig | 40401 | Subject not found |
109+
+--------+-------------------------------------------------+-------+------------------------------+
110+
| PUT | /config** | 42203 | Invalid compatibility level |
111+
+--------+-------------------------------------------------+-------+------------------------------+
112+
| POST | /compatibility/subjects/notfound/versions/[0-9] | 40401 | Subject not found |
113+
+--------+-------------------------------------------------+-------+------------------------------+
114+
| POST | /compatibility/subjects/invalid/versions/[0-9] | 42201 | Invalid Schema |
115+
+--------+-------------------------------------------------+-------+------------------------------+
116+
| POST | /compatibility/subjects/notfound/versions/404 | 40402 | Version not found |
117+
+--------+-------------------------------------------------+-------+------------------------------+
118+
| POST | /compatibility/subjects/invalid/versions/bad | 42202 | Invalid version |
119+
+--------+-------------------------------------------------+-------+------------------------------+
112120
* POST /subjects/{}/versions does not follow the documented API error.
113121
** PUT /config reacts to a trigger in the body: - {"compatibility": "FULL"}
114122
@@ -127,6 +135,7 @@ class MockSchemaRegistryClient(SchemaRegistryClient):
127135
subjects = re.compile("/subjects/?(.*)$")
128136
subject_versions = re.compile("/subjects/(.*)/versions/?(.*)$")
129137
compatibility = re.compile("/config/?(.*)$")
138+
compatibility_subjects_versions = re.compile("/compatibility/subjects/(.*)/versions/?(.*)$")
130139

131140
# constants
132141
SCHEMA_ID = 47
@@ -168,6 +177,8 @@ def __init__(self, conf):
168177
json=self.delete_subject_version_callback)
169178
adapter.register_uri('POST', self.subject_versions,
170179
json=self.post_subject_version_callback)
180+
adapter.register_uri('POST', self.compatibility_subjects_versions,
181+
json=self.post_compatibility_subjects_versions_callback)
171182

172183
adapter.add_matcher(self._auth_matcher)
173184
self._rest_client.session.mount('http://', adapter)
@@ -348,6 +359,40 @@ def post_subject_version_callback(self, request, context):
348359
context.status_code = 200
349360
return {'id': self.SCHEMA_ID}
350361

362+
def post_compatibility_subjects_versions_callback(self, request, context):
363+
self.counter['POST'][request.path] += 1
364+
365+
path_match = re.match(self.compatibility_subjects_versions, request.path)
366+
subject = path_match.group(1)
367+
version = path_match.group(2)
368+
369+
if version == '422':
370+
context.status_code = 422
371+
return {'error_code': 42202,
372+
'message': 'Invalid version'}
373+
374+
if version == '404':
375+
context.status_code = 404
376+
return {'error_code': 40402,
377+
'message': "Version not found"}
378+
379+
if subject == 'conflict':
380+
context.status_code = 200
381+
return {'is_compatible': False}
382+
383+
if subject == 'notfound':
384+
context.status_code = 404
385+
return {'error_code': 40401,
386+
'message': 'Subject not found'}
387+
388+
if subject == 'invalid':
389+
context.status_code = 422
390+
return {'error_code': 42201,
391+
'message': "Invalid Schema"}
392+
393+
context.status_code = 200
394+
return {'is_compatible': True}
395+
351396

352397
@pytest.fixture(scope="package")
353398
def load_avsc():

tests/schema_registry/test_api_client.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,45 @@ def test_schema_equivilence(load_avsc):
376376
assert schema == schema2
377377
assert schema_str1.__eq__(schema_str2)
378378
assert schema_str1 == schema_str2
379+
380+
381+
@pytest.mark.parametrize(
382+
'subject_name,version,expected_compatibility',
383+
[
384+
('conflict', 'latest', False),
385+
('conflict', 1, False),
386+
('test-key', 'latest', True),
387+
('test-key', 1, True),
388+
]
389+
)
390+
def test_test_compatibility_no_error(
391+
mock_schema_registry, load_avsc, subject_name, version, expected_compatibility
392+
):
393+
conf = {'url': TEST_URL}
394+
sr = mock_schema_registry(conf)
395+
schema = Schema(load_avsc('basic_schema.avsc'), schema_type='AVRO')
396+
397+
is_compatible = sr.test_compatibility(subject_name, schema)
398+
assert is_compatible is expected_compatibility
399+
400+
401+
@pytest.mark.parametrize(
402+
'subject_name,version,match_str,status_code,error_code',
403+
[
404+
('notfound', 'latest', 'Subject not found', 404, 40401),
405+
('invalid', 'latest', 'Invalid Schema', 422, 42201),
406+
('invalid', '422', 'Invalid version', 422, 42202),
407+
('notfound', 404, 'Version not found', 404, 40402),
408+
]
409+
)
410+
def test_test_compatibility_with_error(
411+
mock_schema_registry, load_avsc, subject_name, version, match_str, status_code, error_code
412+
):
413+
conf = {'url': TEST_URL}
414+
sr = mock_schema_registry(conf)
415+
schema = Schema(load_avsc('basic_schema.avsc'), schema_type='AVRO')
416+
417+
with pytest.raises(SchemaRegistryError, match=match_str) as e:
418+
sr.test_compatibility(subject_name, schema, version)
419+
assert e.value.http_status_code == status_code
420+
assert e.value.error_code == error_code

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy