diff --git a/analyzer/test_anonymous_trial.py b/analyzer/test_anonymous_trial.py index 7b05e3a..40830d6 100644 --- a/analyzer/test_anonymous_trial.py +++ b/analyzer/test_anonymous_trial.py @@ -73,7 +73,7 @@ def test_anon_grade_page_shows_trial_banner(self): """GET /grade/ for anon shows the trial banner and form.""" response = self.client.get(reverse("grade_query")) self.assertEqual(response.status_code, 200) - self.assertContains(response, "Trial mode") + self.assertContains(response, "free grades left") self.assertContains(response, "SQL Query Grader") # ---------- POST /grade/ ---------- diff --git a/analyzer/test_database_analysis.py b/analyzer/test_database_analysis.py index 37b9141..c977713 100644 --- a/analyzer/test_database_analysis.py +++ b/analyzer/test_database_analysis.py @@ -223,7 +223,7 @@ def test_database_analyze_get(self): """Test database analyze view GET request.""" response = self.client.get(reverse("database_analyze")) self.assertEqual(response.status_code, 200) - self.assertContains(response, "Database Architecture Analysis") + self.assertContains(response, "Connect a database") self.assertContains(response, "Database Engine") def test_database_analyze_requires_login(self): diff --git a/analyzer/test_feedback.py b/analyzer/test_feedback.py index 891ba25..d095db3 100644 --- a/analyzer/test_feedback.py +++ b/analyzer/test_feedback.py @@ -117,7 +117,7 @@ def test_feedback_form_display(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertContains(response, "Provide Feedback") + self.assertContains(response, "Provide feedback") self.assertContains(response, "How accurate was the analysis?") self.assertContains(response, "How useful were the recommendations?") self.assertContains(response, "How clear was the feedback?") @@ -205,7 +205,7 @@ def test_feedback_form_prepopulation(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertContains(response, "Update Your Feedback") + self.assertContains(response, "Update your feedback") # Check form has existing values (this is a basic check) self.assertContains(response, "Existing feedback") @@ -288,11 +288,11 @@ def test_feedback_analytics_display(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertContains(response, "Feedback Analytics") + self.assertContains(response, "Feedback analytics") # Template shows "Feedback will appear here" when no aggregated stats available # The analytics view may require minimum feedback threshold # Just verify page renders successfully - self.assertIn("Feedback Analytics", response.content.decode()) + self.assertIn("Feedback analytics", response.content.decode()) def test_feedback_analytics_no_data(self): """Test feedback analytics page with no feedback data.""" @@ -306,7 +306,8 @@ def test_feedback_analytics_no_data(self): self.assertEqual(response.status_code, 200) # Template shows "Feedback will appear here once users start providing ratings" self.assertContains( - response, "Feedback will appear here once users start providing ratings" + response, + "Feedback analytics will populate as users rate their analyses", ) def test_feedback_form_validation(self): @@ -330,7 +331,9 @@ def test_feedback_button_in_results(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertContains(response, "Provide Feedback") + # The detailed-feedback CTA links to submit_feedback; the visible label + # is "Detailed feedback" (was "Provide Feedback" pre-UX-pass). + self.assertContains(response, "Detailed feedback") self.assertContains( response, reverse("submit_feedback", args=[self.analysis.id]) ) diff --git a/analyzer/test_integration.py b/analyzer/test_integration.py index 369693a..8e0fac1 100644 --- a/analyzer/test_integration.py +++ b/analyzer/test_integration.py @@ -171,12 +171,13 @@ def test_full_query_grading_workflow(self): results_response, f"{analysis.grade}" ) # Grade letter is displayed self.assertContains(results_response, f"{analysis.score}") # Score is displayed - self.assertContains(results_response, "Query Analysis Results") + # Page title was retitled to "Grade results" in the UX pass. + self.assertContains(results_response, "Grade results") # Step 6: Check query history page history_response = self.client.get(reverse("query_history")) self.assertEqual(history_response.status_code, 200) - self.assertContains(history_response, "Query History") + self.assertContains(history_response, "Query history") self.assertContains(history_response, analysis.grade) self.assertContains(history_response, "SELECT") @@ -211,28 +212,24 @@ def test_poor_query_grading_workflow(self): self.assertContains(results_response, "Recommendations") def test_authentication_required(self): - """Test that authentication is required for grading pages.""" + """Test that authentication is required for protected pages. + + Note: the grade form itself is no longer login-gated — anonymous users + can grade up to ANON_TRIAL_CAP queries per session. Pages that remain + login-gated are the user-scoped ones (history, account, connections). + """ # Logout first since setUp force_login's the user self.client.logout() - # Try to access grade query page without login + # /grade/ is anonymously accessible (trial flow) grade_response = self.client.get(reverse("grade_query")) - self.assertEqual(grade_response.status_code, 302) # Redirect to login + self.assertEqual(grade_response.status_code, 200) - # Try to access history page without login + # History page still requires login (it's user-scoped) history_response = self.client.get(reverse("query_history")) - self.assertEqual(history_response.status_code, 302) # Redirect to login - - # Create an analysis to test results page - self.client.login(username="integrationuser", password="testpass123") - self.client.post(reverse("grade_query"), {"sql_query": "SELECT * FROM users;"}) - analysis = QueryAnalysis.objects.first() - self.client.logout() - - # Try to access results page without login - results_response = self.client.get(reverse("grade_results", args=[analysis.id])) - self.assertEqual(results_response.status_code, 302) # Redirect to login + self.assertEqual(history_response.status_code, 302) + self.assertTrue(history_response.url.startswith("/login/")) def test_invalid_query_handling(self): """Test handling of invalid SQL queries.""" @@ -380,10 +377,13 @@ def test_grade_display_formatting(self): analysis = QueryAnalysis.objects.first() results_response = self.client.get(reverse("grade_results", args=[analysis.id])) - # Check for grade badge and score display - self.assertContains(results_response, f"grade-{analysis.grade.lower()}") + # The grade-{letter} CSS class was retired in the UX pass; the grade + # pill is now styled via Tailwind utilities (bg-emerald-50 / bg-lime-50 + # / bg-amber-50 / bg-orange-50 / bg-red-50). Assert the visible grade + # letter and the formatted score directly. + self.assertContains(results_response, analysis.grade) self.assertContains(results_response, f"{analysis.score:.1f}") - # Check history page formatting + # History page formatting — assert grade letter appears for this row history_response = self.client.get(reverse("query_history")) - self.assertContains(history_response, f"grade-{analysis.grade.lower()}") + self.assertContains(history_response, analysis.grade) diff --git a/analyzer/test_optimization.py b/analyzer/test_optimization.py index 8c6cd69..6595201 100644 --- a/analyzer/test_optimization.py +++ b/analyzer/test_optimization.py @@ -141,11 +141,13 @@ def test_optimization_integration_workflow(self): results_response = self.client.get(reverse("grade_results", args=[analysis.id])) self.assertEqual(results_response.status_code, 200) - # Check that optimization section is present if there are issues + # Check that optimization section is present if there are issues. + # Heading / tab labels were lowercased + shortened in the UX pass; assert + # the current strings rather than the pre-pass title-cased versions. if len(analysis.issues_found) > 0: - self.assertContains(results_response, "Query Optimization Suggestions") - self.assertContains(results_response, "Optimized Query") - self.assertContains(results_response, "Side-by-Side Comparison") + self.assertContains(results_response, "Optimization suggestions") + self.assertContains(results_response, "Optimized") + self.assertContains(results_response, "Side-by-side") self.assertContains(results_response, "Explanations") def test_optimization_with_no_issues(self): diff --git a/analyzer/tests.py b/analyzer/tests.py index 0bbb130..258ad2f 100644 --- a/analyzer/tests.py +++ b/analyzer/tests.py @@ -13,11 +13,14 @@ class ParserTestCase(TestCase): def setUp(self): + # Sample MySQL logs live at the repo-root `samples/` dir, not under + # `analyzer/samples/` (no such directory exists). + repo_root = os.path.dirname(os.path.dirname(__file__)) self.sample_slow_log_path = os.path.join( - os.path.dirname(__file__), "samples", "mysql-slow-query.log" + repo_root, "samples", "mysql-slow-query.log" ) self.sample_general_log_path = os.path.join( - os.path.dirname(__file__), "samples", "mysql-general-query.log" + repo_root, "samples", "mysql-general-query.log" ) def test_parse_mysql_slow_log(self):