From 746de2e5485dc0cc28c84041cdd4058c5cf1468f Mon Sep 17 00:00:00 2001 From: Stefan Bogdan Date: Mon, 11 Jan 2021 17:34:19 +0100 Subject: [PATCH] add and change unittest for the new weaviate version (v1.0.0) for the schema module --- test/schema/schema_company.json | 125 ++---- test/schema/test_properties.py | 111 +++-- test/schema/test_schema.py | 763 ++++++++++++++------------------ test/schema/validate_schema.py | 318 ++++++------- 4 files changed, 555 insertions(+), 762 deletions(-) diff --git a/test/schema/schema_company.json b/test/schema/schema_company.json index 45cf21c80..f82099ddb 100644 --- a/test/schema/schema_company.json +++ b/test/schema/schema_company.json @@ -1,83 +1,46 @@ { - "actions": { - "classes": [], - "type": "action" - }, - "things": { - "@context": "", - "version": "0.2.0", - "type": "thing", - "name": "company", - "maintainer": "yourfriends@weaviate.com", - "classes": [ - { - "class": "Company", - "description": "A business that acts in the market", - "keywords": [], - "properties": [ - { - "name": "name", - "description": "The name under which the company is known", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "legalBody", - "description": "The legal body under which the company maintains its business", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "hasEmployee", - "description": "The employees of the company", - "dataType": [ - "Employee" - ], - "cardinality": "many", - "keywords": [] - } - ] - }, - { - "class": "Employee", - "description": "An employee of the company", - "keywords": [], - "properties": [ - { - "name": "name", - "description": "The name of the employee", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "job", - "description": "the job description of the employee", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "yearsInTheCompany", - "description": "The number of years this employee has worked in the company", - "dataType": [ - "int" - ], - "cardinality": "atMostOne", - "keywords": [] - } - ] - } - ] - } + "classes": [ + { + "class": "Company", + "description": "A business that acts in the market", + "properties": [ + { + "name": "name", + "description": "The name under which the company is known", + "dataType": ["text"] + }, + { + "name": "legalBody", + "description": "The legal body under which the company maintains its business", + "dataType": ["text"] + }, + { + "name": "hasEmployee", + "description": "The employees of the company", + "dataType": ["Employee"] + } + ] + }, + { + "class": "Employee", + "description": "An employee of the company", + "properties": [ + { + "name": "name", + "description": "The name of the employee", + "dataType": ["text"] + }, + { + "name": "job", + "description": "the job description of the employee", + "dataType": ["text"] + }, + { + "name": "yearsInTheCompany", + "description": "The number of years this employee has worked in the company", + "dataType": ["int"] + } + ] + } + ] } diff --git a/test/schema/test_properties.py b/test/schema/test_properties.py index e5e1d1158..3d69e7d45 100644 --- a/test/schema/test_properties.py +++ b/test/schema/test_properties.py @@ -1,107 +1,100 @@ import unittest import weaviate -from weaviate import SEMANTIC_TYPE_ACTIONS from weaviate.connect import REST_METHOD_POST, REST_METHOD_DELETE -from unittest.mock import Mock -from test.testing_util import replace_connection, add_run_rest_to_mock +from test.testing_util import replace_connection, add_run_rest_to_mock, Mock class TestCRUDProperty(unittest.TestCase): def test_create_bad_input(self): - w = weaviate.Client("http://localhorst:8080") + """ + Test create property exceptions. + """ + + client = weaviate.Client("http://localhorst:8080") test_prop = { "dataType": ["string"], - "cardinality": "atMostOne", "description": "my Property", - "vectorizePropertyName": True, + "moduleConfig" : { + "text2vec-contextionary": { + "vectorizePropertyName": True + } + }, # "name": "superProp", missing name - "index": True + "indexInverted": True } - try: - w.schema.property.create("Class", test_prop) - self.fail("No error") - except weaviate.SchemaValidationException: - pass + + with self.assertRaises(weaviate.SchemaValidationException): + client.schema.property.create("Class", test_prop) test_prop["name"] = "someName" - try: - w.schema.property.create(35, test_prop) - self.fail("No error") - except TypeError: - pass - try: - w.schema.property.create("Class", ["wrong", "type"]) - self.fail("No error") - except TypeError: - pass + with self.assertRaises(TypeError): + client.schema.property.create(35, test_prop) + with self.assertRaises(TypeError): + client.schema.property.create("Class", ["wrong", "type"]) def test_create(self): - w = weaviate.Client("http://localhorst:8080") + """ + Test create. + """ + + client = weaviate.Client("http://localhorst:8080") connection_mock = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock) - replace_connection(w, connection_mock) + replace_connection(client, connection_mock) test_prop = { "dataType": ["string"], - "cardinality": "atMostOne", "description": "my Property", - "vectorizePropertyName": True, + "moduleConfig" : { + "text2vec-contextionary": { + "vectorizePropertyName": True + } + }, "name": "superProp", - "index": True + "indexInverted": True } - w.schema.property.create("TestThing", test_prop) - w.schema.property.create("TestAction", test_prop, SEMANTIC_TYPE_ACTIONS) + client.schema.property.create("TestThing", test_prop) connection_mock.run_rest.assert_called() call_args_list = connection_mock.run_rest.call_args_list - call_args, call_kwargs = call_args_list[0] + call_args = call_args_list[0][0] - self.assertEqual("/schema/things/TestThing/properties", call_args[0]) + self.assertEqual("/schema/TestThing/properties", call_args[0]) self.assertEqual(REST_METHOD_POST, call_args[1]) self.assertEqual(test_prop, call_args[2]) - call_args, call_kwargs = call_args_list[1] + def test_delete_bad_input(self): + """ + Test create with bad input. + """ - self.assertEqual("/schema/actions/TestAction/properties", call_args[0]) - self.assertEqual(REST_METHOD_POST, call_args[1]) - self.assertEqual(test_prop, call_args[2]) + client = weaviate.Client("http://localhorst:8080") - def test_delete_bad_input(self): - w = weaviate.Client("http://localhorst:8080") - try: - w.schema.property._delete("Class", 4) - self.fail("No error") - except TypeError: - pass - try: - w.schema.property._delete(35, "prop") - self.fail("No error") - except TypeError: - pass + with self.assertRaises(TypeError): + client.schema.property._delete("Class", 4) + with self.assertRaises(TypeError): + client.schema.property._delete(35, "prop") def test_delete(self): - w = weaviate.Client("http://localhorst:8080") + """ + Test delete property. (currently not available) + """ + + client = weaviate.Client("http://localhorst:8080") connection_mock = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock) - replace_connection(w, connection_mock) + replace_connection(client, connection_mock) - w.schema.property._delete("ThingClass", "propUno") - w.schema.property._delete("ActionClass", "propDos", SEMANTIC_TYPE_ACTIONS) + client.schema.property._delete("ThingClass", "propUno") connection_mock.run_rest.assert_called() call_args_list = connection_mock.run_rest.call_args_list - call_args, call_kwargs = call_args_list[0] - - self.assertEqual("/schema/things/ThingClass/properties/propUno", call_args[0]) - self.assertEqual(REST_METHOD_DELETE, call_args[1]) - - call_args, call_kwargs = call_args_list[1] + call_args = call_args_list[0][0] - self.assertEqual("/schema/actions/ActionClass/properties/propDos", call_args[0]) + self.assertEqual("/schema/ThingClass/properties/propUno", call_args[0]) self.assertEqual(REST_METHOD_DELETE, call_args[1]) - diff --git a/test/schema/test_schema.py b/test/schema/test_schema.py index e5d35059b..5787f2187 100644 --- a/test/schema/test_schema.py +++ b/test/schema/test_schema.py @@ -1,397 +1,295 @@ import unittest -import weaviate - import copy import os -from test.testing_util import replace_connection, add_run_rest_to_mock +from unittest.mock import patch +import weaviate +from test.testing_util import replace_connection, add_run_rest_to_mock, Mock from weaviate.connect import REST_METHOD_POST, REST_METHOD_DELETE, REST_METHOD_GET -from weaviate import SEMANTIC_TYPE_ACTIONS -from unittest.mock import Mock from weaviate.exceptions import SchemaValidationException company_test_schema = { - "actions": { - "classes": [], - "type": "action" - }, - "things": { - "@context": "", - "version": "0.2.0", - "type": "thing", - "name": "company", - "maintainer": "yourfriends@weaviate.com", - "classes": [ - { - "class": "Company", - "description": "A business that acts in the market", - "keywords": [], - "properties": [ - { - "name": "name", - "description": "The name under which the company is known", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "legalBody", - "description": "The legal body under which the company maintains its business", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "hasEmployee", - "description": "The employees of the company", - "dataType": [ - "Employee" - ], - "cardinality": "many", - "keywords": [] - } - ] - }, - { - "class": "Employee", - "description": "An employee of the company", - "keywords": [], - "properties": [ - { - "name": "name", - "description": "The name of the employee", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "job", - "description": "the job description of the employee", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [] - }, - { - "name": "yearsInTheCompany", - "description": "The number of years this employee has worked in the company", - "dataType": [ - "int" - ], - "cardinality": "atMostOne", - "keywords": [] - } - ] - } + "classes": + [ + { + "class": "Company", + "description": "A business that acts in the market", + "properties": [ + { + "name": "name", + "description": "The name under which the company is known", + "dataType": ["text"], + }, + { + "name": "legalBody", + "description": "The legal body under which the company maintains its business", + "dataType": ["text"], + }, + { + "name": "hasEmployee", + "description": "The employees of the company", + "dataType": ["Employee"], + } + ] + }, + { + "class": "Employee", + "description": "An employee of the company", + "properties": [ + { + "name": "name", + "description": "The name of the employee", + "dataType": ["text"], + }, + { + "name": "job", + "description": "the job description of the employee", + "dataType": ["text"], + }, + { + "name": "yearsInTheCompany", + "description": "The number of years this employee has worked in the company", + "dataType": ["int"], + } + ] + } ] - } } # A test schema as it was returned from a real weaviate instance persons_return_test_schema = { - "actions": { - "classes": [], - "type": "action" - }, - "things": { - "classes": [ - { - "class": "Person", - "description": "A person such as humans or personality known through culture", - "properties": [ - { - "cardinality": "atMostOne", - "dataType": [ - "text" - ], - "description": "The name of this person", - "name": "name" - } - ] - }, - { - "class": "Group", - "description": "A set of persons who are associated with each other over some common properties", - "properties": [ - { - "cardinality": "atMostOne", - "dataType": [ - "text" - ], - "description": "The name under which this group is known", - "name": "name" - }, - { - "cardinality": "many", - "dataType": [ - "Person" - ], - "description": "The persons that are part of this group", - "name": "members" - } - ] - } - ], - "type": "thing" - } + "classes": [ + { + "class": "Person", + "description": "A person such as humans or personality known through culture", + "properties": [ + { + "dataType": ["text"], + "description": "The name of this person", + "name": "name" + } + ] + }, + { + "class": "Group", + "description": "A set of persons who are associated with each other over some common properties", + "properties": [ + { + "dataType": ["text"], + "description": "The name under which this group is known", + "name": "name" + }, + { + "dataType": ["Person"], + "description": "The persons that are part of this group", + "name": "members" + } + ] + } + ], } # Schema containing explicit index person_index_false_schema = { - "actions": { - "classes": [], - "type": "action" - }, - "things": { - "@context": "", - "version": "0.2.0", - "type": "thing", - "name": "people", - "maintainer": "yourfriends@weaviate.com", "classes": [ - { - "class": "Person", - "description": "A person such as humans or personality known through culture", - "keywords": [], - "properties": [ - { - "name": "name", - "description": "The name of this person", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [], - "index": False - } - ] - }, - { - "class": "Group", - "description": "A set of persons who are associated with each other over some common properties", - "keywords": [], - "properties": [ - { - "name": "name", - "description": "The name under which this group is known", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [], - "index": True - }, - { - "name": "members", - "description": "The persons that are part of this group", - "dataType": [ - "Person" - ], - "cardinality": "many" - } - ] - } + { + "class": "Person", + "description": "A person such as humans or personality known through culture", + "properties": [ + { + "name": "name", + "description": "The name of this person", + "dataType": ["text"], + "indexInverted": False + } + ] + }, + { + "class": "Group", + "description": "A set of persons who are associated with each other over some common properties", + "properties": [ + { + "name": "name", + "description": "The name under which this group is known", + "dataType": ["text"], + "indexInverted": True + }, + { + "name": "members", + "description": "The persons that are part of this group", + "dataType": ["Person"], + } + ] + } ] - } } - stop_vectorization_schema = { - "actions": { - "classes": [], - "type": "action" - }, - "things": { - "@context": "", - "version": "0.2.0", - "type": "thing", - "name": "data", - "maintainer": "yourfriends@weaviate.com", "classes": [ - { - "class": "DataType", - "description": "DataType", - "keywords": [], - "vectorizeClassName": False, - "properties": [ - { - "name": "owner", - "description": "the owner", - "dataType": [ - "text" - ], - "keywords": [], - "vectorizePropertyName": False, - "index": False - }, - { - "name": "complexDescription", - "description": "Description of the complex type", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [], - "vectorizePropertyName": False, - }, - { - "name": "hasPrimitives", - "description": "The primitive data points", - "dataType": [ - "Primitive" - ], - "cardinality": "many", - "keywords": [] - } - ] - }, - - { - "class": "Primitive", - "description": "DataType", - "keywords": [], - "vectorizeClassName": True, - "properties": [ - { - "name": "type", - "description": "the primitive type", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [], - } - ] - } + { + "class": "DataType", + "description": "DataType", + "moduleConfig": { + "text2vec-contextionary": { + "vectorizeClassName": False + } + }, + "properties": [ + { + "name": "owner", + "description": "the owner", + "dataType": ["text"], + "moduleConfig": { + "text2vec-contextionary": { + "vectorizePropertyName": False + } + }, + "indexInverted": False + }, + { + "name": "complexDescription", + "description": "Description of the complex type", + "dataType": ["text"], + "moduleConfig": { + "text2vec-contextionary": { + "vectorizePropertyName": False + } + } + }, + { + "name": "hasPrimitives", + "description": "The primitive data points", + "dataType": ["Primitive"], + } + ] + }, + { + "class": "Primitive", + "description": "DataType", + "moduleConfig": { + "text2vec-contextionary": { + "vectorizeClassName": True + } + }, + "properties": [ + { + "name": "type", + "description": "the primitive type", + "dataType": ["text"], + } + ] + } ] - } } class TestSchema(unittest.TestCase): + def test_create_schema_invalid_input(self): - w = weaviate.Client("http://localhost:8080") - try: - w.schema.create(None) - self.fail("No exception when no valid schema given") - except TypeError: - pass # Expected value error - try: - w.schema.create("/random/noFile") # No valid file or url - self.fail("No exception when no valid schema given") - except ValueError: - pass # Expected value error - try: - w.schema.create(42) # No valid type - self.fail("No exception when no valid schema given") - except TypeError: - pass # Expected value error - # Load from URL - - # @patch('weaviate.client._get_dict_from_object') - # def mock_get_dict_from_object(self, object_): - # return company_test_schema + """ + Test create schema exceptions. + """ + + client = weaviate.Client("http://localhost:8080") + # None value + with self.assertRaises(TypeError): + client.schema.create(None) + # invalid file + with self.assertRaises(ValueError): + client.schema.create("/random/noFile") + # invalid url + with self.assertRaises(ValueError): + client.schema.create("https://www.semi.technology/schema") + # wrong type + with self.assertRaises(TypeError): + client.schema.create(42) def test_create_schema_load_file(self): - w = weaviate.Client("http://localhost:8080") + """ + Test create schema. + """ + client = weaviate.Client("http://localhost:8080") + connection_mock_file = Mock() # Mock calling weaviate + add_run_rest_to_mock(connection_mock_file) + replace_connection(client, add_run_rest_to_mock(connection_mock_file)) # Replace connection with mock # Load from URL - # TODO ??? HOW TO PATCH THIS SHIT - # with patch('weaviate.util._get_dict_from_object') as mock_util: - # # Mock weaviate.client._get_dict_from_object the function where - # # it is looked up see https://docs.python.org/3/library/unittest.mock.html#where-to-patch - # # for more information - # - # connection_mock_url = Mock() # Mock weaviate.connection - # w._connection = connection_mock_url - # add_run_rest_to_mock(connection_mock_url) - # - # mock_util.return_value = company_test_schema - # - # w.schema.create("http://semi.technology/schema") - # mock_util.assert_called() - # connection_mock_url.run_rest.assert_called() - + with patch('weaviate.schema.crud_schema._get_dict_from_object') as mock_util: + mock_util.return_value = company_test_schema + self.assertIsNone(client.schema.create("http://semi.technology/schema")) # Load from file connection_mock_file = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock_file) - replace_connection(w, add_run_rest_to_mock(connection_mock_file)) # Replace connection with mock + replace_connection(client, add_run_rest_to_mock(connection_mock_file)) # Replace connection with mock current_dir = os.path.dirname(__file__) schema_json_file = os.path.join(current_dir, "schema_company.json") - w.schema.create(schema_json_file) # Load from file + client.schema.create(schema_json_file) # Load from file connection_mock_file.run_rest.assert_called() # See if mock has been called # Load dict connection_mock_dict = Mock() # Replace mock add_run_rest_to_mock(connection_mock_dict) - replace_connection(w, add_run_rest_to_mock(connection_mock_dict)) - w.schema.create(company_test_schema) + replace_connection(client, add_run_rest_to_mock(connection_mock_dict)) + client.schema.create(company_test_schema) connection_mock_dict.run_rest.assert_called() - # Test schema missing actions/schema - # Mock run_rest - connection_mock = Mock() - replace_connection(w, add_run_rest_to_mock(connection_mock)) - schema = copy.deepcopy(company_test_schema) - # Remove actions - del schema[weaviate.SEMANTIC_TYPE_ACTIONS] - w.schema.create(company_test_schema) - - schema = copy.deepcopy(company_test_schema) - del schema[weaviate.SEMANTIC_TYPE_THINGS] - w.schema.create(company_test_schema) - connection_mock.run_rest.assert_called() - def test_run_rest_failed(self): - w = weaviate.Client("http://localhost:8080") + """ + test run_rest Failed. + """ + + client = weaviate.Client("http://localhost:8080") connection_mock = Mock() add_run_rest_to_mock(connection_mock, return_json={"Test error"}, status_code=500) - replace_connection(w, connection_mock) + replace_connection(client, connection_mock) - try: - w.schema.create(company_test_schema) - except weaviate.UnexpectedStatusCodeException: - pass # Expected exception + with self.assertRaises(weaviate.UnexpectedStatusCodeException): + client.schema.create(company_test_schema) def test_get_schema(self): - w = weaviate.Client("http://localhost:8080") + """ + Test schema.get + """ + + client = weaviate.Client("http://localhost:8080") connection_mock_file = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock_file, persons_return_test_schema) - replace_connection(w, connection_mock_file) # Replace connection with mock + replace_connection(client, connection_mock_file) # Replace connection with mock - schema = w.schema.get() + schema = client.schema.get() connection_mock_file.run_rest.assert_called() # See if mock has been called - self.assertTrue("things" in schema) - self.assertEqual(len(schema["things"]["classes"]), 2) + self.assertTrue("classes" in schema) + self.assertEqual(len(schema["classes"]), 2) def test_create_schema_with_explicit_index(self): - w = weaviate.Client("http://localhost:8080") + """ + Test create schema with explicit indexInverted. + """ + + client = weaviate.Client("http://localhost:8080") connection_mock_dict = Mock() # Replace mock add_run_rest_to_mock(connection_mock_dict) - replace_connection(w, connection_mock_dict) - w.schema.create(person_index_false_schema) + replace_connection(client, connection_mock_dict) + client.schema.create(person_index_false_schema) connection_mock_dict.run_rest.assert_called() def test_not_indexed_class_name(self): - w = weaviate.Client("http://localhost:8080") + """ + Test un-indexed class name. + """ + + client = weaviate.Client("http://localhost:8080") connection_mock_dict = Mock() # Replace mock add_run_rest_to_mock(connection_mock_dict) - replace_connection(w, connection_mock_dict) - w.schema.create(stop_vectorization_schema) + replace_connection(client, connection_mock_dict) + client.schema.create(stop_vectorization_schema) connection_mock_dict.run_rest.assert_called() def test_invalid_schema(self): @@ -409,169 +307,174 @@ def test_invalid_schema(self): } ] } - w = weaviate.Client("http://localhost:1234") - try: - w.schema.create(schema) - self.fail("Expected SchemaValidationException") - except weaviate.SchemaValidationException: - pass + client = weaviate.Client("http://localhost:1234") + with self.assertRaises(weaviate.SchemaValidationException): + client.schema.create(schema) class TestContainsSchema(unittest.TestCase): def test_contains_a_schema(self): + """ + Test weaviate.schema.contains any schema. + """ + # If a schema is present it should return true otherwise false # 1. test schema is present: - w = weaviate.Client("http://localhost:8080") + client = weaviate.Client("http://localhost:8080") connection_mock_file = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock_file, persons_return_test_schema) - replace_connection(w, connection_mock_file) + replace_connection(client, connection_mock_file) - self.assertTrue(w.schema.contains()) + self.assertTrue(client.schema.contains()) # 2. test no schema is present: - w = weaviate.Client("http://localhost:8080") + client = weaviate.Client("http://localhost:8080") connection_mock_file = Mock() # Mock calling weaviate - empty_schema = {"actions":{"classes":[],"type":"action"},"things":{"classes":[],"type":"thing"}} + empty_schema = {"classes": []} add_run_rest_to_mock(connection_mock_file, empty_schema) - replace_connection(w, connection_mock_file) + replace_connection(client, connection_mock_file) - self.assertFalse(w.schema.contains()) + self.assertFalse(client.schema.contains()) def test_contains_specific_schema(self): - w = weaviate.Client("http://localhost:8080") + """ + Test weaviate.schema.contains specific schema. + """ + + client = weaviate.Client("http://localhost:8080") connection_mock_file = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock_file, persons_return_test_schema) - replace_connection(w, connection_mock_file) - self.assertFalse(w.schema.contains(company_test_schema)) + replace_connection(client, connection_mock_file) + self.assertFalse(client.schema.contains(company_test_schema)) subset_schema = { - "things": { - "classes": [ - { - "class": "Person", - "description": "", - "properties": [{ - "cardinality": "atMostOne", - "dataType": ["text"], - "description": "", - "name": "name" - } - ] - } - ] - } + "classes": [ + { + "class": "Person", + "description": "", + "properties": [ + { + "dataType": ["text"], + "description": "", + "name": "name" + } + ] + } + ] } - self.assertTrue(w.schema.contains(subset_schema)) + self.assertTrue(client.schema.contains(subset_schema)) def test_contains_specific_schema_from_file(self): - w = weaviate.Client("http://localhost:8080") + """ + Test weaviate.schema.contains schema from file. + """ + + client = weaviate.Client("http://localhost:8080") connection_mock_file = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock_file, persons_return_test_schema) - replace_connection(w, connection_mock_file) + replace_connection(client, connection_mock_file) current_dir = os.path.dirname(__file__) schema_json_file = os.path.join(current_dir, "schema_company.json") - self.assertFalse(w.schema.contains(schema_json_file)) + self.assertFalse(client.schema.contains(schema_json_file)) connection_mock_file = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock_file, company_test_schema) - replace_connection(w, connection_mock_file) + replace_connection(client, connection_mock_file) - self.assertTrue(w.schema.contains(schema_json_file)) + self.assertTrue(client.schema.contains(schema_json_file)) class TestCreate(unittest.TestCase): def test_create_single_class(self): + """ + Test create single class. + """ + group_class = { "class": "Group", "description": "A set of persons who are associated with each other over some common properties", - "keywords": [], "properties": [ { "name": "name", "description": "The name under which this group is known", - "dataType": [ - "text" - ], - "cardinality": "atMostOne", - "keywords": [], - "index": True + "dataType": ["text"], + "indexInverted": True }, { "name": "members", "description": "The persons that are part of this group", - "dataType": [ - "Person" - ], - "cardinality": "many" + "dataType": ["Person"], } ] } - w = weaviate.Client("http://localhost:8080") + client = weaviate.Client("http://localhost:8080") connection_mock = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock) - replace_connection(w, connection_mock) + replace_connection(client, connection_mock) - w.schema.create_class(group_class) - w.schema.create_class(group_class, SEMANTIC_TYPE_ACTIONS) + self.assertIsNone(client.schema.create_class(group_class)) connection_mock.run_rest.assert_called() call_args_list = connection_mock.run_rest.call_args_list - call_args, call_kwargs = call_args_list[0] - self.assertEqual("/schema/things", call_args[0]) + call_args = call_args_list[0][0] + + self.assertEqual("/schema", call_args[0]) self.assertEqual(REST_METHOD_POST, call_args[1]) created_class = call_args[2] self.assertEqual("Group", created_class["class"]) self.assertEqual(1, len(created_class["properties"])) - call_args, call_kwargs = call_args_list[1] - self.assertEqual("/schema/things/Group/properties", call_args[0]) + call_args = call_args_list[1][0] + self.assertEqual("/schema/Group/properties", call_args[0]) self.assertEqual(REST_METHOD_POST, call_args[1]) created_property = call_args[2] self.assertEqual(["Person"], created_property["dataType"]) - call_args, call_kwargs = call_args_list[2] - self.assertEqual("/schema/actions", call_args[0]) - self.assertEqual(REST_METHOD_POST, call_args[1]) def test_create_minimal_class(self): - w = weaviate.Client("http://localhost:8080") + """ + Test create minimal class. + """ + + client = weaviate.Client("http://localhost:8080") connection_mock = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock) - replace_connection(w, connection_mock) + replace_connection(client, connection_mock) - w.schema.create_class({"class": "Group"}) + client.schema.create_class({"class": "Group"}) connection_mock.run_rest.assert_called() call_args_list = connection_mock.run_rest.call_args_list - call_args, call_kwargs = call_args_list[0] + call_args = call_args_list[0][0] - self.assertEqual("/schema/things", call_args[0]) + self.assertEqual("/schema", call_args[0]) self.assertEqual(REST_METHOD_POST, call_args[1]) self.assertEqual("Group", call_args[2]["class"]) def test_input(self): - w = weaviate.Client("http://localhorst:8080") + """ + Test input. + """ + + client = weaviate.Client("http://localhorst:8080") invalid_class = { "class": "Group", "description": "A set of persons who are associated with each other over some common properties", - "keywords": [], "properties": [ { "name": "name", "description": "The name under which this group is known", - "cardinality": "atMostOne", - "keywords": [], - "index": True + "indexInverted": True }, { "name": "members", @@ -579,56 +482,51 @@ def test_input(self): "dataType": [ "Person" ], - "cardinality": "many" } ] } - try: - w.schema.create_class(invalid_class) - self.fail("Expected exception") - except SchemaValidationException: - pass + with self.assertRaises(SchemaValidationException): + client.schema.create_class(invalid_class) class TestDelete(unittest.TestCase): def test_delete_class_input(self): - w = weaviate.Client("http://localhost:8080") - try: - w.schema.delete_class(1) - self.fail("Expected error") - except TypeError: - pass - try: - w.schema.delete_class("a", 1) - self.fail("Expected error") - except TypeError: - pass + """ + Test delete class input exceptions. + """ + client = weaviate.Client("http://localhost:8080") + with self.assertRaises(TypeError): + client.schema.delete_class(1) + with self.assertRaises(TypeError): + client.schema.delete_class("a", 1) def test_delete_class(self): - w = weaviate.Client("http://localhorst:8080") + """ + Test delete class. + """ + + client = weaviate.Client("http://localhorst:8080") connection_mock = Mock() # Mock calling weaviate add_run_rest_to_mock(connection_mock) - replace_connection(w, connection_mock) + replace_connection(client, connection_mock) - w.schema.delete_class("Poverty") - w.schema.delete_class("Poverty", SEMANTIC_TYPE_ACTIONS) + self.assertIsNone(client.schema.delete_class("Poverty")) connection_mock.run_rest.assert_called() call_args_list = connection_mock.run_rest.call_args_list - call_args, call_kwargs = call_args_list[0] - - self.assertEqual("/schema/things/Poverty", call_args[0]) - self.assertEqual(REST_METHOD_DELETE, call_args[1]) - - call_args, call_kwargs = call_args_list[1] + call_args = call_args_list[0][0] - self.assertEqual("/schema/actions/Poverty", call_args[0]) + self.assertEqual("/schema/Poverty", call_args[0]) self.assertEqual(REST_METHOD_DELETE, call_args[1]) def test_delete_everything(self): + """ + Test delete everything. + """ + # First request get schema return_value_mock_get_schema = Mock() return_value_mock_get_schema.json.return_value = company_test_schema @@ -644,34 +542,33 @@ def test_delete_everything(self): connection_mock = Mock() # Mock calling weaviate #connection_mock.run_rest.return_value = [return_value_mock, return_value_mock2] - connection_mock.run_rest.side_effect = [return_value_mock_get_schema, return_value_mock_delete_class_1, return_value_mock_delete_class_2] + connection_mock.run_rest.side_effect = [ + return_value_mock_get_schema, + return_value_mock_delete_class_1, + return_value_mock_delete_class_2] - w = weaviate.Client("http://localhost:2121") - replace_connection(w, connection_mock) + client = weaviate.Client("http://localhost:2121") + replace_connection(client, connection_mock) - w.schema.delete_all() + client.schema.delete_all() connection_mock.run_rest.assert_called() call_args_list = connection_mock.run_rest.call_args_list # Check if schema was retrieved - call_args, call_kwargs = call_args_list[0] + call_args = call_args_list[0][0] self.assertEqual("/schema", call_args[0]) self.assertEqual(REST_METHOD_GET, call_args[1]) # Check if class 1 was deleted - call_args, call_kwargs = call_args_list[1] + call_args = call_args_list[1][0] - self.assertEqual("/schema/things/Company", call_args[0]) + self.assertEqual("/schema/Company", call_args[0]) self.assertEqual(REST_METHOD_DELETE, call_args[1]) # Check if class 2 was deleted - call_args, call_kwargs = call_args_list[2] + call_args = call_args_list[2][0] - self.assertEqual("/schema/things/Employee", call_args[0]) + self.assertEqual("/schema/Employee", call_args[0]) self.assertEqual(REST_METHOD_DELETE, call_args[1]) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/schema/validate_schema.py b/test/schema/validate_schema.py index 921661971..707037413 100644 --- a/test/schema/validate_schema.py +++ b/test/schema/validate_schema.py @@ -1,208 +1,148 @@ import unittest -from weaviate.schema.validate_schema import validate_schema, \ - _check_schema_class_types, check_class, check_property +from weaviate.schema.validate_schema import validate_schema, check_class, check_property from weaviate.exceptions import SchemaValidationException class TestSchemaValidation(unittest.TestCase): - def test_actions_and_things(self): - schema_things = {"things": {"classes": []}} - schema_actions = {"actions": {"classes": []}} - schema_both = {"actions": {"classes": []}, - "things": {"classes": []}} - schema_none = {} - schema_wrong = {"thinks": {"classes": []}} - schema_too_much = {"actions": {"classes": []}, - "things": {"classes": []}, - "memories": {"classes": []}} + def test_schema_validation(self): + """ + Test schema validation + """ + schema_valid = {"classes": []} + schema_invalid_0 = {"classes": "my_class"} + schema_invalid_1 = {"things": {"classes": []}} + schema_invalid_2 = {"classes": [], "things" : []} + schema_none = {} - self.assertIsNone(validate_schema(schema_things)) - self.assertIsNone(validate_schema(schema_actions)) - self.assertIsNone(validate_schema(schema_both)) - try: + self.assertIsNone(validate_schema(schema_valid)) + with self.assertRaises(SchemaValidationException): validate_schema(schema_none) - self.fail("expected exception") - except SchemaValidationException: - pass - try: - validate_schema(schema_too_much) - self.fail("expected exception") - except SchemaValidationException: - pass - try: - validate_schema(schema_wrong) - self.fail("expected exception") - except SchemaValidationException: - pass - - def test_check_schema_class_types(self): - with_classes = {"classes": []} - no_classes = {"random": "field"} - with_classes_and_others = {"@context": "", - "version": "0.2.0", - "type": "thing", - "name": "people", - "maintainer": "yourfriends@weaviate.com", - "classes": []} - self.assertIsNone(_check_schema_class_types("things", with_classes)) - self.assertIsNone(_check_schema_class_types("things", with_classes_and_others)) - try: - _check_schema_class_types("things", no_classes) - self.fail("expected exception") - except SchemaValidationException: - pass - # test classes not a list - try: - _check_schema_class_types("things", {"classes": "a"}) - self.fail("expected exception") - except SchemaValidationException: - pass + with self.assertRaises(SchemaValidationException): + validate_schema(schema_invalid_0) + with self.assertRaises(SchemaValidationException): + validate_schema(schema_invalid_1) + with self.assertRaises(SchemaValidationException): + validate_schema(schema_invalid_2) def test_check_class(self): - # minimal must contain class key as string - check_class({"class": "Car"}) - try: - # wrong type - check_class({"class": []}) - self.fail() - except SchemaValidationException: - pass + """ + Test check_class. + """ # Valid maximal schema - max_valid = {"class": "Boat", - "description": "boat swiming on the water", - "vectorizeClassName": True, - "keywords": [], - "properties": []} - - check_class(max_valid) - - try: - # unknown key - max_valid["random"] = "field" - check_class(max_valid) - self.fail() - except SchemaValidationException: - pass + max_valid = { + "class": "Boat", + "description": "boat swiming on the water", + "properties": [], + "vectorIndexType": "hnsw", + "vectorIndexConfig": {}, + "moduleConfig": {}, + "vectorizer": "text2vec-contextionary", + } + self.assertIsNone(check_class(max_valid)) + # minimal must contain class key as string + self.assertIsNone(check_class({"class": "Car"})) - # Check data types optional fields - try: - check_class({"class": "Tree", - "description": []}) - self.fail() - except SchemaValidationException: - pass - try: - check_class({"class": "Tree", - "vectorizeClassName": "yes"}) - self.fail() - except SchemaValidationException: - pass - try: - check_class({"class": "Tree", - "properties": "References please"}) - self.fail() - except SchemaValidationException: - pass + # wrong type + with self.assertRaises(SchemaValidationException): + class_ = {"class" : []} + check_class({ + "class" : [], + "invalid_key": "value" + }) + with self.assertRaises(SchemaValidationException): + check_class({ + "class": "Tree", + "description": [] + }) + with self.assertRaises(SchemaValidationException): + check_class({ + "class": "Tree", + "properties": "References please" + }) + with self.assertRaises(SchemaValidationException): + check_class({ + "class": "Tree", + "vectorIndexType": True + }) + with self.assertRaises(SchemaValidationException): + check_class({ + "class": "Tree", + "vectorIndexConfig": [] + }) + with self.assertRaises(SchemaValidationException): + check_class({ + "class": "Tree", + "moduleConfig": [] + }) + with self.assertRaises(SchemaValidationException): + check_class({ + "class": "Tree", + "vectorizer": 100.1 + }) def test_check_property(self): + """ + Test check_property. + """ + valid_minimal = {"dataType": ["string"], "name": "string"} - check_property(valid_minimal) - valid_max = {"dataType": ["string"], - "name": "Rocket", - "vectorizePropertyName": True, - "keywords": [], - "cardinality": "many", - "description": "some description", - "index": True} - check_property(valid_max) - try: - # unknown field - valid_minimal["random"] = "field" - check_property(valid_minimal) - self.fail() - except SchemaValidationException: - pass - # Wrong data types: - try: - check_property({"dataType": "not list", - "name": "Rocket", - "vectorizePropertyName": True, - "keywords": [], - "cardinality": "many", - "description": "some description", - "index": True}) - self.fail() - except SchemaValidationException: - pass - try: - check_property({"dataType": ["string"], - "name": 12, - "vectorizePropertyName": True, - "keywords": [], - "cardinality": "many", - "description": "some description", - "index": True}) - self.fail() - except SchemaValidationException: - pass - try: - check_property({"dataType": ["string"], - "name": "Rocket", - "vectorizePropertyName": "Yes", - "keywords": [], - "cardinality": "many", - "description": "some description", - "index": True}) - self.fail() - except SchemaValidationException: - pass - try: - check_property({"dataType": ["string"], - "name": "Rocket", - "vectorizePropertyName": True, - "keywords": [], - "cardinality": 1, - "description": "some description", - "index": True}) - self.fail() - except SchemaValidationException: - pass - try: - check_property({"dataType": ["string"], - "name": "Rocket", - "vectorizePropertyName": True, - "keywords": [], - "cardinality": "many", - "description": 3, - "index": True}) - self.fail() - except SchemaValidationException: - pass - try: - check_property({"dataType": ["string"], - "name": "Rocket", - "vectorizePropertyName": True, - "keywords": [], - "cardinality": "many", - "description": "some description", - "index": "Yes"}) - self.fail() - except SchemaValidationException: - pass - # Wrong cardinality - try: - check_property({"dataType": ["string"], - "name": "Rocket", - "vectorizePropertyName": True, - "keywords": [], - "cardinality": "aLot", - "description": "some description", - "index": True}) - self.fail() - except SchemaValidationException: - pass + self.assertIsNone(check_property(valid_minimal)) + valid_max = { + "dataType": ["string"], + "name": "Rocket", + "moduleConfig": {}, + "description": "some description", + "indexInverted": True + } + self.assertIsNone(check_property(valid_max)) + + with self.assertRaises(SchemaValidationException): + properties = { + "dataType": ["string"] + } + check_property(properties) + with self.assertRaises(SchemaValidationException): + properties = { + "name": "string" + } + check_property(properties) + with self.assertRaises(SchemaValidationException): + properties = { + "dataType": ["string"], + "name": "string", + "invalid_property": "value" + } + check_property(properties) + with self.assertRaises(SchemaValidationException): + properties = { + "dataType": ["string"], + "name": "Rocket", + "moduleConfig": [], + } + check_property(properties) + with self.assertRaises(SchemaValidationException): + properties = { + "dataType": ["string"], + "name": "Rocket", + "description": ["some description"], + } + check_property(properties) + with self.assertRaises(SchemaValidationException): + properties = { + "dataType": ["string"], + "name": "Rocket", + "indexInverted": "True" + } + check_property(properties) + with self.assertRaises(SchemaValidationException): + properties = { + "dataType": ["string", 10], + "name": "Rocket", + "indexInverted": "True" + } + check_property(properties)