fix(store): TaskAmended applies description/priority/due, not just title (S7)
Amendments to due/description/inherent_priority were accepted and durably logged but silently discarded by the projection since store.apply only ever handled the title key. Also added the missing Task.Description field (TaskCreated never populated it either). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1rkJ2hBMybnJctPbcy4tT
This commit is contained in:
@@ -61,6 +61,7 @@ type Task struct {
|
||||
Lease *Lease `json:"lease,omitempty"`
|
||||
Version int `json:"version"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
|
||||
+15
-3
@@ -135,6 +135,9 @@ func (s *Store) apply(e domain.Event) error {
|
||||
if v, ok := p["title"].(string); ok {
|
||||
t.Title = v
|
||||
}
|
||||
if v, ok := p["description"].(string); ok {
|
||||
t.Description = v
|
||||
}
|
||||
s.external[t.Source+"\x00"+t.ExternalID] = t.ID
|
||||
case "TaskLeased":
|
||||
t.State = domain.StateLeased
|
||||
@@ -152,9 +155,18 @@ func (s *Store) apply(e domain.Event) error {
|
||||
t.State = domain.StateBlocked
|
||||
t.Lease = nil
|
||||
case "TaskAmended":
|
||||
for k, v := range p {
|
||||
if k == "title" {
|
||||
t.Title, v = v.(string)
|
||||
if v, ok := p["title"].(string); ok {
|
||||
t.Title = v
|
||||
}
|
||||
if v, ok := p["description"].(string); ok {
|
||||
t.Description = v
|
||||
}
|
||||
if v, ok := p["inherent_priority"].(float64); ok {
|
||||
t.InherentPriority = int(v)
|
||||
}
|
||||
if v, ok := p["due"].(string); ok {
|
||||
if d, err := time.Parse(time.RFC3339, v); err == nil {
|
||||
t.Due = &d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,37 @@ func TestAppendReplayAndDeduplicate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTaskAmendedAppliesAllFields guards S7: the projection previously only
|
||||
// applied "title" from a TaskAmended payload, silently discarding due,
|
||||
// description, and inherent_priority amendments even though they were
|
||||
// accepted and logged.
|
||||
func TestTaskAmendedAppliesAllFields(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
s, err := Open(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.Append(created("e1")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
amend, _ := json.Marshal(map[string]any{
|
||||
"title": "new title",
|
||||
"description": "new description",
|
||||
"inherent_priority": 5.0,
|
||||
"due": "2026-08-01T00:00:00Z",
|
||||
})
|
||||
if err := s.Append(domain.Event{Type: "TaskAmended", TaskID: "task-1", Version: 2, Payload: amend, Surface: string(authz.System)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := s.Tasks()[0]
|
||||
if tk.Title != "new title" || tk.Description != "new description" || tk.InherentPriority != 5 {
|
||||
t.Fatalf("unexpected task after amendment: %+v", tk)
|
||||
}
|
||||
if tk.Due == nil || !tk.Due.Equal(time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
t.Fatalf("unexpected due after amendment: %+v", tk.Due)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLeaseAndExpireEventIDsAreUnique guards S5: Event.ID was set to the
|
||||
// task id in both Lease and ExpireLeases, so every lease of the same task
|
||||
// produced a TaskLeased/TaskReleased event with a colliding ID — unsound
|
||||
|
||||
Reference in New Issue
Block a user