/tests/views/compatibility_test.py

https://github.com/Yelp/schematizer · Python · 174 lines · 123 code · 34 blank · 17 comment · 5 complexity · 3359a3d63ee30f3bd76a6a9fccb1bce8 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 Yelp Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing,
  11. # software distributed under the License is distributed on an
  12. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. # KIND, either express or implied. See the License for the
  14. # specific language governing permissions and limitations
  15. # under the License.
  16. from __future__ import absolute_import
  17. from __future__ import unicode_literals
  18. import copy
  19. import pytest
  20. import simplejson
  21. from schematizer.views import compatibility as compatibility_views
  22. from tests.views.api_test_base import ApiTestBase
  23. @pytest.mark.usefixtures("biz_schema")
  24. class TestAvroSchemaCompatibility(ApiTestBase):
  25. @pytest.fixture
  26. def new_biz_schema_json(self, biz_schema_json):
  27. new_schema = copy.deepcopy(biz_schema_json)
  28. new_schema['fields'].append(
  29. {'type': 'int', 'name': 'bar', 'default': 10, 'doc': 'bar'}
  30. )
  31. return new_schema
  32. @pytest.fixture
  33. def request_json(self, biz_schema, new_biz_schema_json):
  34. return {
  35. "schema": simplejson.dumps(new_biz_schema_json),
  36. "namespace": biz_schema.topic.source.namespace.name,
  37. "source": biz_schema.topic.source.name
  38. }
  39. def test_compatible(self, mock_request, request_json):
  40. mock_request.json_body = request_json
  41. actual = compatibility_views.is_avro_schema_compatible(mock_request)
  42. assert actual is True
  43. def test_incompatible_schema(
  44. self,
  45. mock_request,
  46. request_json,
  47. biz_schema_json
  48. ):
  49. # construct the new schema by changing the type of one field of the
  50. # existing schema from int to string, which is an incompatible change
  51. new_schema_json = copy.deepcopy(biz_schema_json)
  52. new_schema_json['fields'][-1]['type'] = 'string'
  53. request_json['schema'] = simplejson.dumps(new_schema_json)
  54. mock_request.json_body = request_json
  55. actual = compatibility_views.is_avro_schema_compatible(mock_request)
  56. assert actual is False
  57. def test_invalid_json_exception(self, mock_request, request_json):
  58. request_json['schema'] = 'Not valid json!%#!#$#'
  59. mock_request.json_body = request_json
  60. expected_exception = self.get_http_exception(422)
  61. with pytest.raises(expected_exception) as e:
  62. compatibility_views.is_avro_schema_compatible(mock_request)
  63. assert e.value.code == expected_exception.code
  64. assert str(e.value) == (
  65. 'Error "Expecting value: line 1 column 1 (char 0)" encountered '
  66. 'decoding JSON: "Not valid json!%#!#$#"'
  67. )
  68. def test_invalid_avro_schema(self, mock_request, request_json):
  69. request_json['schema'] = '{"type": "record", "name": "A"}'
  70. mock_request.json_body = request_json
  71. expected_exception = self.get_http_exception(422)
  72. with pytest.raises(expected_exception) as e:
  73. compatibility_views.is_avro_schema_compatible(mock_request)
  74. assert e.value.code == expected_exception.code
  75. assert str(e.value) == (
  76. 'Record schema requires a non-empty fields property.'
  77. )
  78. class TestMySQLSchemaCompatibility(ApiTestBase):
  79. @property
  80. def new_create_table_stmt(self):
  81. return ("create table `biz` ("
  82. "`id` int(11) not null, "
  83. "`x` varchar(8) default '');")
  84. @property
  85. def old_create_table_stmt(self):
  86. return 'create table `biz` (`id` int(11) not null);'
  87. @property
  88. def alter_table_stmt(self):
  89. return "alter table `biz` add column `x` varchar(8) default '';"
  90. @pytest.fixture
  91. def request_json(self, biz_schema):
  92. return {
  93. "new_create_table_stmt": self.new_create_table_stmt,
  94. "namespace": biz_schema.topic.source.namespace.name,
  95. "source": biz_schema.topic.source.name
  96. }
  97. def test_compatible_new_table(self, mock_request, request_json):
  98. mock_request.json_body = request_json
  99. actual = compatibility_views.is_mysql_schema_compatible(mock_request)
  100. assert actual is True
  101. def test_incompatible_new_table(self, mock_request, request_json):
  102. request_json["new_create_table_stmt"] = (
  103. 'create table `biz` (`id` char(10));'
  104. )
  105. mock_request.json_body = request_json
  106. actual = compatibility_views.is_mysql_schema_compatible(mock_request)
  107. assert actual is False
  108. def test_compatible_updated_table(self, mock_request, request_json):
  109. request_json["old_create_table_stmt"] = self.old_create_table_stmt
  110. request_json["alter_table_stmt"] = self.alter_table_stmt
  111. mock_request.json_body = request_json
  112. actual = compatibility_views.is_mysql_schema_compatible(mock_request)
  113. assert actual is True
  114. def test_invalid_sql_table_stmt(self, mock_request, request_json):
  115. request_json["new_create_table_stmt"] = 'create table biz ();'
  116. mock_request.json_body = request_json
  117. expected_exception = self.get_http_exception(422)
  118. with pytest.raises(expected_exception) as e:
  119. compatibility_views.is_mysql_schema_compatible(mock_request)
  120. assert e.value.code == expected_exception.code
  121. assert 'No column exists in the table.' in str(e.value)
  122. def test_unsupported_avro_type(self, mock_request, request_json):
  123. request_json["new_create_table_stmt"] = ('create table dummy '
  124. '(foo bar);')
  125. mock_request.json_body = request_json
  126. expected_exception = self.get_http_exception(422)
  127. with pytest.raises(expected_exception) as e:
  128. compatibility_views.is_mysql_schema_compatible(mock_request)
  129. assert e.value.code == expected_exception.code
  130. assert 'Unknown MySQL column type' in str(e.value)
  131. def test_invalid_request(self, mock_request, request_json):
  132. request_json["old_create_table_stmt"] = self.old_create_table_stmt
  133. mock_request.json_body = request_json
  134. expected_exception = self.get_http_exception(400)
  135. with pytest.raises(expected_exception) as e:
  136. compatibility_views.is_mysql_schema_compatible(mock_request)
  137. assert expected_exception.code == e.value.code
  138. assert str(e.value) == (
  139. 'Both old_create_table_stmt and alter_table_stmt must be provided.'
  140. )