Add reviewed resolution for missing user identity links
This commit is contained in:
@@ -44,6 +44,61 @@ class IdentityReviewTests(TempDatabaseMixin, unittest.IsolatedAsyncioTestCase):
|
||||
def row(self, report):
|
||||
return next(row for row in report["rows"] if row["user"]["id"] == self.user_id)
|
||||
|
||||
async def test_manual_selection_resolves_different_username_without_guessing(self):
|
||||
self.jf['users'][0]['name'] = 'Different Jellyfin name'
|
||||
before = review.read_snapshot()
|
||||
report, _ = self.build()
|
||||
self.assertEqual(self.row(report)['state'], 'unlinked')
|
||||
report = review.build_report(before, self.jf, self.seerr, self.js, self.runtime, {self.user_id: JF})
|
||||
self.assertTrue(self.row(report)['can_confirm'])
|
||||
self.assertEqual(review.read_snapshot(), before)
|
||||
review.save_confirmations(report, before, self.runtime, [self.user_id], ADMIN)
|
||||
self.assertEqual(linked_user_id('Georgia', self.runtime.jellyfin_base_url), JF)
|
||||
self.assertEqual(self.row(self.build()[0])['state'], 'confirmed')
|
||||
|
||||
async def test_manual_selection_cannot_replace_stored_or_confirmed_identity(self):
|
||||
self.jf['users'].append({'id': OTHER, 'name': 'Other'})
|
||||
self.seerr['users'].append({'id': 21, 'name': 'Other', 'jellyfin_id': OTHER})
|
||||
self.js[OTHER] = {'state': 'matched', 'id': OTHER}
|
||||
report, local = self.build()
|
||||
review.save_confirmations(report, local, self.runtime, [self.user_id], ADMIN)
|
||||
local = review.read_snapshot()
|
||||
report = review.build_report(local, self.jf, self.seerr, self.js, self.runtime, {self.user_id: OTHER})
|
||||
self.assertFalse(self.row(report)['can_confirm'])
|
||||
with self.assertRaises(HTTPException):
|
||||
review.save_confirmations(report, local, self.runtime, [self.user_id], ADMIN)
|
||||
self.assertEqual(review.read_snapshot(), local)
|
||||
|
||||
async def test_manual_selection_checks_missing_ids_and_duplicate_owners(self):
|
||||
self.jf['users'][0]['name'] = 'Different'
|
||||
for state in ['missing', 'unavailable', 'not_configured']:
|
||||
self.js[JF] = {'state': state}
|
||||
report = review.build_report(review.read_snapshot(), self.jf, self.seerr, self.js, self.runtime, {self.user_id: JF})
|
||||
self.assertFalse(self.row(report)['can_confirm'])
|
||||
self.js[JF] = {'state': 'matched', 'id': JF}
|
||||
db.create_user('Owner', 'password', auth_provider='local', jellyseerr_user_id=20)
|
||||
report = review.build_report(review.read_snapshot(), self.jf, self.seerr, self.js, self.runtime, {self.user_id: JF})
|
||||
self.assertEqual(self.row(report)['state'], 'conflict')
|
||||
report = review.build_report(review.read_snapshot(), self.jf, self.seerr, self.js, self.runtime, {self.user_id: OTHER})
|
||||
self.assertFalse(self.row(report)['can_confirm'])
|
||||
with self.assertRaises(HTTPException) as error:
|
||||
review.build_report(review.read_snapshot(), self.jf, self.seerr, self.js, self.runtime, {999: JF})
|
||||
self.assertEqual(error.exception.status_code, 404)
|
||||
|
||||
async def test_resolution_rechecks_live_services_and_rejects_changed_selection(self):
|
||||
with patch.object(review, 'jellyfin_directory', new_callable=AsyncMock, return_value=self.jf), \
|
||||
patch.object(review, 'seerr_directory', new_callable=AsyncMock, return_value=self.seerr), \
|
||||
patch.object(review.JellystatClient, 'check_user_ids', new_callable=AsyncMock, return_value=self.js):
|
||||
before = review.read_snapshot()
|
||||
preview = await review.resolve_identity(self.user_id, JF)
|
||||
self.assertEqual(review.read_snapshot(), before)
|
||||
with self.assertRaises(HTTPException) as error:
|
||||
await review.resolve_identity(self.user_id, OTHER, preview['revision'], ADMIN)
|
||||
self.assertEqual(error.exception.status_code, 409)
|
||||
self.assertEqual(review.read_snapshot(), before)
|
||||
result = await review.resolve_identity(self.user_id, JF, preview['revision'], ADMIN)
|
||||
self.assertEqual(result['confirmed'], 1)
|
||||
|
||||
async def test_georgia_preview_is_read_only_and_uses_seerr_jellyfin_id(self):
|
||||
before = review.read_snapshot()
|
||||
report, _ = self.build()
|
||||
@@ -258,6 +313,18 @@ class IdentityRouteTests(unittest.TestCase):
|
||||
self.assertEqual(client.get("/admin/identities").status_code, status)
|
||||
self.assertEqual(client.post("/admin/identities/confirm", json={"revision": "a" * 64, "user_ids": [1]}).status_code, status)
|
||||
|
||||
def test_resolution_requires_admin_and_strict_ids(self):
|
||||
for endpoint in ['check', 'confirm']:
|
||||
body = {'user_id': 1, 'jellyfin_user_id': JF}
|
||||
if endpoint == 'confirm': body['revision'] = 'a' * 64
|
||||
for role, status in [(None, 401), ('user', 403)]:
|
||||
self.assertEqual(self.client(role).post('/admin/identities/resolve/' + endpoint, json=body).status_code, status)
|
||||
for invalid in [{'user_id': True}, {'jellyfin_user_id': 'invalid'}, {'seerr_user_id': 22}]:
|
||||
self.assertEqual(self.client('admin').post('/admin/identities/resolve/' + endpoint, json={**body, **invalid}).status_code, 422)
|
||||
with patch.object(identities, 'resolve_identity', new_callable=AsyncMock, return_value={'row': {}}):
|
||||
result = self.client('admin').post('/admin/identities/resolve/check', json={'user_id': 1, 'jellyfin_user_id': JF})
|
||||
self.assertEqual(result.headers['cache-control'], 'no-store')
|
||||
|
||||
def test_no_store_and_no_browser_supplied_identity(self):
|
||||
with patch.object(identities, "review_identities", new_callable=AsyncMock, return_value=({"rows": []}, {}, None)):
|
||||
response = self.client("admin").get("/admin/identities")
|
||||
|
||||
Reference in New Issue
Block a user