Skip to content

Commit 99da4a0

Browse files
authored
Separate out test files to one file per endpoint (#295)
Reorganizing the tests to one file per endpoint, because: - it's easier to see which conditions are and aren't tested. - it's useful to be able to run tests for a specific endpoint, since that's usually the change in a PR. Tests under `migration` are left out for now, but will be moved as well.
1 parent 12cdf10 commit 99da4a0

14 files changed

Lines changed: 481 additions & 310 deletions

tests/routers/openml/qualities_test.py renamed to tests/routers/openml/datasets_qualities_test.py

Lines changed: 0 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -5,168 +5,6 @@
55
import deepdiff
66
import httpx
77
import pytest
8-
from sqlalchemy import text
9-
from sqlalchemy.ext.asyncio import AsyncConnection
10-
11-
12-
async def _remove_quality_from_database(quality_name: str, expdb_test: AsyncConnection) -> None:
13-
await expdb_test.execute(
14-
text(
15-
"""
16-
DELETE FROM data_quality
17-
WHERE `quality`=:deleted_quality
18-
""",
19-
),
20-
parameters={"deleted_quality": quality_name},
21-
)
22-
await expdb_test.execute(
23-
text(
24-
"""
25-
DELETE FROM quality
26-
WHERE `name`=:deleted_quality
27-
""",
28-
),
29-
parameters={"deleted_quality": quality_name},
30-
)
31-
32-
33-
async def test_list_qualities_identical(
34-
py_api: httpx.AsyncClient, php_api: httpx.AsyncClient
35-
) -> None:
36-
new, original = await asyncio.gather(
37-
py_api.get("/datasets/qualities/list"),
38-
php_api.get("/data/qualities/list"),
39-
)
40-
assert original.status_code == new.status_code
41-
assert original.json() == new.json()
42-
# To keep the test idempotent, we cannot test if reaction to database changes is identical
43-
44-
45-
@pytest.mark.mut
46-
async def test_list_qualities(py_api: httpx.AsyncClient, expdb_test: AsyncConnection) -> None:
47-
response = await py_api.get("/datasets/qualities/list")
48-
assert response.status_code == HTTPStatus.OK
49-
expected = {
50-
"data_qualities_list": {
51-
"quality": [
52-
"AutoCorrelation",
53-
"CfsSubsetEval_DecisionStumpAUC",
54-
"CfsSubsetEval_DecisionStumpErrRate",
55-
"CfsSubsetEval_DecisionStumpKappa",
56-
"CfsSubsetEval_NaiveBayesAUC",
57-
"CfsSubsetEval_NaiveBayesErrRate",
58-
"CfsSubsetEval_NaiveBayesKappa",
59-
"CfsSubsetEval_kNN1NAUC",
60-
"CfsSubsetEval_kNN1NErrRate",
61-
"CfsSubsetEval_kNN1NKappa",
62-
"ClassEntropy",
63-
"DecisionStumpAUC",
64-
"DecisionStumpErrRate",
65-
"DecisionStumpKappa",
66-
"Dimensionality",
67-
"EquivalentNumberOfAtts",
68-
"J48.00001.AUC",
69-
"J48.00001.ErrRate",
70-
"J48.00001.Kappa",
71-
"J48.0001.AUC",
72-
"J48.0001.ErrRate",
73-
"J48.0001.Kappa",
74-
"J48.001.AUC",
75-
"J48.001.ErrRate",
76-
"J48.001.Kappa",
77-
"MajorityClassPercentage",
78-
"MajorityClassSize",
79-
"MaxAttributeEntropy",
80-
"MaxKurtosisOfNumericAtts",
81-
"MaxMeansOfNumericAtts",
82-
"MaxMutualInformation",
83-
"MaxNominalAttDistinctValues",
84-
"MaxSkewnessOfNumericAtts",
85-
"MaxStdDevOfNumericAtts",
86-
"MeanAttributeEntropy",
87-
"MeanKurtosisOfNumericAtts",
88-
"MeanMeansOfNumericAtts",
89-
"MeanMutualInformation",
90-
"MeanNoiseToSignalRatio",
91-
"MeanNominalAttDistinctValues",
92-
"MeanSkewnessOfNumericAtts",
93-
"MeanStdDevOfNumericAtts",
94-
"MinAttributeEntropy",
95-
"MinKurtosisOfNumericAtts",
96-
"MinMeansOfNumericAtts",
97-
"MinMutualInformation",
98-
"MinNominalAttDistinctValues",
99-
"MinSkewnessOfNumericAtts",
100-
"MinStdDevOfNumericAtts",
101-
"MinorityClassPercentage",
102-
"MinorityClassSize",
103-
"NaiveBayesAUC",
104-
"NaiveBayesErrRate",
105-
"NaiveBayesKappa",
106-
"NumberOfBinaryFeatures",
107-
"NumberOfClasses",
108-
"NumberOfFeatures",
109-
"NumberOfInstances",
110-
"NumberOfInstancesWithMissingValues",
111-
"NumberOfMissingValues",
112-
"NumberOfNumericFeatures",
113-
"NumberOfSymbolicFeatures",
114-
"PercentageOfBinaryFeatures",
115-
"PercentageOfInstancesWithMissingValues",
116-
"PercentageOfMissingValues",
117-
"PercentageOfNumericFeatures",
118-
"PercentageOfSymbolicFeatures",
119-
"Quartile1AttributeEntropy",
120-
"Quartile1KurtosisOfNumericAtts",
121-
"Quartile1MeansOfNumericAtts",
122-
"Quartile1MutualInformation",
123-
"Quartile1SkewnessOfNumericAtts",
124-
"Quartile1StdDevOfNumericAtts",
125-
"Quartile2AttributeEntropy",
126-
"Quartile2KurtosisOfNumericAtts",
127-
"Quartile2MeansOfNumericAtts",
128-
"Quartile2MutualInformation",
129-
"Quartile2SkewnessOfNumericAtts",
130-
"Quartile2StdDevOfNumericAtts",
131-
"Quartile3AttributeEntropy",
132-
"Quartile3KurtosisOfNumericAtts",
133-
"Quartile3MeansOfNumericAtts",
134-
"Quartile3MutualInformation",
135-
"Quartile3SkewnessOfNumericAtts",
136-
"Quartile3StdDevOfNumericAtts",
137-
"REPTreeDepth1AUC",
138-
"REPTreeDepth1ErrRate",
139-
"REPTreeDepth1Kappa",
140-
"REPTreeDepth2AUC",
141-
"REPTreeDepth2ErrRate",
142-
"REPTreeDepth2Kappa",
143-
"REPTreeDepth3AUC",
144-
"REPTreeDepth3ErrRate",
145-
"REPTreeDepth3Kappa",
146-
"RandomTreeDepth1AUC",
147-
"RandomTreeDepth1ErrRate",
148-
"RandomTreeDepth1Kappa",
149-
"RandomTreeDepth2AUC",
150-
"RandomTreeDepth2ErrRate",
151-
"RandomTreeDepth2Kappa",
152-
"RandomTreeDepth3AUC",
153-
"RandomTreeDepth3ErrRate",
154-
"RandomTreeDepth3Kappa",
155-
"StdvNominalAttDistinctValues",
156-
"kNN1NAUC",
157-
"kNN1NErrRate",
158-
"kNN1NKappa",
159-
],
160-
},
161-
}
162-
assert expected == response.json()
163-
164-
deleted = expected["data_qualities_list"]["quality"].pop()
165-
await _remove_quality_from_database(quality_name=deleted, expdb_test=expdb_test)
166-
167-
response = await py_api.get("/datasets/qualities/list")
168-
assert response.status_code == HTTPStatus.OK
169-
assert expected == response.json()
1708

1719

17210
async def test_get_quality(py_api: httpx.AsyncClient) -> None:
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from http import HTTPStatus
2+
3+
import httpx
4+
import pytest
5+
from pytest_mock import MockerFixture
6+
from sqlalchemy.ext.asyncio import AsyncConnection
7+
8+
from core.errors import FlowNotFoundError
9+
from routers.openml.flows import flow_exists
10+
from tests.conftest import Flow
11+
12+
13+
async def test_flow_exists(flow: Flow, py_api: httpx.AsyncClient) -> None:
14+
response = await py_api.get(f"/flows/exists/{flow.name}/{flow.external_version}")
15+
assert response.status_code == HTTPStatus.OK
16+
assert response.json() == {"flow_id": flow.id}
17+
18+
19+
async def test_flow_exists_not_exists(py_api: httpx.AsyncClient) -> None:
20+
name, version = "foo", "bar"
21+
response = await py_api.get(f"/flows/exists/{name}/{version}")
22+
assert response.status_code == HTTPStatus.NOT_FOUND
23+
assert response.headers["content-type"] == "application/problem+json"
24+
error = response.json()
25+
assert error["type"] == FlowNotFoundError.uri
26+
assert name in error["detail"]
27+
assert version in error["detail"]
28+
29+
30+
@pytest.mark.parametrize(
31+
("name", "external_version"),
32+
[
33+
("a", "b"),
34+
("c", "d"),
35+
],
36+
)
37+
async def test_flow_exists_calls_db_correctly(
38+
name: str,
39+
external_version: str,
40+
expdb_test: AsyncConnection,
41+
mocker: MockerFixture,
42+
) -> None:
43+
mocked_db = mocker.patch(
44+
"database.flows.get_by_name",
45+
new_callable=mocker.AsyncMock,
46+
)
47+
await flow_exists(name, external_version, expdb_test)
48+
mocked_db.assert_called_once_with(
49+
name=name,
50+
external_version=external_version,
51+
expdb=mocker.ANY,
52+
)
53+
54+
55+
@pytest.mark.parametrize(
56+
"flow_id",
57+
[1, 2],
58+
)
59+
async def test_flow_exists_processes_found(
60+
flow_id: int,
61+
mocker: MockerFixture,
62+
expdb_test: AsyncConnection,
63+
) -> None:
64+
fake_flow = mocker.MagicMock(id=flow_id)
65+
mocker.patch(
66+
"database.flows.get_by_name",
67+
new_callable=mocker.AsyncMock,
68+
return_value=fake_flow,
69+
)
70+
response = await flow_exists("name", "external_version", expdb_test)
71+
assert response == {"flow_id": fake_flow.id}
72+
73+
74+
async def test_flow_exists_handles_flow_not_found(
75+
mocker: MockerFixture, expdb_test: AsyncConnection
76+
) -> None:
77+
mocker.patch("database.flows.get_by_name", return_value=None)
78+
with pytest.raises(FlowNotFoundError) as error:
79+
await flow_exists("foo", "bar", expdb_test)
80+
assert error.value.status_code == HTTPStatus.NOT_FOUND
81+
assert error.value.uri == FlowNotFoundError.uri
Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,84 +2,6 @@
22

33
import deepdiff.diff
44
import httpx
5-
import pytest
6-
from pytest_mock import MockerFixture
7-
from sqlalchemy.ext.asyncio import AsyncConnection
8-
9-
from core.errors import FlowNotFoundError
10-
from routers.openml.flows import flow_exists
11-
from tests.conftest import Flow
12-
13-
14-
@pytest.mark.parametrize(
15-
("name", "external_version"),
16-
[
17-
("a", "b"),
18-
("c", "d"),
19-
],
20-
)
21-
async def test_flow_exists_calls_db_correctly(
22-
name: str,
23-
external_version: str,
24-
expdb_test: AsyncConnection,
25-
mocker: MockerFixture,
26-
) -> None:
27-
mocked_db = mocker.patch(
28-
"database.flows.get_by_name",
29-
new_callable=mocker.AsyncMock,
30-
)
31-
await flow_exists(name, external_version, expdb_test)
32-
mocked_db.assert_called_once_with(
33-
name=name,
34-
external_version=external_version,
35-
expdb=mocker.ANY,
36-
)
37-
38-
39-
@pytest.mark.parametrize(
40-
"flow_id",
41-
[1, 2],
42-
)
43-
async def test_flow_exists_processes_found(
44-
flow_id: int,
45-
mocker: MockerFixture,
46-
expdb_test: AsyncConnection,
47-
) -> None:
48-
fake_flow = mocker.MagicMock(id=flow_id)
49-
mocker.patch(
50-
"database.flows.get_by_name",
51-
new_callable=mocker.AsyncMock,
52-
return_value=fake_flow,
53-
)
54-
response = await flow_exists("name", "external_version", expdb_test)
55-
assert response == {"flow_id": fake_flow.id}
56-
57-
58-
async def test_flow_exists_handles_flow_not_found(
59-
mocker: MockerFixture, expdb_test: AsyncConnection
60-
) -> None:
61-
mocker.patch("database.flows.get_by_name", return_value=None)
62-
with pytest.raises(FlowNotFoundError) as error:
63-
await flow_exists("foo", "bar", expdb_test)
64-
assert error.value.status_code == HTTPStatus.NOT_FOUND
65-
assert error.value.uri == FlowNotFoundError.uri
66-
67-
68-
async def test_flow_exists(flow: Flow, py_api: httpx.AsyncClient) -> None:
69-
response = await py_api.get(f"/flows/exists/{flow.name}/{flow.external_version}")
70-
assert response.status_code == HTTPStatus.OK
71-
assert response.json() == {"flow_id": flow.id}
72-
73-
74-
async def test_flow_exists_not_exists(py_api: httpx.AsyncClient) -> None:
75-
name, version = "foo", "bar"
76-
response = await py_api.get(f"/flows/exists/{name}/{version}")
77-
assert response.status_code == HTTPStatus.NOT_FOUND
78-
assert response.headers["content-type"] == "application/problem+json"
79-
error = response.json()
80-
assert error["type"] == FlowNotFoundError.uri
81-
assert name in error["detail"]
82-
assert version in error["detail"]
835

846

857
async def test_get_flow_no_subflow(py_api: httpx.AsyncClient) -> None:

0 commit comments

Comments
 (0)