diff --git a/possible-solution/client/package-lock.json b/possible-solution/client/package-lock.json index 9d943ef..e21e875 100644 --- a/possible-solution/client/package-lock.json +++ b/possible-solution/client/package-lock.json @@ -8,7 +8,7 @@ "name": "client", "version": "0.0.1", "devDependencies": { - "@sveltejs/adapter-auto": "^4.0.0", + "@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/kit": "^2.17.1", "svelte": "^5.19.10", "svelte-check": "^4.1.4", @@ -783,14 +783,11 @@ ] }, "node_modules/@sveltejs/adapter-auto": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-4.0.0.tgz", - "integrity": "sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-7.0.1.tgz", + "integrity": "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ==", "dev": true, "license": "MIT", - "dependencies": { - "import-meta-resolve": "^4.1.0" - }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } diff --git a/possible-solution/client/package.json b/possible-solution/client/package.json index c205637..23680c7 100644 --- a/possible-solution/client/package.json +++ b/possible-solution/client/package.json @@ -10,7 +10,7 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" }, "devDependencies": { - "@sveltejs/adapter-auto": "^4.0.0", + "@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/kit": "^2.17.1", "svelte": "^5.19.10", "svelte-check": "^4.1.4", diff --git a/possible-solution/server/test_app.py b/possible-solution/server/test_app.py new file mode 100644 index 0000000..71bffd7 --- /dev/null +++ b/possible-solution/server/test_app.py @@ -0,0 +1,78 @@ +import importlib +import io +import os +import sys +import unittest +from pathlib import Path +from unittest.mock import patch + + +SERVER_DIR = Path(__file__).parent + + +class FakeModel: + def __init__(self): + self.inputs = None + + def predict_proba(self, inputs): + self.inputs = inputs + return [FakePrediction()] + + +class FakePrediction: + def __str__(self): + return "[0.25 0.75]" + + +class AppRoutesTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.original_cwd = Path.cwd() + os.chdir(SERVER_DIR) + sys.path.insert(0, str(SERVER_DIR)) + cls.model = FakeModel() + + # Importing app loads the model at module scope. Keep this test + # independent of the binary model and its optional dependencies. + with patch("pickle.load", return_value=cls.model), patch( + "builtins.open", return_value=io.BytesIO() + ): + cls.app = importlib.import_module("app") + + cls.client = cls.app.app.test_client() + + @classmethod + def tearDownClass(cls): + os.chdir(cls.original_cwd) + sys.path.remove(str(SERVER_DIR)) + + def test_airports_returns_sorted_airports_with_numeric_ids(self): + response = self.client.get("/airports") + + self.assertEqual(response.status_code, 200) + airports = response.get_json() + self.assertIsInstance(airports, list) + self.assertTrue(airports) + self.assertEqual( + airports, + sorted(airports, key=lambda airport: airport["name"]), + ) + self.assertTrue(all(isinstance(airport["id"], int) for airport in airports)) + self.assertTrue(all(set(airport) == {"id", "name"} for airport in airports)) + + def test_predict_converts_query_values_and_returns_probability_fields(self): + response = self.client.get("/predict?day_of_week=3&airport_id=14771") + + self.assertEqual(response.status_code, 200) + self.assertEqual(self.model.inputs, [[3, 14771]]) + self.assertEqual(response.get_json(), {"certainty": 0.25, "delay": 0.75}) + self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*") + + def test_predict_rejects_missing_query_values(self): + response = self.client.get("/predict?day_of_week=3") + + self.assertEqual(response.status_code, 500) + + +if __name__ == "__main__": + unittest.main()