def test_empty_components(): """ GIVEN specification with empty components WHEN init_model_factory is called with the specification THEN KeyError is raised. """ with pytest.raises(open_alchemy.exceptions.MalformedSpecificationError): open_alchemy.init_model_factory(base=None, spec={"components": {}})
def test_empty_spec(): """ GIVEN empty specification WHEN init_model_factory is called with the specification THEN KeyError is raised. """ with pytest.raises(open_alchemy.exceptions.MalformedSpecificationError): open_alchemy.init_model_factory(base=None, spec={})
def test_schema(): """ GIVEN valid specification with single property WHEN return value of init_model_factory is called with the name of the schema THEN a SQLAlchemy model with a single property is returned. """ model_factory = open_alchemy.init_model_factory( base=mock.MagicMock, spec={ "components": { "schemas": { "Table": { "properties": { "column": { "type": "integer" } }, "x-tablename": "table", "type": "object", } } } }, ) model = model_factory(name="Table") # Checking model assert model.__tablename__ == "table" assert hasattr(model, "column") assert isinstance(model.column.type, sqlalchemy.Integer)
def test_to_from_dict_many_to_many_read_only(engine, sessionmaker): """ GIVEN specification that has a schema with a many to many relationship with read only on the child WHEN model is defined based on schema and constructed using from_dict THEN when to_dict is called the construction dictionary with read only value is returned. """ # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory( base=base, spec={ "components": { "schemas": { "RefTable": { "properties": { "id": {"type": "integer", "x-primary-key": True}, "tables": { "readOnly": True, "type": "array", "items": { "type": "object", "properties": {"id": {"type": "integer"}}, }, }, }, "x-tablename": "ref_table", "type": "object", "x-backref": "tables", "x-secondary": "association", }, "Table": { "properties": { "id": {"type": "integer", "x-primary-key": True}, "ref_tables": { "type": "array", "items": {"$ref": "#/components/schemas/RefTable"}, }, }, "x-tablename": "table", "type": "object", }, } } }, ) ref_model = model_factory(name="RefTable") model = model_factory(name="Table") # Creating models base.metadata.create_all(engine) # Constructing and turning back to dictionary model_dict = {"id": 11, "ref_tables": [{"id": 12}]} instance = model.from_dict(**model_dict) session = sessionmaker() session.add(instance) session.flush() queried_ref_instance = session.query(ref_model).first() assert queried_ref_instance.to_dict() == {"id": 12, "tables": [{"id": 11}]}
def test_basic_types(engine, sessionmaker, column_schema, value): """ GIVEN specification that has schema with a basic type property WHEN model is defined based on schema and constructed using from_dict THEN when to_dict is called the construction dictionary is returned. """ # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory( base=base, spec={ "components": { "schemas": { "Table": { "properties": {"column": column_schema}, "x-tablename": "table", "type": "object", } } } }, ) model = model_factory(name="Table") # Creating models base.metadata.create_all(engine) # Constructing and turning back to dictionary model_dict = {"column": value} instance = model.from_dict(**model_dict) session = sessionmaker() session.add(instance) session.flush() queried_instance = session.query(model).first() assert queried_instance.to_dict() == model_dict
def test_indexes(engine, index: str): """ GIVEN specification with a schema with an integer and given index WHEN schema is created THEN no exceptions get raised. """ # Defining specification column_schema = {"type": "integer", index: True} spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "column": column_schema, }, "x-tablename": "table", "type": "object", } } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model_factory(name="Table") # Creating models base.metadata.create_all(engine)
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.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 = models_file._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_cache_same(mocked_model_factory: mock.MagicMock): """ GIVEN valid specification and mocked model_factory WHEN return value of init_model_factory is called multiple times with the same name THEN mocked model_factory is called once. """ model_factory = open_alchemy.init_model_factory( base=mock.MagicMock, spec={ "components": { "schemas": { "Schema1": { "x-tablename": "schema_1", "type": "object", "properties": { "id": { "type": "integer" } }, } } } }, ) model_factory(name="Schema1") model_factory(name="Schema1") assert mocked_model_factory.call_count == 1
def test_database_many_to_one_relationship(engine, sessionmaker): """ GIVEN specification with a schema with a many to one object relationship WHEN schema is created, values inserted in both tables and queried THEN the data is returned as it was inserted. """ # 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": {"$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") # Creating models base.metadata.create_all(engine) # Creating instance of model and ref_model ref_model_instance = ref_model(id=11, name="ref table name 1") model_instance = model(id=12, name="table name 1", ref_table=ref_model_instance) session = sessionmaker() session.add(ref_model_instance) session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.id == 12 assert queried_model.name == "table name 1" assert queried_model.ref_table_id == 11 assert queried_model.ref_table.id == 11 assert queried_model.ref_table.name == "ref table name 1" queried_ref_model = session.query(ref_model).first() assert queried_ref_model.id == 11 assert queried_ref_model.name == "ref table name 1" assert len(queried_ref_model.tables) == 1 assert queried_ref_model.tables[0].id == 12
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", "nullable": False, }, "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") model_factory(name="RefTable") # Calculate the type through model factory operations schema = model._schema["properties"]["ref_table"] model_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=schema, required=None ) calculated_type_str = models_file._model._type.model(artifacts=model_artifacts) # 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"'
def test_model_database_type_simple( engine, sessionmaker, type_, format_, nullable, required, generated, value ): """ GIVEN simple type, format, nullable, required, generated and a value 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_, "format": format_, "nullable": nullable, }, }, "x-tablename": "table", "type": "object", # Use required to implement generated "required": ["column"] if required or generated else [], } } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") # Create artifacts artifacts = models_file.types.ColumnSchemaArtifacts( type=type_, format=format_, nullable=nullable, required=required ) calculated_type_str = models_file._model._type.model(artifacts=artifacts) 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_cache_same(mocked_model_factory: mock.MagicMock): """ GIVEN valid specification and mocked model_factory WHEN return value of init_model_factory is called multiple times with the same name THEN mocked model_factory is called once. """ model_factory = open_alchemy.init_model_factory( base=mock.MagicMock, spec={"components": {"schemas": {}}} ) model_factory(name="table 1") model_factory(name="table 1") assert mocked_model_factory.call_count == 1
def test_cache_diff(mocked_model_factory: mock.MagicMock): """ GIVEN valid specification and mocked model_factory WHEN return value of init_model_factory is called with different names THEN mocked model_factory is called the same number of times the return value is called. """ model_factory = open_alchemy.init_model_factory( base=mock.MagicMock, spec={"components": {"schemas": {}}} ) model_factory(name="table 1") model_factory(name="table 2") assert mocked_model_factory.call_count == 2
def test_model_database_type_simple_nullable_fail(engine, sessionmaker, nullable, required, generated): """ GIVEN simple type, format, nullable, required, generated and a None value WHEN a specification is written for the combination and a model created and initialized with the value THEN sqlalchemy.exc.IntegrityError is raised. """ spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True, "x-autoincrement": True, }, "column": { "type": "integer", "nullable": nullable }, }, "x-tablename": "table", "type": "object", # Use required to implement generated "required": ["column"] if required or generated else [], } } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") # Creating models base.metadata.create_all(engine) # Creating model instance model_instance = model(column=None) session = sessionmaker() session.add(model_instance) with pytest.raises(sqlalchemy.exc.IntegrityError): session.flush()
def test_types_server_default(engine, sessionmaker): """ GIVEN specification with a schema with a column that has a server default WHEN schema is created and an instance is added to the session THEN the instance with the default value is returned when the session is queried for it. """ # Defining specification column_schema = { "type": "string", "format": "date-time", "x-server-default": "CURRENT_TIMESTAMP", } spec = { "components": { "schemas": { "Table": { "properties": { "id": {"type": "integer", "x-primary-key": True}, "column": column_schema, }, "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") # Creating models base.metadata.create_all(engine) # Creating model instance model_instance = model(id=1) session = sessionmaker() session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.column.timestamp() == pytest.approx( datetime.datetime.utcnow().timestamp(), abs=10 )
def test_default(engine, sessionmaker): """ GIVEN specification with a schema with a default value for a column WHEN schema is created, values inserted without value for the default column THEN the data is returned as it was inserted with default value. """ # Defining specification spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string", "default": "name 1" }, }, "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") # Creating models base.metadata.create_all(engine) # Creating instance of model and ref_model model_instance = model(id=1) session = sessionmaker() session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.id == 1 assert queried_model.name == "name 1"
def test_types_default(engine, sessionmaker, type_, format_, default, expected_value): """ GIVEN specification with a schema with a given type column and a default for that column WHEN schema is created and an instance is added to the session THEN the instance with the default value is returned when the session is queried for it. """ # Defining specification column_schema = {"type": type_, "x-primary-key": True, "default": default} if format_ is not None: column_schema["format"] = format_ spec = { "components": { "schemas": { "Table": { "properties": { "column": column_schema }, "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") # Creating models base.metadata.create_all(engine) # Creating model instance model_instance = model() session = sessionmaker() session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.column == expected_value
def test_table_args_unique(engine, schema_additions, sql, expected_contents): """ GIVEN schema, additional properties for schema, sql to execute and expected contents WHEN models are constructed THEN when sql is executed the expected contents are in the result. """ # Defining schema spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "column": { "type": "integer" }, }, "x-tablename": "table", "type": "object", **schema_additions, } } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model_factory(name="Table") # Creating models base.metadata.create_all(engine) # Query schema results_list = list(str(result) for result in engine.execute(sql)) results = "\n".join(results_list) for expected_content in expected_contents: assert expected_content in results
def test_not_autoincrement(engine, sessionmaker): """ GIVEN specification with a schema with autoincrement disabled id column WHEN schema is created, values inserted without id column THEN exception is raised. """ # Defining specification spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True, "x-autoincrement": False, }, "name": { "type": "string" }, }, "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") # Creating models base.metadata.create_all(engine) # Creating instance of model and ref_model model_instance = model(name="table name 1") session = sessionmaker() session.add(model_instance) with pytest.raises(sqlalchemy.orm.exc.FlushError): session.flush()
def test_types_json(engine, sessionmaker, schema, value): """ GIVEN specification with a schema with a JSON property WHEN schema is created and an instance is added to the session THEN the instance is returned when the session is queried for it. """ # Defining specification spec = { "components": { "schemas": { "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "column": schema, }, "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") # Creating models base.metadata.create_all(engine) # Creating model instance model_instance = model(id=1, column=copy.deepcopy(value)) session = sessionmaker() session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.column == value
def test_ref_all_of(engine, sessionmaker, spec): """ GIVEN specification with a schema that has a $ref on a column WHEN schema is created and an instance is added to the session THEN the instance is returned when the session is queried for it. """ # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name="Table") # Creating models base.metadata.create_all(engine) # Creating model instance model_instance = model(column=1) session = sessionmaker() session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.column == 1
def create_models(*, filename: str, model_names: typing.Tuple[str], engine: typing.Any) -> typing.Tuple[typing.Any, ...]: """ Create model for a test. Args: filename: The name of the spec file where examples/ is treated as the base folder. model_name:s The names of the models to create. engine: The engine to connect to the database. Returns: The models. """ spec = read_spec(filename=filename) # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) models = tuple(map(lambda name: model_factory(name=name), model_names)) # Initialise database base.metadata.create_all(engine) return models
def create_model(*, filename: str, model_name: str, engine: typing.Any) -> typing.Any: """ Create model for a test. Args: filename: The name of the spec file where examples/ is treated as the base folder. model_name: The name of the model to create. engine: The engine to connect to the database. Returns: The model. """ spec = read_spec(filename=filename) # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) model = model_factory(name=model_name) # Initialise database base.metadata.create_all(engine) return model
def test_model_database_type_one_to_many(engine, sessionmaker): """ GIVEN spec for a one to many relationship WHEN spec is constructed with model factory and queried THEN the referenced type is an array that is not nullable and the back reference is an 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", }, "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.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 = models_file._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 = models_file._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 with model ref_model_instance5 = ref_model( id=51, name="ref table name 5", table=model(id=52, name="table name 5") ) session.add(ref_model_instance5) # Creating instance of ref_model with None model ref_model_instance6 = ref_model(id=61, name="ref table name 6", table=None) session.add(ref_model_instance6) session.flush() # Querying session queried_models = session.query(ref_model).all() assert queried_models[1].table is not None assert queried_models[2].table is None assert calculated_backref_type_str == 'typing.Optional["TTable"]'
def test_one_to_many_relationship_kwargs(engine, sessionmaker): """ GIVEN specification with a schema with a one to many object relationship with kwargs WHEN schema is created, values inserted in both tables and queried THEN the data is returned as specified by kwargs. """ # 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", }, "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "ref_tables": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/RefTable" }, { "x-kwargs": { "order_by": "desc(RefTable.name)", "lazy": "dynamic", } }, ] }, }, }, "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") # Creating models base.metadata.create_all(engine) # Creating instance of model and ref_model ref_model_instance1 = ref_model(id=11, name="ref table name 1") ref_model_instance2 = ref_model(id=21, name="ref table name 2") model_instance = model( id=12, name="table name 1", ref_tables=[ref_model_instance1, ref_model_instance2], ) session = sessionmaker() session.add(ref_model_instance1) session.add(ref_model_instance2) session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() ref_tables = list(ref_table.name for ref_table in queried_model.ref_tables) assert ref_tables == ["ref table name 2", "ref table name 1"]
def test_multiple(engine, sessionmaker): """ GIVEN specification with a schema with multiple relationships pointing to the same table WHEN schema is created, values inserted in both tables and queried THEN the data is returned as it was inserted with the correct foreign key. """ # Defining specification spec = { "components": { "schemas": { "RefTable": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, }, "x-tablename": "ref_table", "type": "object", }, "Table": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "ref_table_first": { "allOf": [ { "$ref": "#/components/schemas/RefTable" }, { "x-kwargs": { "foreign_keys": "Table.ref_table_first_id" } }, ] }, "ref_table_second": { "allOf": [ { "$ref": "#/components/schemas/RefTable" }, { "x-kwargs": { "foreign_keys": "Table.ref_table_second_id" } }, ] }, }, "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") # Creating models base.metadata.create_all(engine) # Creating instance of model and ref_model ref_model_instance_first = ref_model(id=11, name="ref table name 1") ref_model_instance_second = ref_model(id=21, name="ref table name 2") model_instance = model( id=12, name="table name 1", ref_table_first=ref_model_instance_first, ref_table_second=ref_model_instance_second, ) session = sessionmaker() session.add(ref_model_instance_first) session.add(ref_model_instance_second) session.add(model_instance) session.flush() # Querying session queried_model = session.query(model).first() assert queried_model.id == 12 assert queried_model.name == "table name 1" assert queried_model.ref_table_first_id == 11 assert queried_model.ref_table_first.id == 11 assert queried_model.ref_table_second_id == 21 assert queried_model.ref_table_second.id == 21 assert queried_model.ref_table_first.name == "ref table name 1" assert queried_model.ref_table_second.name == "ref table name 2"
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 type through model factory operations schema = model._schema["properties"]["ref_table"] backref_schema = ref_model._schema["x-backrefs"]["table"] model_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=schema, required=None ) model_backref_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=backref_schema, required=None ) calculated_type_str = models_file._model._type.model(artifacts=model_artifacts) calculated_backref_type_str = models_file._model._type.model( artifacts=model_backref_artifacts ) # 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_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, "nullable": False, }, "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 type through model factory operations schema = model._schema["properties"]["ref_table"] backref_schema = ref_model._schema["x-backrefs"]["table"] model_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=schema, required=None ) model_backref_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=backref_schema, required=None ) calculated_type_str = models_file._model._type.model(artifacts=model_artifacts) calculated_backref_type_str = models_file._model._type.model( artifacts=model_backref_artifacts ) # 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_single(engine, sessionmaker): """ GIVEN specification with a schema with single inheritance WHEN schema is created and values inserted without id column THEN the data is returned as it was inserted with autogenerated value. """ # Defining specification spec = { "components": { "schemas": { "Employee": { "properties": { "id": { "type": "integer", "x-primary-key": True }, "name": { "type": "string" }, "type": { "type": "string" }, }, "x-tablename": "employee", "type": "object", "x-kwargs": { "__mapper_args__": { "polymorphic_on": "type", "polymorphic_identity": "employee", } }, }, "Manager": { "allOf": [ { "$ref": "#/components/schemas/Employee" }, { "x-inherits": "Employee", "type": "object", "properties": { "manager_data": { "type": "string" } }, "x-kwargs": { "__mapper_args__": { "polymorphic_identity": "manager" } }, }, ] }, "Engineer": { "allOf": [ { "$ref": "#/components/schemas/Employee" }, { "x-inherits": "Employee", "type": "object", "properties": { "engineer_info": { "type": "string" } }, "x-kwargs": { "__mapper_args__": { "polymorphic_identity": "engineer" } }, }, ] }, } } } # Creating model factory base = declarative.declarative_base() model_factory = open_alchemy.init_model_factory(spec=spec, base=base) employee = model_factory(name="Employee") manager = model_factory(name="Manager") engineer = model_factory(name="Engineer") # Creating models base.metadata.create_all(engine) # Creating instance of models employee_instance = employee(id=1, name="employee 1") manager_instance = manager(id=2, name="employee 2", manager_data="manager data 2") engineer_instance = engineer(id=3, name="employee 3", engineer_info="engineer info 3") session = sessionmaker() session.add(employee_instance) session.add(manager_instance) session.add(engineer_instance) session.flush() # Querying session for employee queried_employee = session.query(employee).first() assert queried_employee.id == 1 assert queried_employee.name == "employee 1" assert queried_employee.type == "employee" # Querying session for manager queried_manager = session.query(manager).first() assert queried_manager.id == 2 assert queried_manager.name == "employee 2" assert queried_manager.type == "manager" assert queried_manager.manager_data == "manager data 2" # Querying session for engineer queried_engineer = session.query(engineer).first() assert queried_engineer.id == 3 assert queried_engineer.name == "employee 3" assert queried_engineer.type == "engineer" assert queried_engineer.engineer_info == "engineer info 3"
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 type through model factory operations schema = model._schema["properties"]["ref_tables"] backref_schema = ref_model._schema["x-backrefs"]["tables"] model_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=schema, required=None ) model_backref_artifacts = models_file._model._artifacts.gather_column_artifacts( schema=backref_schema, required=None ) calculated_type_str = models_file._model._type.model(artifacts=model_artifacts) calculated_backref_type_str = models_file._model._type.model( artifacts=model_backref_artifacts ) # 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"]'