예제 #1
0
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": {}})
예제 #2
0
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={})
예제 #3
0
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)
예제 #4
0
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}]}
예제 #5
0
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
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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"'
예제 #11
0
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)
예제 #12
0
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
예제 #13
0
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
예제 #14
0
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()
예제 #15
0
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
    )
예제 #16
0
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"
예제 #17
0
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
예제 #18
0
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
예제 #19
0
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()
예제 #20
0
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
예제 #21
0
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
예제 #22
0
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
예제 #23
0
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
예제 #24
0
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"]'
예제 #25
0
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"]
예제 #26
0
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"
예제 #27
0
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"]'
예제 #28
0
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"'
예제 #29
0
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"
예제 #30
0
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"]'