v0.7.1: in-app password change, custom labels, always-on folders
- 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>
This commit is contained in:
parent
fcd7da853f
commit
c2a354809a
14 changed files with 562 additions and 23 deletions
113
tests/views/test_settings_labels.py
Normal file
113
tests/views/test_settings_labels.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue