From 49270bf469a86c8f22f6209e0b83cfd1b5e4818d Mon Sep 17 00:00:00 2001 From: Alvaro Lopez Garcia Date: Tue, 11 Jun 2024 11:46:09 +0200 Subject: [PATCH] tests: migrate API and API V2 tests to pytest --- deepaas/tests/fake_responses.py | 5 +- deepaas/tests/test_api.py | 108 ++++++++++ deepaas/tests/test_v2_api.py | 335 ++++++++++++++------------------ 3 files changed, 253 insertions(+), 195 deletions(-) create mode 100644 deepaas/tests/test_api.py diff --git a/deepaas/tests/fake_responses.py b/deepaas/tests/fake_responses.py index 7512655e..28337ff6 100644 --- a/deepaas/tests/fake_responses.py +++ b/deepaas/tests/fake_responses.py @@ -77,4 +77,7 @@ "labels": [{"label": "foo", "probability": 1.0}], } -deepaas_test_train = {"status": "running", "uuid": "2ad53089edfb4521af081c45df16bed5"} +deepaas_test_train = { + "status": "running", + "args": {"sleep": 0}, +} diff --git a/deepaas/tests/test_api.py b/deepaas/tests/test_api.py new file mode 100644 index 00000000..70f3a929 --- /dev/null +++ b/deepaas/tests/test_api.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Spanish National Research Council (CSIC) +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import io + +import pytest + +import deepaas +from deepaas import api +from deepaas.model.v2 import wrapper as v2_wrapper +from deepaas.tests import fake_responses +from deepaas.tests import fake_v2_model + + +class TestApiV2: + @pytest.fixture + @staticmethod + def wrapped_model(): + return v2_wrapper.ModelWrapper("deepaas-test", fake_v2_model.TestModel(), None) + + @pytest.fixture + @staticmethod + async def client(application, aiohttp_client): + c = await aiohttp_client(application) + return c + + @pytest.fixture + @staticmethod + async def application(monkeypatch): + # app = web.Application() + # app.middlewares.append(web.normalize_path_middleware()) + + w = v2_wrapper.ModelWrapper("deepaas-test", fake_v2_model.TestModel(), None) + + monkeypatch.setattr(api, "APP", None) + monkeypatch.setattr(deepaas.model, "V2_MODELS", {"deepaas-test": w}) + monkeypatch.setattr(deepaas.model, "register_v2_models", lambda x: None) + + app = await api.get_app(enable_doc=False, base_path="/custom") + + return app + + async def test_not_found(self, client): + ret = await client.get("/api") + assert 404 == ret.status + + async def test_predict_data(self, client): + f = io.BytesIO(b"foo") + ret = await client.post( + "/custom/v2/models/deepaas-test/predict/", + data={"data": (f, "foo.txt"), "parameter": 1}, + ) + assert 200 == ret.status + + json = await ret.json() + + assert "data" in json + del json["data"] + + assert fake_responses.deepaas_test_predict == json + + async def test_train(self, client): + ret = await client.post( + "/custom/v2/models/deepaas-test/train/", data={"sleep": 0} + ) + assert 200 == ret.status + + json = await ret.json() + + assert "date" in json + json.pop("date") + + assert "uuid" in json + del json["uuid"] + + assert fake_responses.deepaas_test_train == json + + async def test_get_metadata(self, client): + meta = fake_responses.models_meta + + ret = await client.get("/custom/v2/models/") + assert ret.status in [200, 201] + + ret_meta = await ret.json() + ret_meta["models"][0]["links"][0]["href"] = "/v2/models/deepaas-test" + + assert meta == ret_meta + + ret = await client.get("/custom/v2/models/deepaas-test/") + assert ret.status in [200, 201] + + ret_meta = await ret.json() + ret_meta["links"][0]["href"] = "/v2/models/deepaas-test" + + assert meta["models"][0] == ret_meta diff --git a/deepaas/tests/test_v2_api.py b/deepaas/tests/test_v2_api.py index b97a1948..78a258fe 100644 --- a/deepaas/tests/test_v2_api.py +++ b/deepaas/tests/test_v2_api.py @@ -20,6 +20,7 @@ from aiohttp import web from oslo_config import cfg from oslo_log import log as logging +import pytest import deepaas from deepaas.api import v2 @@ -27,8 +28,9 @@ from deepaas.api.v2 import responses import deepaas.model import deepaas.model.v2 -from deepaas.tests import base +from deepaas.model.v2 import wrapper as v2_wrapper from deepaas.tests import fake_responses +from deepaas.tests import fake_v2_model CONF = cfg.CONF logging.register_options(CONF) @@ -42,270 +44,215 @@ class Fake(object): assert response is responses.Prediction -class TestApiV2NoTrain(base.TestCase): - async def get_application(self): - app = web.Application(debug=True) - app.middlewares.append(web.normalize_path_middleware()) - - deepaas.model.v2.register_models(app) - - v2app = v2.get_app(enable_train=False) - app.add_subapp("/v2", v2app) - - return app - - def setUp(self): - super(TestApiV2NoTrain, self).setUp() - - self.maxDiff = None +@pytest.fixture(autouse=True) +def cfg_fixture(): + def set_flag(flag, value): + CONF.set_override(flag, value) - self.flags(debug=True) - - def assert_ok(self, response): - self.assertIn(response.status, [200, 201]) - - async def test_not_found(self): - ret = await self.client.post("/v2/models/deepaas-test/train") - self.assertEqual(404, ret.status) - - async def test_predict_data(self): - f = io.BytesIO(b"foo") - ret = await self.client.post( - "/v2/models/deepaas-test/predict/", - data={"data": (f, "foo.txt"), "parameter": 1}, - ) - json = await ret.json() - self.assertEqual(200, ret.status) - self.assertDictEqual(fake_responses.deepaas_test_predict, json) + return set_flag - async def test_get_metadata(self): - meta = fake_responses.models_meta - ret = await self.client.get("/v2/models/") - self.assert_ok(ret) - self.assertDictEqual(meta, await ret.json()) +class BaseTestApiV2: + @pytest.fixture + @staticmethod + def wrapped_model(): + return v2_wrapper.ModelWrapper("deepaas-test", fake_v2_model.TestModel(), None) - ret = await self.client.get("/v2/models/deepaas-test/") - self.assert_ok(ret) - self.assertDictEqual(meta["models"][0], await ret.json()) + @pytest.fixture + @staticmethod + async def client(application, aiohttp_client): + c = await aiohttp_client(application) + return c -class TestApiV2NoPredict(base.TestCase): - async def get_application(self): - app = web.Application(debug=True) +class TestApiV2NoTrain(BaseTestApiV2): + @pytest.fixture + @staticmethod + async def application(monkeypatch): + app = web.Application() app.middlewares.append(web.normalize_path_middleware()) - deepaas.model.v2.register_models(app) + w = v2_wrapper.ModelWrapper("deepaas-test", fake_v2_model.TestModel(), app) - v2app = v2.get_app(enable_predict=False) + monkeypatch.setattr(deepaas.model, "V2_MODELS", {"deepaas-test": w}) + + v2app = v2.get_app(enable_train=False) app.add_subapp("/v2", v2app) return app - def setUp(self): - super(TestApiV2NoPredict, self).setUp() - - self.maxDiff = None + async def test_not_found(self, client): + ret = await client.post("/v2/models/deepaas-test/train") + assert 402 == ret.status - self.flags(debug=True) + async def test_predict_data(self, client): + f = io.BytesIO(b"foo") + ret = await client.post( + "/v2/models/deepaas-test/predict/", + data={"data": (f, "foo.txt"), "parameter": 1}, + ) + assert 200 == ret.status - def assert_ok(self, response): - self.assertIn(response.status, [200, 201]) + json = await ret.json() - async def test_not_found(self): - ret = await self.client.post("/v2/models/deepaas-test/predict/") - self.assertEqual(404, ret.status) + assert "data" in json + del json["data"] - async def test_train(self): - ret = await self.client.post( - "/v2/models/deepaas-test/train/", data={"sleep": 0} - ) - self.assertEqual(200, ret.status) - json = await ret.json() - json.pop("date") - self.assertDictEqual(fake_responses.deepaas_test_train, json) + assert fake_responses.deepaas_test_predict == json - async def test_get_metadata(self): + async def test_get_metadata(self, client): meta = fake_responses.models_meta - ret = await self.client.get("/v2/models/") - self.assert_ok(ret) - self.assertDictEqual(meta, await ret.json()) + ret = await client.get("/v2/models/") + assert ret.status in [200, 201] + assert meta == await ret.json() - ret = await self.client.get("/v2/models/deepaas-test/") - self.assert_ok(ret) - self.assertDictEqual(meta["models"][0], await ret.json()) + ret = await client.get("/v2/models/deepaas-test/") + assert ret.status in [200, 201] + assert meta["models"][0] == await ret.json() -class TestApiV2NoDoc(base.TestCase): - async def get_application(self): - app = web.Application(debug=True) +class TestApiV2NoPredict(BaseTestApiV2): + @pytest.fixture + @staticmethod + async def application(monkeypatch): + app = web.Application() app.middlewares.append(web.normalize_path_middleware()) - deepaas.model.v2.register_models(app) - - v2app = v2.get_app(enable_doc=False) - app.add_subapp("/v2", v2app) - - return app + w = v2_wrapper.ModelWrapper("deepaas-test", fake_v2_model.TestModel(), app) - def setUp(self): - super(TestApiV2NoDoc, self).setUp() + monkeypatch.setattr(deepaas.model, "V2_MODELS", {"deepaas-test": w}) - self.maxDiff = None - - self.flags(debug=True) - - def assert_ok(self, response): - self.assertIn(response.status, [200, 201]) - - async def test_not_found(self): - ret = await self.client.get("/api") - self.assertEqual(404, ret.status) - - -class TestApiV2CusomBasePath(base.TestCase): - async def get_application(self): - app = web.Application(debug=True) - app.middlewares.append(web.normalize_path_middleware()) - - deepaas.model.v2.register_models(app) - - v2app = v2.get_app(base_path="/custom") + v2app = v2.get_app(enable_predict=False) app.add_subapp("/v2", v2app) return app - def setUp(self): - super(TestApiV2CusomBasePath, self).setUp() + async def test_not_found(self, client): + ret = await client.post("/v2/models/deepaas-test/predict/") + assert 402 == ret.status - self.maxDiff = None + async def test_train(self, client): + ret = await client.post("/v2/models/deepaas-test/train/", data={"sleep": 0}) + assert 200 == ret.status - self.flags(debug=True) - - def assert_ok(self, response): - self.assertIn(response.status, [200, 201]) - - async def test_predict_data(self): - f = io.BytesIO(b"foo") - ret = await self.client.post( - "/custom/v2/models/deepaas-test/predict/", - data={"data": (f, "foo.txt"), "parameter": 1}, - ) json = await ret.json() - self.assertEqual(200, ret.status) - self.assertDictEqual(fake_responses.deepaas_test_predict, json) - async def test_train(self): - ret = await self.client.post( - "/custom/v2/models/deepaas-test/train/", data={"sleep": 0} - ) - self.assertEqual(200, ret.status) - json = await ret.json() + assert "date" in json json.pop("date") - self.assertDictEqual(fake_responses.deepaas_test_train, json) - async def test_get_metadata(self): + assert "uuid" in json + json.pop("uuid") + + assert fake_responses.deepaas_test_train == json + + async def test_get_metadata(self, client): meta = fake_responses.models_meta - ret = await self.client.get("/custom/v2/models/") - self.assert_ok(ret) - self.assertDictEqual(meta, await ret.json()) + ret = await client.get("/v2/models/") + assert ret.status in [200, 201] + assert meta == await ret.json() - ret = await self.client.get("/custom/v2/models/deepaas-test/") - self.assert_ok(ret) - self.assertDictEqual(meta["models"][0], await ret.json()) + ret = await client.get("/v2/models/deepaas-test/") + assert ret.status in [200, 201] + assert meta["models"][0] == await ret.json() -class TestApiV2(base.TestCase): - async def get_application(self): - app = web.Application(debug=True) +class TestApiV2(BaseTestApiV2): + @pytest.fixture + @staticmethod + async def application(monkeypatch): + app = web.Application() app.middlewares.append(web.normalize_path_middleware()) - deepaas.model.v2.register_models(app) + w = v2_wrapper.ModelWrapper("deepaas-test", fake_v2_model.TestModel(), app) + + monkeypatch.setattr(deepaas.model, "V2_MODELS", {"deepaas-test": w}) v2app = v2.get_app() app.add_subapp("/v2", v2app) return app - def setUp(self): - super(TestApiV2, self).setUp() - - self.maxDiff = None - - self.flags(debug=True) + async def test_not_found(self, client): + ret = await client.get("/v2/models/%s" % uuid.uuid4().hex) + assert 404 == ret.status - def assert_ok(self, response): - self.assertIn(response.status, [200, 201]) + ret = await client.put("/v2/models/%s" % uuid.uuid4().hex) + assert 404 == ret.status - async def test_not_found(self): - ret = await self.client.get("/v2/models/%s" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) + ret = await client.post("/v2/models/%s" % uuid.uuid4().hex) + assert 404 == ret.status - ret = await self.client.put("/v2/models/%s" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) + ret = await client.delete("/v2/models/%s" % uuid.uuid4().hex) + assert 404 == ret.status - ret = await self.client.post("/v2/models/%s" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) + async def test_model_not_found(self, client): + ret = await client.get("/v2/models/%s/train" % uuid.uuid4().hex) + assert 404 == ret.status - ret = await self.client.delete("/v2/models/%s" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) + ret = await client.put("/v2/models/%s/train" % uuid.uuid4().hex) + assert 404 == ret.status - async def test_model_not_found(self): - ret = await self.client.put("/v2/models/%s/train" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) + ret = await client.post("/v2/models/%s/predict" % uuid.uuid4().hex) + assert 404 == ret.status - ret = await self.client.post("/v2/models/%s/predict" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) + ret = await client.get("/v2/models/%s" % uuid.uuid4().hex) + assert 404 == ret.status - ret = await self.client.get("/v2/models/%s" % uuid.uuid4().hex) - self.assertEqual(404, ret.status) - - async def test_predict_no_parameters(self): - ret = await self.client.post("/v2/models/deepaas-test/predict/") + async def test_predict_no_parameters(self, client): + ret = await client.post("/v2/models/deepaas-test/predict/") json = await ret.json() - print(json) - self.assertDictEqual( - { - "parameter": ["Missing data for required field."], - "data": ["Missing data for required field."], - }, - json, - ) - self.assertEqual(422, ret.status) - async def test_predict_data(self): + expected = { + "parameter": ["Missing data for required field."], + "data": ["Missing data for required field."], + } + assert 422 == ret.status + assert expected == json + + async def test_predict_data(self, client): f = io.BytesIO(b"foo") - ret = await self.client.post( + ret = await client.post( "/v2/models/deepaas-test/predict/", data={"data": (f, "foo.txt"), "parameter": 1}, ) + assert 200 == ret.status + json = await ret.json() - self.assertEqual(200, ret.status) - self.assertDictEqual(fake_responses.deepaas_test_predict, json) - async def test_train(self): - ret = await self.client.post( - "/v2/models/deepaas-test/train/", data={"sleep": 0} - ) - self.assertEqual(200, ret.status) + assert "data" in json + del json["data"] + assert fake_responses.deepaas_test_predict == json + + async def test_train(self, client): + ret = await client.post("/v2/models/deepaas-test/train/", data={"sleep": 0}) + assert 200 == ret.status + json = await ret.json() + + assert "date" in json json.pop("date") - self.assertDictEqual(fake_responses.deepaas_test_train, json) - async def test_get_metadata(self): + assert "uuid" in json + json.pop("uuid") + + assert fake_responses.deepaas_test_train == json + + async def test_get_metadata(self, client): meta = fake_responses.models_meta - ret = await self.client.get("/v2/models/") - self.assert_ok(ret) - self.assertDictEqual(meta, await ret.json()) + ret = await client.get("/v2/models/") + assert ret.status in [200, 201] + + assert meta == await ret.json() + + ret = await client.get("/v2/models/deepaas-test/") + assert ret.status in [200, 201] - ret = await self.client.get("/v2/models/deepaas-test/") - self.assert_ok(ret) - self.assertDictEqual(meta["models"][0], await ret.json()) + assert meta["models"][0] == await ret.json() - async def test_bad_metods_metadata(self): - for i in (self.client.post, self.client.put, self.client.delete): + async def test_bad_metods_metadata(self, client): + for i in (client.post, client.put, client.delete): ret = await i("/v2/models/") - self.assertEqual(405, ret.status) + assert 405 == ret.status