Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,168 +5,6 @@
import deepdiff
import httpx
import pytest
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncConnection


async def _remove_quality_from_database(quality_name: str, expdb_test: AsyncConnection) -> None:
await expdb_test.execute(
text(
"""
DELETE FROM data_quality
WHERE `quality`=:deleted_quality
""",
),
parameters={"deleted_quality": quality_name},
)
await expdb_test.execute(
text(
"""
DELETE FROM quality
WHERE `name`=:deleted_quality
""",
),
parameters={"deleted_quality": quality_name},
)


async def test_list_qualities_identical(
py_api: httpx.AsyncClient, php_api: httpx.AsyncClient
) -> None:
new, original = await asyncio.gather(
py_api.get("/datasets/qualities/list"),
php_api.get("/data/qualities/list"),
)
assert original.status_code == new.status_code
assert original.json() == new.json()
# To keep the test idempotent, we cannot test if reaction to database changes is identical


@pytest.mark.mut
async def test_list_qualities(py_api: httpx.AsyncClient, expdb_test: AsyncConnection) -> None:
response = await py_api.get("/datasets/qualities/list")
assert response.status_code == HTTPStatus.OK
expected = {
"data_qualities_list": {
"quality": [
"AutoCorrelation",
"CfsSubsetEval_DecisionStumpAUC",
"CfsSubsetEval_DecisionStumpErrRate",
"CfsSubsetEval_DecisionStumpKappa",
"CfsSubsetEval_NaiveBayesAUC",
"CfsSubsetEval_NaiveBayesErrRate",
"CfsSubsetEval_NaiveBayesKappa",
"CfsSubsetEval_kNN1NAUC",
"CfsSubsetEval_kNN1NErrRate",
"CfsSubsetEval_kNN1NKappa",
"ClassEntropy",
"DecisionStumpAUC",
"DecisionStumpErrRate",
"DecisionStumpKappa",
"Dimensionality",
"EquivalentNumberOfAtts",
"J48.00001.AUC",
"J48.00001.ErrRate",
"J48.00001.Kappa",
"J48.0001.AUC",
"J48.0001.ErrRate",
"J48.0001.Kappa",
"J48.001.AUC",
"J48.001.ErrRate",
"J48.001.Kappa",
"MajorityClassPercentage",
"MajorityClassSize",
"MaxAttributeEntropy",
"MaxKurtosisOfNumericAtts",
"MaxMeansOfNumericAtts",
"MaxMutualInformation",
"MaxNominalAttDistinctValues",
"MaxSkewnessOfNumericAtts",
"MaxStdDevOfNumericAtts",
"MeanAttributeEntropy",
"MeanKurtosisOfNumericAtts",
"MeanMeansOfNumericAtts",
"MeanMutualInformation",
"MeanNoiseToSignalRatio",
"MeanNominalAttDistinctValues",
"MeanSkewnessOfNumericAtts",
"MeanStdDevOfNumericAtts",
"MinAttributeEntropy",
"MinKurtosisOfNumericAtts",
"MinMeansOfNumericAtts",
"MinMutualInformation",
"MinNominalAttDistinctValues",
"MinSkewnessOfNumericAtts",
"MinStdDevOfNumericAtts",
"MinorityClassPercentage",
"MinorityClassSize",
"NaiveBayesAUC",
"NaiveBayesErrRate",
"NaiveBayesKappa",
"NumberOfBinaryFeatures",
"NumberOfClasses",
"NumberOfFeatures",
"NumberOfInstances",
"NumberOfInstancesWithMissingValues",
"NumberOfMissingValues",
"NumberOfNumericFeatures",
"NumberOfSymbolicFeatures",
"PercentageOfBinaryFeatures",
"PercentageOfInstancesWithMissingValues",
"PercentageOfMissingValues",
"PercentageOfNumericFeatures",
"PercentageOfSymbolicFeatures",
"Quartile1AttributeEntropy",
"Quartile1KurtosisOfNumericAtts",
"Quartile1MeansOfNumericAtts",
"Quartile1MutualInformation",
"Quartile1SkewnessOfNumericAtts",
"Quartile1StdDevOfNumericAtts",
"Quartile2AttributeEntropy",
"Quartile2KurtosisOfNumericAtts",
"Quartile2MeansOfNumericAtts",
"Quartile2MutualInformation",
"Quartile2SkewnessOfNumericAtts",
"Quartile2StdDevOfNumericAtts",
"Quartile3AttributeEntropy",
"Quartile3KurtosisOfNumericAtts",
"Quartile3MeansOfNumericAtts",
"Quartile3MutualInformation",
"Quartile3SkewnessOfNumericAtts",
"Quartile3StdDevOfNumericAtts",
"REPTreeDepth1AUC",
"REPTreeDepth1ErrRate",
"REPTreeDepth1Kappa",
"REPTreeDepth2AUC",
"REPTreeDepth2ErrRate",
"REPTreeDepth2Kappa",
"REPTreeDepth3AUC",
"REPTreeDepth3ErrRate",
"REPTreeDepth3Kappa",
"RandomTreeDepth1AUC",
"RandomTreeDepth1ErrRate",
"RandomTreeDepth1Kappa",
"RandomTreeDepth2AUC",
"RandomTreeDepth2ErrRate",
"RandomTreeDepth2Kappa",
"RandomTreeDepth3AUC",
"RandomTreeDepth3ErrRate",
"RandomTreeDepth3Kappa",
"StdvNominalAttDistinctValues",
"kNN1NAUC",
"kNN1NErrRate",
"kNN1NKappa",
],
},
}
assert expected == response.json()

