def test_model_database_type_simple_json(engine, sessionmaker, type_, value):
    """
    GIVEN JSON type
    WHEN a specification is written for the combination and a model created and
        initialized with the value
    THEN the queried value complies with the type calculated by type_.model.
    """
    spec = {
        "components": {
            "schemas": {
                "Table": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True,
                            "x-autoincrement": True,
                        },
                        "column": {"type": type_, "x-json": True},
                    },
                    "x-tablename": "table",
                    "type": "object",
                    "required": ["column"],
                }
            }
        }
    }
    # Creating model factory
    base = declarative.declarative_base()
    model_factory = open_alchemy.init_model_factory(spec=spec, base=base)
    model = model_factory(name="Table")

    # Calculate the expected type
    schemas_artifacts = schemas_artifacts_module.get_from_schemas(
        schemas=spec["components"]["schemas"], stay_within_model=False
    )
    assert "Table" in schemas_artifacts
    model_schemas_artifacts = schemas_artifacts["Table"]
    model_models_artifacts = artifacts.calculate(
        artifacts=model_schemas_artifacts, name="Table"
    )
    assert len(model_models_artifacts.sqlalchemy.columns) == 2
    column_column_artifacts = model_models_artifacts.sqlalchemy.columns[1]
    assert column_column_artifacts.name == "column"
    calculated_type_str = column_column_artifacts.type
    calculated_type = eval(calculated_type_str)  # pylint: disable=eval-used

    # Creating models
    base.metadata.create_all(engine)
    # Creating model instance
    model_instance = model(column=value)
    session = sessionmaker()
    session.add(model_instance)
    session.flush()

    # Querying session
    queried_model = session.query(model).first()
    assert queried_model.column == value
    typeguard.check_type("queried_model.column", queried_model.column, calculated_type)
Example #2
0
def test_calculate_column_empty(artifacts, expected_empty):
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the given expected columns are added to the artifacts.
    """
    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.sqlalchemy.empty == expected_empty
Example #3
0
def test_calculate_description(artifacts, expected_description):
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the description is added to the artifacts.
    """
    name = "Model"

    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name=name)

    assert returned_artifacts.sqlalchemy.description == expected_description
Example #4
0
def test_calculate_parent():
    """
    GIVEN
    WHEN calculate is called
    THEN the correct parent class is set.
    """
    artifacts = _construct_model_artifacts([], None)

    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.sqlalchemy.parent_cls == _EXPECTED_CLS_BASE
Example #5
0
def test_calculate_name():
    """
    GIVEN name
    WHEN calculate is called with the name
    THEN the name is added to the artifacts.
    """
    artifacts = _construct_model_artifacts([], None)
    name = "Model"

    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name=name)

    assert returned_artifacts.sqlalchemy.name == name
Example #6
0
def test_calculate_td_names(artifacts, expected_required_name,
                            expected_not_required_name):
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the given expected td required and not required names are added to the
        artifacts.
    """
    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.typed_dict.required.name == expected_required_name
    assert returned_artifacts.typed_dict.not_required.name == expected_not_required_name
Example #7
0
def test_calculate_typed_dict_column_empty(artifacts, expected_required_empty,
                                           expected_not_required_empty):
    """
    GIVEN artifacts and expected required and not required empty
    WHEN calculate is called with the artifacts
    THEN the typed dict required and not required empty are as expected.
    """
    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.typed_dict.required.empty == expected_required_empty
    assert (returned_artifacts.typed_dict.not_required.empty ==
            expected_not_required_empty)
Example #8
0
def test_calculate_td_parent(artifacts, expected_required_parent,
                             expected_not_required_parent):
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the given expected td required and not required parents are added to the
        artifacts.
    """
    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert (returned_artifacts.typed_dict.required.parent_class ==
            expected_required_parent)
    artifacts_not_required_parent = (
        returned_artifacts.typed_dict.not_required.parent_class)
    assert artifacts_not_required_parent == expected_not_required_parent
