Team JWTs include `aud=mnemosyne` while per-turn JWTs omit `aud` entirely. Since `iss` + `typ` already partition the two token populations, explicitly skip audience verification to avoid rejecting valid tokens. Also expand test coverage for the MCP auth surface to exercise all three credential types (opaque MCPToken, per-turn JWT, team JWT), including replay cache behavior and Neo4j-backed library resolution via mocked cypher queries.
162 lines
5.2 KiB
Python
162 lines
5.2 KiB
Python
"""Tests for the ``backfill_library_memberships`` management command.
|
|
|
|
The command queries ``library.models.Library.nodes`` (Neo4j), which we
|
|
don't want to exercise in unit tests. We substitute a lightweight fake
|
|
library iterable onto ``Library.nodes.filter`` via ``unittest.mock``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from io import StringIO
|
|
from unittest import mock
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.core.management import call_command
|
|
from django.core.management.base import CommandError
|
|
from django.test import TestCase
|
|
|
|
from mcp_server.models import LibraryMembership
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class _FakeLibrary:
|
|
def __init__(self, uid: str, workspace_id=None):
|
|
self.uid = uid
|
|
self.workspace_id = workspace_id
|
|
|
|
|
|
class _FakeNodes:
|
|
"""Stand-in for the neomodel ``Library.nodes`` node set."""
|
|
|
|
def __init__(self, all_libs):
|
|
self._all = list(all_libs)
|
|
|
|
def filter(self, **kwargs):
|
|
out = list(self._all)
|
|
if "workspace_id__isnull" in kwargs:
|
|
want = kwargs["workspace_id__isnull"]
|
|
out = [l for l in out if (l.workspace_id is None) == want]
|
|
return out
|
|
|
|
|
|
def _run(*cli_args):
|
|
"""Invoke the command with stdout/stderr buffered. Returns stdout."""
|
|
buf = StringIO()
|
|
call_command(
|
|
"backfill_library_memberships", *cli_args, stdout=buf, stderr=StringIO()
|
|
)
|
|
return buf.getvalue()
|
|
|
|
|
|
class BackfillCommandTest(TestCase):
|
|
def setUp(self):
|
|
self.superuser = User.objects.create_superuser(
|
|
username="admin", password="pw", email="a@b.c"
|
|
)
|
|
self.patcher = mock.patch(
|
|
"library.models.Library.nodes",
|
|
new=_FakeNodes(
|
|
[
|
|
_FakeLibrary("lib-a"), # global
|
|
_FakeLibrary("lib-b"), # global
|
|
_FakeLibrary("lib-ws", "ws-1"), # workspace-scoped
|
|
]
|
|
),
|
|
)
|
|
self.patcher.start()
|
|
self.addCleanup(self.patcher.stop)
|
|
|
|
def test_assigns_owner_to_all_global_libraries(self):
|
|
_run()
|
|
owners = LibraryMembership.objects.filter(user=self.superuser)
|
|
self.assertEqual(
|
|
sorted(owners.values_list("library_uid", flat=True)),
|
|
["lib-a", "lib-b"],
|
|
)
|
|
self.assertTrue(
|
|
all(
|
|
m.role == LibraryMembership.Role.OWNER
|
|
for m in owners
|
|
)
|
|
)
|
|
|
|
def test_skips_workspace_scoped_libraries(self):
|
|
_run()
|
|
self.assertFalse(
|
|
LibraryMembership.objects.filter(library_uid="lib-ws").exists()
|
|
)
|
|
|
|
def test_idempotent(self):
|
|
_run()
|
|
count_after_first = LibraryMembership.objects.count()
|
|
_run()
|
|
self.assertEqual(LibraryMembership.objects.count(), count_after_first)
|
|
|
|
def test_skips_libraries_with_existing_membership(self):
|
|
# Pre-seed someone else as manager on lib-a; the command should
|
|
# not overwrite or add a second row.
|
|
other = User.objects.create_user(username="other", password="pw")
|
|
LibraryMembership.objects.create(
|
|
user=other,
|
|
library_uid="lib-a",
|
|
role=LibraryMembership.Role.MANAGER,
|
|
)
|
|
_run()
|
|
|
|
# lib-a keeps its original manager-row; the superuser is NOT
|
|
# granted on it because any membership existing is an opt-out.
|
|
memberships_for_lib_a = LibraryMembership.objects.filter(
|
|
library_uid="lib-a"
|
|
)
|
|
self.assertEqual(memberships_for_lib_a.count(), 1)
|
|
self.assertEqual(memberships_for_lib_a.first().user, other)
|
|
|
|
# lib-b had none, so the superuser got it.
|
|
self.assertTrue(
|
|
LibraryMembership.objects.filter(
|
|
library_uid="lib-b", user=self.superuser
|
|
).exists()
|
|
)
|
|
|
|
def test_dry_run_does_not_persist(self):
|
|
out = _run("--dry-run")
|
|
self.assertIn("would insert", out)
|
|
self.assertEqual(LibraryMembership.objects.count(), 0)
|
|
|
|
def test_user_arg_overrides_default_superuser(self):
|
|
target = User.objects.create_user(username="librarian", password="pw")
|
|
_run("--user", "librarian")
|
|
self.assertTrue(
|
|
LibraryMembership.objects.filter(user=target).exists()
|
|
)
|
|
self.assertFalse(
|
|
LibraryMembership.objects.filter(user=self.superuser).exists()
|
|
)
|
|
|
|
def test_unknown_user_raises_command_error(self):
|
|
with self.assertRaises(CommandError):
|
|
_run("--user", "nobody")
|
|
|
|
def test_no_superuser_raises_command_error(self):
|
|
self.superuser.is_superuser = False
|
|
self.superuser.save(update_fields=["is_superuser"])
|
|
with self.assertRaises(CommandError):
|
|
_run()
|
|
|
|
|
|
class BackfillCommandEmptyNeo4jTest(TestCase):
|
|
def setUp(self):
|
|
self.superuser = User.objects.create_superuser(
|
|
username="admin", password="pw", email="a@b.c"
|
|
)
|
|
self.patcher = mock.patch(
|
|
"library.models.Library.nodes", new=_FakeNodes([])
|
|
)
|
|
self.patcher.start()
|
|
self.addCleanup(self.patcher.stop)
|
|
|
|
def test_no_libraries_is_noop(self):
|
|
_run()
|
|
self.assertEqual(LibraryMembership.objects.count(), 0)
|