- Settings -> Account: change your own password (current + new), session
preserved via update_session_auth_hash.
- Custom labels: per-user palette in Settings -> Labels; apply/remove from a
message's ... menu (menu stays open, checkmark = applied). Labels are unified
with tags; chip colours sync to the global TagColor. New Label model +
migration 0017; POST /a/{id}/label (session, owner-scoped, palette-validated).
- Sidebar Keep/Deleted folders are now always shown; count badge only when
non-empty. Updates the stale 'hide when empty' note in CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
113 lines
5 KiB
Python
113 lines
5 KiB
Python
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from artifacts.models import Artifact, Label, TagColor
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class LabelPaletteTestCase(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(username="alice", password="pw")
|
|
self.client.force_login(self.user)
|
|
|
|
def url(self):
|
|
return reverse("settings-labels")
|
|
|
|
def test_requires_login(self):
|
|
self.client.logout()
|
|
self.assertEqual(self.client.get(self.url()).status_code, 302)
|
|
|
|
def test_create_label_sets_palette_and_tagcolor(self):
|
|
self.client.post(self.url(), {"action": "create", "name": "follow-up", "color": "#112233"})
|
|
label = Label.objects.get(user=self.user, name="follow-up")
|
|
self.assertEqual(label.color, "#112233")
|
|
self.assertEqual(TagColor.objects.get(name="follow-up").color, "#112233")
|
|
|
|
def test_create_rejects_bad_color(self):
|
|
self.client.post(self.url(), {"action": "create", "name": "x", "color": "red"})
|
|
self.assertFalse(Label.objects.filter(user=self.user).exists())
|
|
|
|
def test_create_requires_name(self):
|
|
self.client.post(self.url(), {"action": "create", "name": " ", "color": "#112233"})
|
|
self.assertFalse(Label.objects.filter(user=self.user).exists())
|
|
|
|
def test_duplicate_name_same_user_rejected(self):
|
|
Label.objects.create(user=self.user, name="dup", color="#111111")
|
|
self.client.post(self.url(), {"action": "create", "name": "dup", "color": "#222222"})
|
|
self.assertEqual(Label.objects.filter(user=self.user, name="dup").count(), 1)
|
|
|
|
def test_same_name_different_user_allowed(self):
|
|
other = User.objects.create_user(username="bob", password="pw")
|
|
Label.objects.create(user=other, name="shared", color="#111111")
|
|
self.client.post(self.url(), {"action": "create", "name": "shared", "color": "#222222"})
|
|
self.assertTrue(Label.objects.filter(user=self.user, name="shared").exists())
|
|
|
|
def test_recolor_updates_tagcolor(self):
|
|
label = Label.objects.create(user=self.user, name="urgent", color="#111111")
|
|
self.client.post(self.url(), {"action": "update", "label_id": label.pk, "color": "#ff0000"})
|
|
label.refresh_from_db()
|
|
self.assertEqual(label.color, "#ff0000")
|
|
self.assertEqual(TagColor.objects.get(name="urgent").color, "#ff0000")
|
|
|
|
def test_delete_removes_from_palette_only(self):
|
|
label = Label.objects.create(user=self.user, name="temp", color="#111111")
|
|
art = Artifact.objects.create(
|
|
id="lbl00001", owner=self.user, source="agent", title="T", kind=Artifact.MARKDOWN,
|
|
day=timezone.localdate(), path="agent/lbl00001.html", original_kind=Artifact.MARKDOWN,
|
|
tags=["temp"],
|
|
)
|
|
self.client.post(self.url(), {"action": "delete", "label_id": label.pk})
|
|
self.assertFalse(Label.objects.filter(pk=label.pk).exists())
|
|
art.refresh_from_db()
|
|
self.assertEqual(art.tags, ["temp"]) # chip stays on the message
|
|
|
|
|
|
class ToggleLabelTestCase(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(username="alice", password="pw")
|
|
self.other = User.objects.create_user(username="bob", password="pw")
|
|
self.client.force_login(self.user)
|
|
self.label = Label.objects.create(user=self.user, name="star", color="#abcdef")
|
|
self.art = Artifact.objects.create(
|
|
id="tog00001", owner=self.user, source="agent", title="T", kind=Artifact.MARKDOWN,
|
|
day=timezone.localdate(), path="agent/tog00001.html", original_kind=Artifact.MARKDOWN,
|
|
)
|
|
|
|
def toggle(self, art_id, name):
|
|
return self.client.post(reverse("artifact-toggle-label", args=[art_id]), {"name": name})
|
|
|
|
def test_toggle_on_then_off(self):
|
|
r = self.toggle(self.art.id, "star")
|
|
self.assertEqual(r.status_code, 200)
|
|
self.assertTrue(r.json()["applied"])
|
|
self.art.refresh_from_db()
|
|
self.assertIn("star", self.art.tags)
|
|
|
|
r = self.toggle(self.art.id, "star")
|
|
self.assertFalse(r.json()["applied"])
|
|
self.art.refresh_from_db()
|
|
self.assertNotIn("star", self.art.tags)
|
|
|
|
def test_toggle_syncs_tagcolor(self):
|
|
self.toggle(self.art.id, "star")
|
|
self.assertEqual(TagColor.objects.get(name="star").color, "#abcdef")
|
|
|
|
def test_name_not_in_palette_rejected(self):
|
|
r = self.toggle(self.art.id, "nope")
|
|
self.assertEqual(r.status_code, 400)
|
|
self.art.refresh_from_db()
|
|
self.assertEqual(self.art.tags, [])
|
|
|
|
def test_owner_scoped(self):
|
|
theirs = Artifact.objects.create(
|
|
id="tog00002", owner=self.other, source="agent", title="T", kind=Artifact.MARKDOWN,
|
|
day=timezone.localdate(), path="agent/tog00002.html", original_kind=Artifact.MARKDOWN,
|
|
)
|
|
self.assertEqual(self.toggle(theirs.id, "star").status_code, 404)
|
|
|
|
def test_requires_login(self):
|
|
self.client.logout()
|
|
self.assertEqual(self.toggle(self.art.id, "star").status_code, 302)
|