Example #9
0
def test_calculate_arg():
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the given expected args are added to the artifacts.
    """
    artifacts = _construct_model_artifacts(
        [
            (
                "prop_1",
                _construct_simple_property_artifacts(False),
            ),
            (
                "prop_2",
                _construct_simple_property_artifacts(True),
            ),
        ],
        None,
    )

    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.sqlalchemy.arg.not_required == [
        models_types.ColumnArgArtifacts(
            name="prop_1",
            init_type="typing.Optional[int]",
            from_dict_type="typing.Optional[int]",
            default=None,
            read_only=None,
        )
    ]
    assert returned_artifacts.sqlalchemy.arg.required == [
        models_types.ColumnArgArtifacts(
            name="prop_2",
            init_type="int",
            from_dict_type="int",
            default=None,
            read_only=None,
        )
    ]
Example #10
0
def test_calculate_typed_dict_column():
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the given expected typed dict columns are added to the artifacts.
    """
    artifacts = _construct_model_artifacts(
        [
            (
                "prop_1",
                _construct_simple_property_artifacts(False),
            ),
            (
                "prop_2",
                _construct_simple_property_artifacts(True),
            ),
        ],
        None,
    )

    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.typed_dict.not_required.props == [
        models_types.ColumnArtifacts(
            name="prop_1",
            type="int",
            description=None,
        )
    ]
    assert returned_artifacts.typed_dict.required.props == [
        models_types.ColumnArtifacts(
            name="prop_2",
            type="int",
            description=None,
        )
    ]
Example #11
0
def test_calculate_column():
    """
    GIVEN artifacts
    WHEN calculate is called with the artifacts
    THEN the given expected columns are added to the artifacts.
    """
    artifacts = _construct_model_artifacts(
        [(
            "prop_1",
            _construct_simple_property_artifacts(False),
        )],
        None,
    )

    returned_artifacts = models_artifacts.calculate(artifacts=artifacts,
                                                    name="Model")

    assert returned_artifacts.sqlalchemy.columns == [
        models_types.ColumnArtifacts(
            name="prop_1",
            type="int",
            description=None,
        )
    ]
