Spaces:
Running
Running
| """Integration tests for the Flask API validation endpoints. | |
| Run with: python -m unittest tests.test_api -v | |
| Slow tests (live yfinance) are marked; skip them with: | |
| python -m unittest tests.test_api.TestAPIFast -v | |
| """ | |
| import sys | |
| import os | |
| import json | |
| import unittest | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) | |
| # Import the Flask app (IRIS_System init may fail in CI — that's fine) | |
| from app import app as flask_app | |
| class TestAPIFast(unittest.TestCase): | |
| """Tests that don't require live network access.""" | |
| def setUp(self): | |
| flask_app.config["TESTING"] = True | |
| self.client = flask_app.test_client() | |
| def test_health_endpoint(self): | |
| """GET /api/health should return 200 with ticker_db_loaded=true.""" | |
| resp = self.client.get("/api/health") | |
| self.assertEqual(resp.status_code, 200) | |
| data = json.loads(resp.data) | |
| self.assertIn("status", data) | |
| self.assertEqual(data["status"], "ok") | |
| self.assertTrue(data.get("ticker_db_loaded"), "Ticker DB should be loaded") | |
| self.assertGreater(data.get("ticker_count", 0), 0) | |
| def test_validate_missing_body(self): | |
| """POST /api/validate-ticker with no body should return valid=false.""" | |
| resp = self.client.post("/api/validate-ticker", | |
| content_type="application/json", | |
| data=json.dumps({})) | |
| self.assertEqual(resp.status_code, 200) | |
| data = json.loads(resp.data) | |
| self.assertFalse(data.get("valid")) | |
| def test_validate_invalid_format(self): | |
| """POST /api/validate-ticker with bad format should return valid=false immediately.""" | |
| resp = self.client.post("/api/validate-ticker", | |
| content_type="application/json", | |
| data=json.dumps({"ticker": "123ABC"})) | |
| self.assertEqual(resp.status_code, 200) | |
| data = json.loads(resp.data) | |
| self.assertFalse(data.get("valid")) | |
| self.assertIn("error", data) | |
| def test_analyze_rejects_invalid_ticker(self): | |
| """GET /api/analyze with a clearly invalid ticker should return 422.""" | |
| resp = self.client.get("/api/analyze?ticker=XYZQW") | |
| self.assertEqual(resp.status_code, 422) | |
| data = json.loads(resp.data) | |
| self.assertFalse(data.get("valid")) | |
| self.assertIn("error", data) | |
| class TestAPISlow(unittest.TestCase): | |
| """Tests that hit live yfinance — skip in CI with -k 'not slow'.""" | |
| def setUp(self): | |
| flask_app.config["TESTING"] = True | |
| self.client = flask_app.test_client() | |
| def test_validate_valid_ticker(self): | |
| """POST /api/validate-ticker with AAPL should return valid=true.""" | |
| resp = self.client.post("/api/validate-ticker", | |
| content_type="application/json", | |
| data=json.dumps({"ticker": "AAPL"})) | |
| self.assertEqual(resp.status_code, 200) | |
| data = json.loads(resp.data) | |
| self.assertTrue(data.get("valid")) | |
| self.assertEqual(data.get("ticker"), "AAPL") | |
| self.assertIn("company_name", data) | |
| def test_validate_invalid_ticker(self): | |
| """POST /api/validate-ticker with XYZQW should return valid=false with error.""" | |
| resp = self.client.post("/api/validate-ticker", | |
| content_type="application/json", | |
| data=json.dumps({"ticker": "XYZQW"})) | |
| self.assertEqual(resp.status_code, 200) | |
| data = json.loads(resp.data) | |
| self.assertFalse(data.get("valid")) | |
| self.assertIn("error", data) | |
| def test_analyze_accepts_valid_ticker(self): | |
| """GET /api/analyze with AAPL should NOT return 422 (validation must pass).""" | |
| resp = self.client.get("/api/analyze?ticker=AAPL") | |
| self.assertNotEqual(resp.status_code, 422, | |
| "Validation gate should not reject AAPL") | |
| if __name__ == "__main__": | |
| unittest.main() | |