from tests.support import ApiTestCase, db_execute class NoteTests(ApiTestCase): def setUp(self): super().setUp() self.register() self.book = self.create_book() self.notes_path = f"/api/books/{self.book['id']}/notes" def add(self, body: str, client=None): return self.call("POST", self.notes_path, {"body": body}, client) def test_add_and_list_newest_first(self): first = self.add(" Started reading ") self.assertEqual(first.status_code, 201) self.assertEqual(first.get_json()["body"], "Started reading") self.add("Chapter 3 was great") bodies = [n["body"] for n in self.call("GET", self.notes_path).get_json()] self.assertEqual(bodies, ["Chapter 3 was great", "Started reading"]) def test_edit_updates_body_and_timestamp(self): note = self.add("Draft").get_json() response = self.call("PATCH", f"/api/notes/{note['id']}", {"body": "Final"}) self.assertEqual(response.status_code, 200) edited = response.get_json() self.assertEqual(edited["body"], "Final") self.assertGreater(edited["updated_at"], edited["created_at"]) def test_delete(self): note = self.add("Temporary").get_json() self.assertEqual( self.call("DELETE", f"/api/notes/{note['id']}").status_code, 204 ) self.assertEqual(self.call("GET", self.notes_path).get_json(), []) self.assertEqual( self.call("DELETE", f"/api/notes/{note['id']}").status_code, 404 ) def test_validation(self): for body, status in ((" ", 400), ("x" * 10_001, 400), ("a\x00b", 400)): with self.subTest(length=len(body)): self.assertEqual(self.add(body).status_code, status) self.assertEqual( self.call( "POST", self.notes_path, {"body": "ok", "book_id": 2} ).status_code, 400, ) def test_missing_book_and_out_of_range_ids_are_404(self): self.assertEqual(self.call("GET", "/api/books/999/notes").status_code, 404) self.assertEqual( self.call("PATCH", "/api/notes/99999999999", {"body": "x"}).status_code, 404 ) def test_deleting_book_deletes_its_notes(self): self.add("Will be gone") self.call("DELETE", f"/api/books/{self.book['id']}") self.assertEqual(db_execute("SELECT count(*) FROM notes")[0][0], 0) class NoteIsolationTests(ApiTestCase): def test_other_user_cannot_read_add_edit_or_delete_notes(self): self.register("alice") book = self.create_book() note = self.call( "POST", f"/api/books/{book['id']}/notes", {"body": "Private"} ).get_json() bob = self.other_user("bob") attempts = ( ("GET", f"/api/books/{book['id']}/notes", None), ("POST", f"/api/books/{book['id']}/notes", {"body": "Intrusion"}), ("PATCH", f"/api/notes/{note['id']}", {"body": "Hacked"}), ("DELETE", f"/api/notes/{note['id']}", None), ) for method, path, payload in attempts: with self.subTest(method=method, path=path): self.assertEqual( self.call(method, path, payload, client=bob).status_code, 404 ) bodies = [ n["body"] for n in self.call("GET", f"/api/books/{book['id']}/notes").get_json() ] self.assertEqual(bodies, ["Private"])