def test_model_database_type_many_to_many(engine, sessionmaker):
    """
    GIVEN spec for a many to many relationship
    WHEN spec is constructed with model factory and queried
    THEN the referenced and back reference type is an array that is not nullable.
    """
    # Defining specification
    spec = {
        "components": {
            "schemas": {
                "RefTable": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                    },
                    "x-tablename": "ref_table",
                    "x-backref": "tables",
                    "type": "object",
                    "x-secondary": "association",
                },
                "Table": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                        "ref_tables": {
                            "type": "array",
                            "items": {
                                "$ref": "#/components/schemas/RefTable"
                            },
                        },
                    },
                    "x-tablename": "table",
                    "type": "object",
                },
            }
        }
    }
    # Creating model factory
    base = declarative.declarative_base()
    model_factory = open_alchemy.init_model_factory(spec=spec, base=base)
    model = model_factory(name="Table")
    ref_model = model_factory(name="RefTable")

    # Calculate the expected type
    schemas_artifacts = schemas_artifacts_module.get_from_schemas(
        schemas=spec["components"]["schemas"], stay_within_model=False)
    # Expecting 3 due to association
    assert "RefTable" in schemas_artifacts
    ref_model_schemas_artifacts = schemas_artifacts["RefTable"]
    ref_model_models_artifacts = artifacts.calculate(
        artifacts=ref_model_schemas_artifacts, name="RefTable")
    assert len(ref_model_models_artifacts.sqlalchemy.columns) == 3
    tables_column_artifacts = ref_model_models_artifacts.sqlalchemy.columns[2]
    assert tables_column_artifacts.name == "tables"
    calculated_backref_type_str = tables_column_artifacts.type
    assert "Table" in schemas_artifacts
    model_schemas_artifacts = schemas_artifacts["Table"]
    model_models_artifacts = artifacts.calculate(
        artifacts=model_schemas_artifacts, name="Table")
    assert len(model_models_artifacts.sqlalchemy.columns) == 3
    ref_tables_column_artifacts = model_models_artifacts.sqlalchemy.columns[2]
    assert ref_tables_column_artifacts.name == "ref_tables"
    calculated_type_str = ref_tables_column_artifacts.type

    # Creating models
    base.metadata.create_all(engine)
    session = sessionmaker()

    # Creating instance of model without ref_models
    model_instance1 = model(id=11, name="ref table name 1")
    session.add(model_instance1)
    # Creating instance of model without empty ref_models
    model_instance2 = model(id=21, name="ref table name 2", ref_tables=[])
    session.add(model_instance2)
    # Creating instance of model with single ref_model
    model_instance3 = model(
        id=31,
        name="ref table name 3",
        ref_tables=[ref_model(id=32, name="table name 3")],
    )
    session.add(model_instance3)
    session.flush()

    # Querying session
    queried_models = session.query(model).all()
    assert len(queried_models[0].ref_tables) == 0
    assert len(queried_models[1].ref_tables) == 0
    assert len(queried_models[2].ref_tables) == 1

    # Try constructing null for models
    with pytest.raises(TypeError):
        model(id=41, name="ref table name 4", ref_tables=None)

    assert calculated_type_str == 'typing.Sequence["TRefTable"]'

    # Creating instance of ref_model without models
    ref_model_instance5 = ref_model(id=51, name="ref table name 5")
    session.add(ref_model_instance5)
    # Creating instance of ref_model without empty models
    ref_model_instance6 = ref_model(id=61, name="ref table name 6", tables=[])
    session.add(ref_model_instance6)
    # Creating instance of ref_model with single model
    ref_model_instance7 = ref_model(id=71,
                                    name="ref table name 7",
                                    tables=[model(id=72, name="table name 7")])
    session.add(ref_model_instance7)
    session.flush()

    # Querying session
    queried_ref_models = session.query(ref_model).all()
    assert len(queried_ref_models[1].tables) == 0
    assert len(queried_ref_models[2].tables) == 0
    assert len(queried_ref_models[3].tables) == 1

    # Try constructing null for models
    with pytest.raises(TypeError):
        ref_model(id=81, name="ref table name 8", tables=None)

    assert calculated_backref_type_str == 'typing.Sequence["TTable"]'
