def test_tablename(): """ GIVEN schemas with schema WHEN model_factory is called with the name of the schema THEN a model where __tablename__ has been set to the x-tablename value. """ schemas = { "SingleProperty": { "x-tablename": "table 1", "type": "object", "properties": {"property_1": {"type": "integer"}}, } } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="SingleProperty", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts, ) assert model.__tablename__ == "table 1"
def test_multiple_property(): """ GIVEN schemas with schema that has multiple item properties key WHEN model_factory is called with the name of the schema THEN a model with the properties is returned. """ schemas = { "SingleProperty": { "x-tablename": "table 1", "type": "object", "properties": { "property_1": {"type": "integer"}, "property_2": {"type": "integer"}, }, } } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="SingleProperty", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts, ) assert hasattr(model, "property_1") assert hasattr(model, "property_2")
def test_kwargs(): """ GIVEN schemas with schema that has kwargs WHEN model_factory is called with the name of the schema THEN a model with the kwargs is returned. """ schemas = { "SingleProperty": { "x-tablename": "table 1", "type": "object", "properties": {"property_1": {"type": "integer"}}, "x-kwargs": {"__mapper_args__": {"passive_deletes": True}}, } } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="SingleProperty", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts, ) assert model.__mapper_args__ == {"passive_deletes": True}
def test_all_of(): """ GIVEN schemas with schema that has allOf and the referenced schema WHEN model_factory is called with the name of the schema THEN a model with the property and tablename is returned. """ schemas = { "Schema": { "allOf": [ { "x-tablename": "table 1", "type": "object", "properties": {"property_1": {"type": "integer"}}, } ] } } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="Schema", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts ) assert hasattr(model, "property_1") assert model.__tablename__ == "table 1"
def test_table_args_index(): """ GIVEN schemas with schema that has a composite index WHEN model_factory is called with the name of the schema THEN a model with a composite index is returned. """ schemas = { "SingleProperty": { "x-tablename": "table 1", "type": "object", "properties": {"property_1": {"type": "integer"}}, "x-composite-index": ["property_1"], } } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="SingleProperty", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts, ) (index,) = model.__table_args__ assert isinstance(index, sql_schema.Index)
def test_inherits(): """ GIVEN schemas with schema that inherits WHEN model_factory is called with the name of the schema THEN a model which inherits from the parent and with only the child properties defined is returned. """ schemas = { "Child": { "allOf": [ { "x-inherits": True, "type": "object", "properties": {"property_2": {"type": "integer"}}, }, {"$ref": "#/components/schemas/Parent"}, ] }, "Parent": { "x-tablename": "parent", "type": "object", "properties": {"property_1": {"type": "string"}}, }, } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="Child", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts ) assert not hasattr(model, "property_1") assert hasattr(model, "property_2") assert getattr(model, "__tablename__", None) is None
def test_schema_name(): """ GIVEN schemas with schema that has a custom name WHEN model_factory is called with the name of the schema THEN a model with a custom schema name is returned. """ schemas = { "SchemaName": { "x-tablename": "table 1", "x-schema-name": "schema 1", "type": "object", "properties": {"property_1": {"type": "integer"}}, } } artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="SchemaName", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts, ) assert model.__table_args__[0]["schema"] == "schema 1"
def test_single_property_column_factory_call(mocked_column_factory: mock.MagicMock): """ GIVEN mocked column_factory and schemas with schema that has single item properties key and does not have the required key WHEN model_factory is called with the name of the schema THEN column_factory is called with required as None. """ model_schema = { "x-tablename": "table 1", "type": "object", "properties": {"id": {"type": "integer"}}, } model_name = "SingleProperty" schemas = {model_name: model_schema} artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model_factory.model_factory( name=model_name, get_base=_mock_get_base, schemas=copy.deepcopy(schemas), artifacts=artifacts, ) mocked_column_factory.assert_called_once_with( artifacts=artifacts[model_name].properties[0][1] )
def test_model_database_type_simple_json(engine, sessionmaker, type_, value): """ GIVEN JSON type WHEN a specification is written for the combination and a model created and initialized with the value THEN the queried value complies with the type calculated by type_.model. """ spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True, "x-autoincrement": True, }, "column": {"type": type_, "x-json": True}, }, "x-tablename": "table", "type": "object", "required": ["column"], } } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") # Calculate the expected type schemas_artifacts = schemas_artifacts_module.get_from_schemas( schemas=spec["components"]["schemas"], stay_within_model=False ) assert "Table" in schemas_artifacts model_schemas_artifacts = schemas_artifacts["Table"] model_models_artifacts = artifacts.calculate( artifacts=model_schemas_artifacts, name="Table" ) assert len(model_models_artifacts.sqlalchemy.columns) == 2 column_column_artifacts = model_models_artifacts.sqlalchemy.columns[1] assert column_column_artifacts.name == "column" calculated_type_str = column_column_artifacts.type calculated_type = eval(calculated_type_str) # pylint: disable=eval-used # Creating models base.metadata.create_all(engine) # Creating model instance model_instance = model(column=value) session = sessionmaker() session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.column == value typeguard.check_type("queried_model.column", queried_model.column, calculated_type)
def test_get_from_schemas(schemas, stay_within_model, expected_artifacts): """ GIVEN schemas and the expected artifacts WHEN get_from_schemas is called with the schemas THEN the expected artifacts is returned. """ returned_artifacts = artifacts.get_from_schemas( schemas=schemas, stay_within_model=stay_within_model) assert returned_artifacts == expected_artifacts
def test_schema(schemas, expected_schema): """ GIVEN schemas and expected schema WHEN model_factory is called with the schemas and the name of a schema THEN a model with _schema set to the expected schema is returned. """ artifacts = schemas_artifacts.get_from_schemas( schemas=schemas, stay_within_model=True ) model = model_factory.model_factory( name="Schema", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts ) assert model._schema == expected_schema
def test_mixin(monkeypatch): """ GIVEN schemas with schema that has a mixin WHEN model_factory is called with the name of the schema THEN a model with the property is returned. """ # Define mixin mock_import_module = mock.MagicMock() mixin_class = type( "Mixin1", (), { "property_2": sqlalchemy.types.Column(sqlalchemy.types.Integer), "__abstract__": True, }, ) mock_import_module.return_value.Mixin1 = mixin_class monkeypatch.setattr(importlib, "import_module", mock_import_module) schemas = { "SingleProperty": { "x-tablename": "table 1", "type": "object", "x-mixins": "module.Mixin1", "properties": { "property_1": { "type": "integer" } }, } } artifacts = schemas_artifacts.get_from_schemas(schemas=schemas, stay_within_model=True) model = model_factory.model_factory( name="SingleProperty", get_base=_mock_get_base, schemas=schemas, artifacts=artifacts, ) assert hasattr(model, "property_2") assert model.__abstract__ is False
def test_model_database_type_many_to_many(engine, sessionmaker): """ GIVEN spec for a many to many relationship WHEN spec is constructed with model factory and queried THEN the referenced and back reference type is an array that is not nullable. """ # Defining specification spec = { "components": { "schemas": { "RefTable": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, }, "x-tablename": "ref_table", "x-backref": "tables", "type": "object", "x-secondary": "association", }, "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "ref_tables": { "type": "array", "items": { "$ref": "#/components/schemas/RefTable" }, }, }, "x-tablename": "table", "type": "object", }, } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") ref_model = model_factory(name="RefTable") # Calculate the expected type schemas_artifacts = schemas_artifacts_module.get_from_schemas( schemas=spec["components"]["schemas"], stay_within_model=False) # Expecting 3 due to association assert "RefTable" in schemas_artifacts ref_model_schemas_artifacts = schemas_artifacts["RefTable"] ref_model_models_artifacts = artifacts.calculate( artifacts=ref_model_schemas_artifacts, name="RefTable") assert len(ref_model_models_artifacts.sqlalchemy.columns) == 3 tables_column_artifacts = ref_model_models_artifacts.sqlalchemy.columns[2] assert tables_column_artifacts.name == "tables" calculated_backref_type_str = tables_column_artifacts.type assert "Table" in schemas_artifacts model_schemas_artifacts = schemas_artifacts["Table"] model_models_artifacts = artifacts.calculate( artifacts=model_schemas_artifacts, name="Table") assert len(model_models_artifacts.sqlalchemy.columns) == 3 ref_tables_column_artifacts = model_models_artifacts.sqlalchemy.columns[2] assert ref_tables_column_artifacts.name == "ref_tables" calculated_type_str = ref_tables_column_artifacts.type # Creating models base.metadata.create_all(engine) session = sessionmaker() # Creating instance of model without ref_models model_instance1 = model(id=11, name="ref table name 1") session.add(model_instance1) # Creating instance of model without empty ref_models model_instance2 = model(id=21, name="ref table name 2", ref_tables=[]) session.add(model_instance2) # Creating instance of model with single ref_model model_instance3 = model( id=31, name="ref table name 3", ref_tables=[ref_model(id=32, name="table name 3")], ) session.add(model_instance3) session.flush() # Querying session queried_models = session.query(model).all() assert len(queried_models[0].ref_tables) == 0 assert len(queried_models[1].ref_tables) == 0 assert len(queried_models[2].ref_tables) == 1 # Try constructing null for models with pytest.raises(TypeError): model(id=41, name="ref table name 4", ref_tables=None) assert calculated_type_str == 'typing.Sequence["TRefTable"]' # Creating instance of ref_model without models ref_model_instance5 = ref_model(id=51, name="ref table name 5") session.add(ref_model_instance5) # Creating instance of ref_model without empty models ref_model_instance6 = ref_model(id=61, name="ref table name 6", tables=[]) session.add(ref_model_instance6) # Creating instance of ref_model with single model ref_model_instance7 = ref_model(id=71, name="ref table name 7", tables=[model(id=72, name="table name 7")]) session.add(ref_model_instance7) session.flush() # Querying session queried_ref_models = session.query(ref_model).all() assert len(queried_ref_models[1].tables) == 0 assert len(queried_ref_models[2].tables) == 0 assert len(queried_ref_models[3].tables) == 1 # Try constructing null for models with pytest.raises(TypeError): ref_model(id=81, name="ref table name 8", tables=None) assert calculated_backref_type_str == 'typing.Sequence["TTable"]'
def test_model_database_type_one_to_one_not_nullable(engine, sessionmaker): """ GIVEN spec with one to one relationship that is not nullable WHEN models are constructed and None is passed for the object reference THEN sqlalchemy.exc.IntegrityError is raised for the relationship and not for the back reference which is still a single object that is nullable. """ # Defining specification spec = { "components": { "schemas": { "RefTable": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, }, "x-tablename": "ref_table", "x-backref": "table", "type": "object", "x-uselist": False, }, "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "ref_table": { "allOf": [ { "$ref": "#/components/schemas/RefTable" }, { "nullable": False }, ] }, }, "x-tablename": "table", "type": "object", }, } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") ref_model = model_factory(name="RefTable") # Calculate the expected type schemas_artifacts = schemas_artifacts_module.get_from_schemas( schemas=spec["components"]["schemas"], stay_within_model=False) assert "RefTable" in schemas_artifacts ref_model_schemas_artifacts = schemas_artifacts["RefTable"] ref_model_models_artifacts = artifacts.calculate( artifacts=ref_model_schemas_artifacts, name="RefTable") assert len(ref_model_models_artifacts.sqlalchemy.columns) == 3 table_column_artifacts = ref_model_models_artifacts.sqlalchemy.columns[2] assert table_column_artifacts.name == "table" calculated_backref_type_str = table_column_artifacts.type assert "Table" in schemas_artifacts model_schemas_artifacts = schemas_artifacts["Table"] model_models_artifacts = artifacts.calculate( artifacts=model_schemas_artifacts, name="Table") assert len(model_models_artifacts.sqlalchemy.columns) == 3 ref_table_column_artifacts = model_models_artifacts.sqlalchemy.columns[2] assert ref_table_column_artifacts.name == "ref_table" calculated_type_str = ref_table_column_artifacts.type # Creating models base.metadata.create_all(engine) session = sessionmaker() # Creating instance of ref_model without model ref_model_instance1 = ref_model(id=11, name="ref table name 1") session.add(ref_model_instance1) # Creating instance of ref_model with model ref_model_instance2 = ref_model(id=21, name="ref table name 2", table=model(id=22, name="table name 2")) session.add(ref_model_instance2) session.flush() # Querying session queried_ref_models = session.query(ref_model).all() assert queried_ref_models[0].table is None assert queried_ref_models[1].table is not None assert calculated_backref_type_str == 'typing.Optional["TTable"]' # Creating models base.metadata.create_all(engine) # Creating instance of model with None ref_model model_instance = model(id=32, name="table name 3", ref_table=None) session = sessionmaker() session.add(model_instance) with pytest.raises(sqlalchemy.exc.IntegrityError): session.flush() # Check that returned type is correct assert calculated_type_str == '"TRefTable"'
def test_model_database_type_one_to_one(engine, sessionmaker): """ GIVEN spec for a one to one relationship WHEN spec is constructed with model factory and queried THEN the referenced type is a single object that is nullable and the back reference is an single object that is nullable. """ # Defining specification spec = { "components": { "schemas": { "RefTable": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, }, "x-tablename": "ref_table", "x-backref": "table", "x-uselist": False, "type": "object", }, "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "ref_table": { "$ref": "#/components/schemas/RefTable" }, }, "x-tablename": "table", "type": "object", }, } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") ref_model = model_factory(name="RefTable") # Calculate the expected type schemas_artifacts = schemas_artifacts_module.get_from_schemas( schemas=spec["components"]["schemas"], stay_within_model=False) assert "RefTable" in schemas_artifacts ref_model_schemas_artifacts = schemas_artifacts["RefTable"] ref_model_models_artifacts = artifacts.calculate( artifacts=ref_model_schemas_artifacts, name="RefTable") assert len(ref_model_models_artifacts.sqlalchemy.columns) == 3 table_column_artifacts = ref_model_models_artifacts.sqlalchemy.columns[2] assert table_column_artifacts.name == "table" calculated_backref_type_str = table_column_artifacts.type assert "Table" in schemas_artifacts model_schemas_artifacts = schemas_artifacts["Table"] model_models_artifacts = artifacts.calculate( artifacts=model_schemas_artifacts, name="Table") assert len(model_models_artifacts.sqlalchemy.columns) == 3 ref_table_column_artifacts = model_models_artifacts.sqlalchemy.columns[2] assert ref_table_column_artifacts.name == "ref_table" calculated_type_str = ref_table_column_artifacts.type # Creating models base.metadata.create_all(engine) session = sessionmaker() # Creating instance of model and ref_model ref_model_instance1 = ref_model(id=11, name="ref table name 1") model_instance1 = model(id=12, name="table name 1", ref_table=ref_model_instance1) session.add(ref_model_instance1) session.add(model_instance1) session.flush() # Creating instance of model with None ref_model model_instance2 = model(id=22, name="table name 2", ref_table=None) session.add(model_instance2) session.flush() # Querying session queried_models = session.query(model).all() assert queried_models[0].ref_table is not None assert queried_models[1].ref_table is None # Check that returned type is correct assert calculated_type_str == 'typing.Optional["TRefTable"]' # Creating instance of ref_model without model ref_model_instance3 = ref_model(id=31, name="ref table name 3") session.add(ref_model_instance3) # Creating instance of ref_model with model ref_model_instance4 = ref_model(id=41, name="ref table name 4", table=model(id=42, name="table name 4")) session.add(ref_model_instance4) session.flush() # Querying session queried_ref_models = session.query(ref_model).all() assert queried_ref_models[1].table is None assert queried_ref_models[2].table is not None assert calculated_backref_type_str == 'typing.Optional["TTable"]'
def test_model_database_type_many_to_one_not_nullable(engine, sessionmaker): """ GIVEN spec with many to one relationship that is not nullable WHEN models are constructed and None is passed for the object reference THEN sqlalchemy.exc.IntegrityError is raised. """ # Defining specification spec = { "components": { "schemas": { "RefTable": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, }, "x-tablename": "ref_table", "x-backref": "tables", "type": "object", }, "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "ref_table": { "allOf": [ { "$ref": "#/components/schemas/RefTable" }, { "nullable": False }, ], }, }, "x-tablename": "table", "type": "object", }, } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") model_factory(name="RefTable") # Calculate the expected type schemas_artifacts = schemas_artifacts_module.get_from_schemas( schemas=spec["components"]["schemas"], stay_within_model=False) assert "Table" in schemas_artifacts model_schemas_artifacts = schemas_artifacts["Table"] model_models_artifacts = artifacts.calculate( artifacts=model_schemas_artifacts, name="Table") assert len(model_models_artifacts.sqlalchemy.columns) == 3 ref_table_column_artifacts = model_models_artifacts.sqlalchemy.columns[2] assert ref_table_column_artifacts.name == "ref_table" calculated_type_str = ref_table_column_artifacts.type # Creating models base.metadata.create_all(engine) session = sessionmaker() # Creating instance of model with None ref_model model_instance = model(id=12, name="table name 1", ref_table=None) session.add(model_instance) with pytest.raises(sqlalchemy.exc.IntegrityError): session.flush() # Check that returned type is correct assert calculated_type_str == '"TRefTable"'