"""Public share-link endpoints: token lifecycle and public landing data.""" import unittest from datetime import datetime import test_quiz_builder as fixtures from app.models.quiz import Quiz from app.models.quiz_question_link import QuizQuestionLink from app.routers import quizzes, share class ShareLinkTests(unittest.TestCase): def setUp(self): self.bank = fixtures.BuilderTests() self.bank.setUp() self.client = self.bank.client self.client.app.include_router(quizzes.router, prefix='/quizzes') self.client.app.include_router(share.router, prefix='/share') def tearDown(self): self.bank.tearDown() def test_owner_only_token_lifecycle_and_public_landing(self): self.bank.user = self.bank.owner self.assertEqual(self.client.post('/quizzes/1/share-link').status_code, 403) self.bank.user = self.bank.mod created = self.client.post('/quizzes/1/share-link') self.assertEqual(created.status_code, 200, created.text) token = created.json()['token'] self.assertEqual(self.client.post('/quizzes/1/share-link').json()['token'], token) # Stable token. public = self.client.get(f'/share/{token}') self.assertEqual(public.status_code, 200, public.text) data = public.json() self.assertEqual(data['quiz_id'], 1) self.assertEqual(data['title'], 'Origin') self.assertNotIn('questions', data) self.assertNotIn('correct_answer', str(data)) self.assertEqual(self.client.get('/share/unknown-token').status_code, 404) self.bank.user = self.bank.peer detail = self.client.get('/quizzes/1') self.assertEqual(detail.json()['share_token'], token) self.assertEqual(self.client.delete('/quizzes/1/share-link').status_code, 403) self.bank.user = self.bank.mod self.assertEqual(self.client.delete('/quizzes/1/share-link').status_code, 204) self.assertEqual(self.client.get(f'/share/{token}').status_code, 404) self.assertEqual(self.bank.db.get(Quiz, 1).is_shared, 0) self.assertEqual(self.client.get('/quizzes/1').json()['share_token'], None) def test_a_quiz_holding_a_removed_question_cannot_share(self): self.bank.user = self.bank.mod # A session carrying a question nobody can reach cannot be handed on. # That used to mean one its author kept back; it means a removed one. mixed = Quiz(title='Removed mix', user_id=3, is_published=1, questions_count=1) self.bank.db.add(mixed) self.bank.db.flush() self.bank.db.add(QuizQuestionLink(quiz_id=mixed.id, question_id=4, position=0)) self.bank.db.get(fixtures.Question, 4).deleted_at = datetime(2026, 1, 1) self.bank.db.commit() self.assertEqual(self.client.post(f'/quizzes/{mixed.id}/share-link').status_code, 400) mixed.is_shared = 1 # Stale share flag alone must not make it public. self.bank.db.commit() self.assertEqual(self.client.post(f'/quizzes/{mixed.id}/share-link').status_code, 400) self.assertEqual(self.bank.db.get(Quiz, mixed.id).share_token, None) if __name__ == '__main__': unittest.main()