def test_model_database_type_one_to_one_not_nullable(engine, sessionmaker):
    """
    GIVEN spec with one to one relationship that is not nullable
    WHEN models are constructed and None is passed for the object reference
    THEN sqlalchemy.exc.IntegrityError is raised for the relationship and not
        for the back reference which is still a single object that is nullable.
    """
    # Defining specification
    spec = {
        "components": {
            "schemas": {
                "RefTable": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                    },
                    "x-tablename": "ref_table",
                    "x-backref": "table",
                    "type": "object",
                    "x-uselist": False,
                },
                "Table": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                        "ref_table": {
                            "allOf": [
                                {
                                    "$ref": "#/components/schemas/RefTable"
                                },
                                {
                                    "nullable": False
                                },
                            ]
                        },
                    },
                    "x-tablename": "table",
                    "type": "object",
                },
            }
        }
    }
    # Creating model factory
    base = declarative.declarative_base()
    model_factory = open_alchemy.init_model_factory(spec=spec, base=base)
    model = model_factory(name="Table")
    ref_model = model_factory(name="RefTable")

    # Calculate the expected type
    schemas_artifacts = schemas_artifacts_module.get_from_schemas(
        schemas=spec["components"]["schemas"], stay_within_model=False)
    assert "RefTable" in schemas_artifacts
    ref_model_schemas_artifacts = schemas_artifacts["RefTable"]
    ref_model_models_artifacts = artifacts.calculate(
        artifacts=ref_model_schemas_artifacts, name="RefTable")
    assert len(ref_model_models_artifacts.sqlalchemy.columns) == 3
    table_column_artifacts = ref_model_models_artifacts.sqlalchemy.columns[2]
    assert table_column_artifacts.name == "table"
    calculated_backref_type_str = table_column_artifacts.type
    assert "Table" in schemas_artifacts
    model_schemas_artifacts = schemas_artifacts["Table"]
    model_models_artifacts = artifacts.calculate(
        artifacts=model_schemas_artifacts, name="Table")
    assert len(model_models_artifacts.sqlalchemy.columns) == 3
    ref_table_column_artifacts = model_models_artifacts.sqlalchemy.columns[2]
    assert ref_table_column_artifacts.name == "ref_table"
    calculated_type_str = ref_table_column_artifacts.type

    # Creating models
    base.metadata.create_all(engine)
    session = sessionmaker()

    # Creating instance of ref_model without model
    ref_model_instance1 = ref_model(id=11, name="ref table name 1")
    session.add(ref_model_instance1)
    # Creating instance of ref_model with model
    ref_model_instance2 = ref_model(id=21,
                                    name="ref table name 2",
                                    table=model(id=22, name="table name 2"))
    session.add(ref_model_instance2)
    session.flush()

    # Querying session
    queried_ref_models = session.query(ref_model).all()
    assert queried_ref_models[0].table is None
    assert queried_ref_models[1].table is not None

    assert calculated_backref_type_str == 'typing.Optional["TTable"]'

    # Creating models
    base.metadata.create_all(engine)
    # Creating instance of model with None ref_model
    model_instance = model(id=32, name="table name 3", ref_table=None)
    session = sessionmaker()
    session.add(model_instance)
    with pytest.raises(sqlalchemy.exc.IntegrityError):
        session.flush()

    # Check that returned type is correct
    assert calculated_type_str == '"TRefTable"'
def test_model_database_type_one_to_one(engine, sessionmaker):
    """
    GIVEN spec for a one to one relationship
    WHEN spec is constructed with model factory and queried
    THEN the referenced type is a single object that is nullable and the back reference
        is an single object that is nullable.
    """
    # Defining specification
    spec = {
        "components": {
            "schemas": {
                "RefTable": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                    },
                    "x-tablename": "ref_table",
                    "x-backref": "table",
                    "x-uselist": False,
                    "type": "object",
                },
                "Table": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                        "ref_table": {
                            "$ref": "#/components/schemas/RefTable"
                        },
                    },
                    "x-tablename": "table",
                    "type": "object",
                },
            }
        }
    }
    # Creating model factory
    base = declarative.declarative_base()
    model_factory = open_alchemy.init_model_factory(spec=spec, base=base)
    model = model_factory(name="Table")
    ref_model = model_factory(name="RefTable")

    # Calculate the expected type
    schemas_artifacts = schemas_artifacts_module.get_from_schemas(
        schemas=spec["components"]["schemas"], stay_within_model=False)
    assert "RefTable" in schemas_artifacts
    ref_model_schemas_artifacts = schemas_artifacts["RefTable"]
    ref_model_models_artifacts = artifacts.calculate(
        artifacts=ref_model_schemas_artifacts, name="RefTable")
    assert len(ref_model_models_artifacts.sqlalchemy.columns) == 3
    table_column_artifacts = ref_model_models_artifacts.sqlalchemy.columns[2]
    assert table_column_artifacts.name == "table"
    calculated_backref_type_str = table_column_artifacts.type
    assert "Table" in schemas_artifacts
    model_schemas_artifacts = schemas_artifacts["Table"]
    model_models_artifacts = artifacts.calculate(
        artifacts=model_schemas_artifacts, name="Table")
    assert len(model_models_artifacts.sqlalchemy.columns) == 3
    ref_table_column_artifacts = model_models_artifacts.sqlalchemy.columns[2]
    assert ref_table_column_artifacts.name == "ref_table"
    calculated_type_str = ref_table_column_artifacts.type

    # Creating models
    base.metadata.create_all(engine)
    session = sessionmaker()

    # Creating instance of model and ref_model
    ref_model_instance1 = ref_model(id=11, name="ref table name 1")
    model_instance1 = model(id=12,
                            name="table name 1",
                            ref_table=ref_model_instance1)
    session.add(ref_model_instance1)
    session.add(model_instance1)
    session.flush()
    # Creating instance of model with None ref_model
    model_instance2 = model(id=22, name="table name 2", ref_table=None)
    session.add(model_instance2)
    session.flush()

    # Querying session
    queried_models = session.query(model).all()
    assert queried_models[0].ref_table is not None
    assert queried_models[1].ref_table is None

    # Check that returned type is correct
    assert calculated_type_str == 'typing.Optional["TRefTable"]'

    # Creating instance of ref_model without model
    ref_model_instance3 = ref_model(id=31, name="ref table name 3")
    session.add(ref_model_instance3)
    # Creating instance of ref_model with model
    ref_model_instance4 = ref_model(id=41,
                                    name="ref table name 4",
                                    table=model(id=42, name="table name 4"))
    session.add(ref_model_instance4)
    session.flush()

    # Querying session
    queried_ref_models = session.query(ref_model).all()
    assert queried_ref_models[1].table is None
    assert queried_ref_models[2].table is not None

    assert calculated_backref_type_str == 'typing.Optional["TTable"]'
