"""Feedback on a question: reported by a learner, worked through by an educator. Disposable SQLite. The rules worth pinning: any learner may report, only someone with editorial access may read or answer, resolving keeps the report, and the badge answers quietly for someone with no access rather than refusing. """ import unittest import test_quiz_builder as fixtures from app.models.category_grant import CategoryGrant from app.models.feedback import QuestionFeedback from app.routers import feedback class FeedbackTests(unittest.TestCase): def setUp(self): self.bank = fixtures.BuilderTests() self.bank.setUp() self.client = self.bank.client self.db = self.bank.db self.client.app.include_router(feedback.router, prefix="/feedback") self.bank.user = self.bank.owner def tearDown(self): self.bank.tearDown() def send(self, question_id=1, message="The answer key looks wrong."): return self.client.post(f"/feedback/questions/{question_id}", json={"message": message}) def test_any_learner_may_report_and_it_names_the_question(self): self.bank.user = self.bank.peer response = self.send() self.assertEqual(response.status_code, 201, response.text) row = self.db.query(QuestionFeedback).one() self.assertEqual((row.question_id, row.user_id, row.status), (1, self.bank.peer.id, "open")) def test_a_report_about_nothing_is_refused(self): self.assertEqual(self.send(question_id=9999).status_code, 404) self.assertEqual(self.client.post("/feedback/questions/1", json={"message": "no"}).status_code, 422) def test_only_an_educator_may_read_or_answer(self): self.send() row = self.db.query(QuestionFeedback).one() self.bank.user = self.bank.peer self.assertEqual(self.client.get("/feedback/questions/1").status_code, 403) self.assertEqual(self.client.patch(f"/feedback/{row.id}", json={"status": "resolved"}).status_code, 403) self.assertEqual(self.client.delete(f"/feedback/{row.id}").status_code, 403) def test_the_badge_answers_quietly_for_someone_with_no_access(self): self.send() self.bank.user = self.bank.peer body = self.client.get("/feedback/open").json() # Not a 403: the header asks for this without first asking who is asking. self.assertEqual(body, {"open": 0, "items": []}) def test_an_educator_sees_what_is_outstanding_with_the_question_it_is_about(self): self.send() self.bank.user = self.bank.mod body = self.client.get("/feedback/open").json() self.assertEqual(body["open"], 1) self.assertEqual(body["items"][0]["question_id"], 1) self.assertTrue(body["items"][0]["question_excerpt"]) self.assertEqual(body["items"][0]["from_name"], self.bank.owner.name) def test_replying_resolves_and_keeps_the_report(self): self.send() row_id = self.db.query(QuestionFeedback).one().id self.bank.user = self.bank.mod answered = self.client.patch(f"/feedback/{row_id}", json={"reply": "Fixed, thank you.", "status": "resolved"}) self.assertEqual(answered.status_code, 200, answered.text) self.assertEqual(answered.json()["reply"], "Fixed, thank you.") # Kept, not deleted: a question with a history of the same complaint # should visibly have one. self.db.expire_all() self.assertEqual(self.db.query(QuestionFeedback).count(), 1) self.assertEqual(self.client.get("/feedback/open").json()["open"], 0) def test_a_resolved_report_can_be_reopened(self): self.send() row_id = self.db.query(QuestionFeedback).one().id self.bank.user = self.bank.mod self.client.patch(f"/feedback/{row_id}", json={"status": "resolved"}) self.client.patch(f"/feedback/{row_id}", json={"status": "open"}) self.assertEqual(self.client.get("/feedback/open").json()["open"], 1) self.assertEqual(self.client.patch(f"/feedback/{row_id}", json={"status": "maybe"}).status_code, 400) def test_deleting_removes_it(self): self.send() row_id = self.db.query(QuestionFeedback).one().id self.bank.user = self.bank.mod self.assertEqual(self.client.delete(f"/feedback/{row_id}").status_code, 204) self.assertEqual(self.db.query(QuestionFeedback).count(), 0) def test_a_granted_educator_sees_only_their_own_branch(self): # Questions 1 and 2 sit under the Root branch; 6 is filed nowhere, so # no grant over the tree can reach it. self.send(question_id=1) self.send(question_id=6) self.db.add(CategoryGrant(category_id=1, user_id=self.bank.peer.id)) self.db.commit() self.bank.user = self.bank.peer body = self.client.get("/feedback/open").json() self.assertEqual(body["open"], 1) self.assertEqual(body["items"][0]["question_id"], 1) # A moderator sees both, including the one filed nowhere. self.bank.user = self.bank.mod self.assertEqual(self.client.get("/feedback/open").json()["open"], 2) if __name__ == "__main__": unittest.main()