deleted = expected["data_qualities_list"]["quality"].pop()
await _remove_quality_from_database(quality_name=deleted, expdb_test=expdb_test)

response = await py_api.get("/datasets/qualities/list")
assert response.status_code == HTTPStatus.OK
assert expected == response.json()


async def test_get_quality(py_api: httpx.AsyncClient) -> None:
Expand Down
81 changes: 81 additions & 0 deletions tests/routers/openml/flows_exists_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from http import HTTPStatus

import httpx
import pytest
from pytest_mock import MockerFixture
from sqlalchemy.ext.asyncio import AsyncConnection

from core.errors import FlowNotFoundError
from routers.openml.flows import flow_exists
from tests.conftest import Flow


async def test_flow_exists(flow: Flow, py_api: httpx.AsyncClient) -> None:
response = await py_api.get(f"/flows/exists/{flow.name}/{flow.external_version}")
assert response.status_code == HTTPStatus.OK
assert response.json() == {"flow_id": flow.id}


async def test_flow_exists_not_exists(py_api: httpx.AsyncClient) -> None:
name, version = "foo", "bar"
response = await py_api.get(f"/flows/exists/{name}/{version}")
assert response.status_code == HTTPStatus.NOT_FOUND
assert response.headers["content-type"] == "application/problem+json"
error = response.json()
assert error["type"] == FlowNotFoundError.uri
assert name in error["detail"]
assert version in error["detail"]


@pytest.mark.parametrize(
("name", "external_version"),
[
("a", "b"),
("c", "d"),
],
)
async def test_flow_exists_calls_db_correctly(
name: str,
external_version: str,
expdb_test: AsyncConnection,
mocker: MockerFixture,
) -> None:
mocked_db = mocker.patch(
"database.flows.get_by_name",
new_callable=mocker.AsyncMock,
)
await flow_exists(name, external_version, expdb_test)
mocked_db.assert_called_once_with(
name=name,
external_version=external_version,
expdb=mocker.ANY,
)


@pytest.mark.parametrize(
"flow_id",
[1, 2],
)
async def test_flow_exists_processes_found(
flow_id: int,
mocker: MockerFixture,
expdb_test: AsyncConnection,
) -> None:
fake_flow = mocker.MagicMock(id=flow_id)
mocker.patch(
"database.flows.get_by_name",
new_callable=mocker.AsyncMock,
return_value=fake_flow,
)
response = await flow_exists("name", "external_version", expdb_test)
assert response == {"flow_id": fake_flow.id}


async def test_flow_exists_handles_flow_not_found(
mocker: MockerFixture, expdb_test: AsyncConnection
) -> None:
mocker.patch("database.flows.get_by_name", return_value=None)
with pytest.raises(FlowNotFoundError) as error:
await flow_exists("foo", "bar", expdb_test)
assert error.value.status_code == HTTPStatus.NOT_FOUND
assert error.value.uri == FlowNotFoundError.uri
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,6 @@

import deepdiff.diff
import httpx
import pytest
from pytest_mock import MockerFixture
from sqlalchemy.ext.asyncio import AsyncConnection

from core.errors import FlowNotFoundError
from routers.openml.flows import flow_exists
from tests.conftest import Flow


@pytest.mark.parametrize(
("name", "external_version"),
[
("a", "b"),
("c", "d"),
],
)
async def test_flow_exists_calls_db_correctly(
name: str,
external_version: str,
expdb_test: AsyncConnection,
mocker: MockerFixture,
) -> None:
mocked_db = mocker.patch(
"database.flows.get_by_name",
new_callable=mocker.AsyncMock,
)
await flow_exists(name, external_version, expdb_test)
mocked_db.assert_called_once_with(
name=name,
external_version=external_version,
expdb=mocker.ANY,
)


@pytest.mark.parametrize(
"flow_id",
[1, 2],
)
async def test_flow_exists_processes_found(
flow_id: int,
mocker: MockerFixture,
expdb_test: AsyncConnection,
) -> None:
fake_flow = mocker.MagicMock(id=flow_id)
mocker.patch(
"database.flows.get_by_name",
new_callable=mocker.AsyncMock,
return_value=fake_flow,
)
response = await flow_exists("name", "external_version", expdb_test)
assert response == {"flow_id": fake_flow.id}


async def test_flow_exists_handles_flow_not_found(
mocker: MockerFixture, expdb_test: AsyncConnection
) -> None:
mocker.patch("database.flows.get_by_name", return_value=None)
with pytest.raises(FlowNotFoundError) as error:
await flow_exists("foo", "bar", expdb_test)
assert error.value.status_code == HTTPStatus.NOT_FOUND
assert error.value.uri == FlowNotFoundError.uri


async def test_flow_exists(flow: Flow, py_api: httpx.AsyncClient) -> None:
response = await py_api.get(f"/flows/exists/{flow.name}/{flow.external_version}")
assert response.status_code == HTTPStatus.OK
assert response.json() == {"flow_id": flow.id}


async def test_flow_exists_not_exists(py_api: httpx.AsyncClient) -> None:
name, version = "foo", "bar"
response = await py_api.get(f"/flows/exists/{name}/{version}")
assert response.status_code == HTTPStatus.NOT_FOUND
assert response.headers["content-type"] == "application/problem+json"
error = response.json()
assert error["type"] == FlowNotFoundError.uri
assert name in error["detail"]
assert version in error["detail"]


async def test_get_flow_no_subflow(py_api: httpx.AsyncClient) -> None:
Expand Down
Loading
Loading