import os import sys import unittest BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if BACKEND_DIR not in sys.path: sys.path.insert(0, BACKEND_DIR) from server import app, model_service class ServerTests(unittest.TestCase): @classmethod def setUpClass(cls): app.config.update(TESTING=True) cls.client = app.test_client() def test_grouped_metrics_have_no_page_leakage(self): response = self.client.get("/api/metrics") self.assertEqual(response.status_code, 200) metrics = response.get_json() self.assertEqual(metrics["groupOverlap"], 0) self.assertIn("page_id", metrics["splitMethod"]) self.assertTrue(metrics["calibrated"]) self.assertGreater(metrics["charFeatureCount"], 0) def test_single_text_classification_is_calibrated(self): response = self.client.post( "/api/analyze-text", json={"text": "Only two investment slots left. Act now."}, ) self.assertEqual(response.status_code, 200) result = response.get_json() self.assertTrue(result["isDarkPattern"]) self.assertIn(result["prediction"], model_service.classes) self.assertTrue(result["calibrated"]) self.assertGreaterEqual(result["confidence"], 0) self.assertLessEqual(result["confidence"], 100) def test_batch_classification_preserves_ids(self): response = self.client.post( "/api/analyze-texts", json={ "items": [ {"id": "cta", "text": "Hurry, only 2 left!"}, { "id": "statement", "text": "Your monthly statement is ready.", }, ] }, ) self.assertEqual(response.status_code, 200) payload = response.get_json() self.assertEqual(payload["analyzed"], 2) self.assertEqual( [result["id"] for result in payload["results"]], ["cta", "statement"], ) def test_remote_image_fetch_is_blocked(self): response = self.client.post( "/api/analyze", json={"imageUrl": "https://example.com/private-image.png"}, ) self.assertEqual(response.status_code, 400) self.assertIn( "Remote image URLs are disabled", response.get_json()["error"] ) def test_cors_allows_extension_and_rejects_unrelated_sites(self): allowed = self.client.options( "/api/analyze-texts", headers={ "Origin": "chrome-extension://abcdefghijklmnopabcdefghijklmnop", "Access-Control-Request-Method": "POST", }, ) self.assertEqual( allowed.headers.get("Access-Control-Allow-Origin"), "chrome-extension://abcdefghijklmnopabcdefghijklmnop", ) denied = self.client.options( "/api/analyze-texts", headers={ "Origin": "https://unrelated.example", "Access-Control-Request-Method": "POST", }, ) self.assertIsNone(denied.headers.get("Access-Control-Allow-Origin")) def test_sample_ocr_response_schema(self): response = self.client.post( "/api/analyze", json={ "imageUrl": ( "http://127.0.0.1:8000/api/samples/checkout_urgency.png" ) }, ) self.assertEqual(response.status_code, 200) payload = response.get_json() self.assertGreaterEqual(payload["ocrMinimumConfidence"], 0) self.assertIsInstance(payload["darkPatterns"], list) self.assertIn("complianceReport", payload) if __name__ == "__main__": unittest.main()