def test_model_database_type_many_to_one_not_nullable(engine, sessionmaker):
    """
    GIVEN spec with many to one relationship that is not nullable
    WHEN models are constructed and None is passed for the object reference
    THEN sqlalchemy.exc.IntegrityError is raised.
    """
    # Defining specification
    spec = {
        "components": {
            "schemas": {
                "RefTable": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                    },
                    "x-tablename": "ref_table",
                    "x-backref": "tables",
                    "type": "object",
                },
                "Table": {
                    "properties": {
                        "id": {
                            "type": "integer",
                            "x-primary-key": True
                        },
                        "name": {
                            "type": "string"
                        },
                        "ref_table": {
                            "allOf": [
                                {
                                    "$ref": "#/components/schemas/RefTable"
                                },
                                {
                                    "nullable": False
                                },
                            ],
                        },
                    },
                    "x-tablename": "table",
                    "type": "object",
                },
            }
        }
    }
    # Creating model factory
    base = declarative.declarative_base()
    model_factory = open_alchemy.init_model_factory(spec=spec, base=base)
    model = model_factory(name="Table")
    model_factory(name="RefTable")

    # Calculate the expected type
    schemas_artifacts = schemas_artifacts_module.get_from_schemas(
        schemas=spec["components"]["schemas"], stay_within_model=False)
    assert "Table" in schemas_artifacts
    model_schemas_artifacts = schemas_artifacts["Table"]
    model_models_artifacts = artifacts.calculate(
        artifacts=model_schemas_artifacts, name="Table")
    assert len(model_models_artifacts.sqlalchemy.columns) == 3
    ref_table_column_artifacts = model_models_artifacts.sqlalchemy.columns[2]
    assert ref_table_column_artifacts.name == "ref_table"
    calculated_type_str = ref_table_column_artifacts.type

    # Creating models
    base.metadata.create_all(engine)
    session = sessionmaker()

    # Creating instance of model with None ref_model
    model_instance = model(id=12, name="table name 1", ref_table=None)
    session.add(model_instance)
    with pytest.raises(sqlalchemy.exc.IntegrityError):
        session.flush()

    # Check that returned type is correct
    assert calculated_type_str == '"TRefTable"'