Skip to content

Commit 386c4a1

Browse files
committed
RemoveTag disconnects events
1 parent dacbb2f commit 386c4a1

2 files changed

Lines changed: 90 additions & 1 deletion

File tree

e2e/tags_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package e2e
22

33
import (
44
"testing"
5+
"time"
56

67
"github.com/git-calendar/core/pkg/core"
78
"github.com/google/uuid"
@@ -161,6 +162,63 @@ func TestTag_RemoveTag_RemovesTag(t *testing.T) {
161162
assertTagEqual(t, recreated, *got)
162163
}
163164

165+
func TestTag_RemoveTag_UntagsAffectedEvents(t *testing.T) {
166+
c := newTestCore(t)
167+
168+
removedTag := newTestTag("work", "blue")
169+
otherTag := newTestTag("personal", "green")
170+
if _, err := c.CreateTag(testCalendarName, removedTag); err != nil {
171+
t.Fatalf("CreateTag removed tag failed: %v", err)
172+
}
173+
if _, err := c.CreateTag(testCalendarName, otherTag); err != nil {
174+
t.Fatalf("CreateTag other tag failed: %v", err)
175+
}
176+
177+
start := time.Date(2026, time.January, 1, 10, 0, 0, 0, time.UTC)
178+
removedTagID := removedTag.ID
179+
otherTagID := otherTag.ID
180+
tagged := createEvent(t, c, core.Event{
181+
ID: uuid.New(),
182+
Calendar: testCalendarName,
183+
Title: "Tagged event",
184+
From: start,
185+
To: start.Add(time.Hour),
186+
TagID: &removedTagID,
187+
})
188+
repeating := createEvent(t, c, core.Event{
189+
ID: uuid.New(),
190+
Calendar: testCalendarName,
191+
Title: "Tagged repeating event",
192+
From: start.AddDate(0, 0, 1),
193+
To: start.AddDate(0, 0, 1).Add(time.Hour),
194+
TagID: &removedTagID,
195+
Repeat: recurrenceWithCount(t, start.AddDate(0, 0, 1), "DAILY", 3),
196+
})
197+
unaffected := createEvent(t, c, core.Event{
198+
ID: uuid.New(),
199+
Calendar: testCalendarName,
200+
Title: "Other tag event",
201+
From: start.AddDate(0, 0, 2),
202+
To: start.AddDate(0, 0, 2).Add(time.Hour),
203+
TagID: &otherTagID,
204+
})
205+
206+
if err := c.RemoveTag(testCalendarName, removedTag.ID); err != nil {
207+
t.Fatalf("RemoveTag failed: %v", err)
208+
}
209+
210+
assertEventTag(t, c, tagged.ID, nil)
211+
assertEventTag(t, c, repeating.ID, nil)
212+
assertEventTag(t, c, unaffected.ID, &otherTag.ID)
213+
214+
if err := c.LoadCalendars(); err != nil {
215+
t.Fatalf("LoadCalendars failed: %v", err)
216+
}
217+
assertEventTag(t, c, tagged.ID, nil)
218+
assertEventTag(t, c, repeating.ID, nil)
219+
assertEventTag(t, c, unaffected.ID, &otherTag.ID)
220+
}
221+
164222
func TestTag_RemoveTag_InvalidIDReturnsError(t *testing.T) {
165223
c := newTestCore(t)
166224

@@ -188,6 +246,24 @@ func TestTag_InvalidCalendarReturnsError(t *testing.T) {
188246
}
189247
}
190248

249+
func assertEventTag(t *testing.T, c *core.Core, eventID uuid.UUID, want *uuid.UUID) {
250+
t.Helper()
251+
252+
event, err := c.GetEvent(eventID)
253+
if err != nil {
254+
t.Fatalf("GetEvent(%s) failed: %v", eventID, err)
255+
}
256+
if want == nil {
257+
if event.TagID != nil {
258+
t.Fatalf("event %s tag = %s, want nil", eventID, *event.TagID)
259+
}
260+
return
261+
}
262+
if event.TagID == nil || *event.TagID != *want {
263+
t.Fatalf("event %s tag = %v, want %s", eventID, event.TagID, *want)
264+
}
265+
}
266+
191267
func newTestTag(name string, color string) core.Tag {
192268
return core.Tag{
193269
ID: uuid.New(),

pkg/core/core_tags.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (c *Core) UpdateTag(calendar string, tag Tag) (*Tag, error) {
7373
return &tag, commitWorktree(wt, fmt.Sprintf("Updated tag %s", tag.ID))
7474
}
7575

76-
// RemoveTag deletes one tag from a calendar repository.
76+
// RemoveTag clears the tag from affected events, then deletes it from the calendar repository.
7777
func (c *Core) RemoveTag(calendar string, id uuid.UUID) error {
7878
if id == uuid.Nil {
7979
return errors.New("invalid tag id")
@@ -84,6 +84,19 @@ func (c *Core) RemoveTag(calendar string, id uuid.UUID) error {
8484
return err
8585
}
8686

87+
for _, event := range c.events {
88+
if event == nil || event.Calendar != calendar || event.TagID == nil || *event.TagID != id {
89+
continue
90+
}
91+
92+
updated := *event
93+
updated.TagID = nil
94+
if err := c.saveAndCommitEvent(&updated, fmt.Sprintf("Removed tag %s from event %s", id, event.ID)); err != nil {
95+
return fmt.Errorf("failed to remove tag from event %s: %w", event.ID, err)
96+
}
97+
*event = updated
98+
}
99+
87100
gitPath := Tag{ID: id}.getPath()
88101
if _, err := wt.Remove(gitPath); err != nil && !errors.Is(err, os.ErrNotExist) {
89102
return fmt.Errorf("failed to git remove %q: %w", gitPath, err)

0 commit comments

Comments
 (0)