diora-web/accounts/tests.py

92 lines
3.9 KiB
Python
Raw Permalink Normal View History

import json
from django.contrib.auth.models import User
from django.test import TestCase
from .models import ApiToken
from books.models import EBook, EBookProgress, EBookHighlights
class ApiTokenAuthMiddlewareTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='alice', password='pw12345678')
self.token = ApiToken.objects.create(user=self.user)
def test_valid_bearer_token_authenticates(self):
resp = self.client.get('/books/', HTTP_AUTHORIZATION=f'Bearer {self.token.token}')
self.assertEqual(resp.status_code, 200)
def test_missing_token_is_unauthenticated(self):
resp = self.client.get('/books/')
self.assertEqual(resp.status_code, 401)
def test_invalid_token_is_unauthenticated(self):
resp = self.client.get('/books/', HTTP_AUTHORIZATION='Bearer not-a-real-token')
self.assertEqual(resp.status_code, 401)
def test_token_scoped_to_owning_user(self):
other = User.objects.create_user(username='bob', password='pw12345678')
EBook.objects.create(user=other, meta_ct='ct', meta_iv='iv', data_ct='ct', data_iv='iv')
resp = self.client.get('/books/', HTTP_AUTHORIZATION=f'Bearer {self.token.token}')
self.assertEqual(resp.json(), [])
def test_regenerate_invalidates_old_token(self):
old = self.token.token
self.token.regenerate()
resp = self.client.get('/books/', HTTP_AUTHORIZATION=f'Bearer {old}')
self.assertEqual(resp.status_code, 401)
resp = self.client.get('/books/', HTTP_AUTHORIZATION=f'Bearer {self.token.token}')
self.assertEqual(resp.status_code, 200)
def test_regenerate_view_requires_login_and_rotates_token(self):
resp = self.client.post('/accounts/api-token/regenerate/')
self.assertEqual(resp.status_code, 302)
self.assertIn('/accounts/login/', resp.url)
self.client.login(username='alice', password='pw12345678')
old = self.token.token
resp = self.client.post('/accounts/api-token/regenerate/')
self.assertRedirects(resp, '/accounts/settings/')
self.token.refresh_from_db()
self.assertNotEqual(self.token.token, old)
class SyncSnapshotTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='alice', password='pw12345678')
self.token = ApiToken.objects.create(user=self.user)
self.book = EBook.objects.create(
user=self.user, meta_ct='meta-ct', meta_iv='meta-iv', data_ct='data-ct', data_iv='data-iv',
)
EBookProgress.objects.create(
user=self.user, book=self.book, scroll_fraction=0.5, position_anchor='3:0.5',
)
EBookHighlights.objects.create(user=self.user, book=self.book, ct='hl-ct', iv='hl-iv')
def test_requires_auth(self):
resp = self.client.get('/api/sync/')
self.assertEqual(resp.status_code, 401)
def test_snapshot_shape_and_data(self):
resp = self.client.get('/api/sync/', HTTP_AUTHORIZATION=f'Bearer {self.token.token}')
self.assertEqual(resp.status_code, 200)
data = resp.json()
for key in (
'server_time', 'books', 'book_progress', 'book_highlights', 'book_bookmarks',
'podcast_feeds', 'episode_progress', 'podcast_queue', 'saved_stations',
):
self.assertIn(key, data)
self.assertEqual(len(data['books']), 1)
self.assertEqual(data['books'][0]['meta_ct'], 'meta-ct')
self.assertEqual(len(data['book_progress']), 1)
self.assertEqual(data['book_progress'][0]['book_id'], self.book.id)
self.assertEqual(data['book_progress'][0]['position_anchor'], '3:0.5')
self.assertEqual(len(data['book_highlights']), 1)
self.assertEqual(data['book_highlights'][0]['ct'], 'hl-ct')
# No data_ct/data_iv leaked into the snapshot (book bytes stay lazy-fetched)
self.assertNotIn('data_ct', data['books'][0])