feat: add title and description fields to capture grammar
Implement | prefix for titles and // separator for descriptions across the full stack: parser, schema, API, CLI, and web frontend. - Parser: line-aware extraction for |title, |title // desc, // leading desc, body // inline desc. URL-safe (skips :// lines). Modifiers (#tag, @time, ^card) extracted from all segments. - Schema: ALTER TABLE migration adds title, description columns - DB: Entity/EntityUpdate structs, all CRUD queries updated - API: title/description on create/update/response, body validation relaxed (title-only entries valid) - CLI: shows title as scan label when present - Web: parseInput mirrors Go parser, list shows title, detail pane renders title + description with double-click inline editing - Tests: 10 new cases (grammar, entity, API) — 71 total, all pass
This commit is contained in:
@@ -441,3 +441,99 @@ func TestResolve_NotFound(t *testing.T) {
|
||||
t.Errorf("expected ErrNotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate_WithTitleAndDescription(t *testing.T) {
|
||||
s := testStore(t)
|
||||
e := &Entity{
|
||||
Body: "body text",
|
||||
Title: ptr("nginx trick"),
|
||||
Description: ptr("always forget this"),
|
||||
Glyph: GlyphNote,
|
||||
Tags: []string{"ops"},
|
||||
}
|
||||
if err := s.Create(e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := s.Get(e.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Title == nil || *got.Title != "nginx trick" {
|
||||
t.Errorf("title: got %v", got.Title)
|
||||
}
|
||||
if got.Description == nil || *got.Description != "always forget this" {
|
||||
t.Errorf("description: got %v", got.Description)
|
||||
}
|
||||
if got.Body != "body text" {
|
||||
t.Errorf("body: got %q", got.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate_WithoutTitle(t *testing.T) {
|
||||
s := testStore(t)
|
||||
e := &Entity{Body: "just body", Glyph: GlyphNote}
|
||||
if err := s.Create(e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, _ := s.Get(e.ID)
|
||||
if got.Title != nil {
|
||||
t.Errorf("expected nil title, got %v", got.Title)
|
||||
}
|
||||
if got.Description != nil {
|
||||
t.Errorf("expected nil description, got %v", got.Description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate_Title(t *testing.T) {
|
||||
s := testStore(t)
|
||||
e := &Entity{Body: "body", Glyph: GlyphNote}
|
||||
s.Create(e)
|
||||
|
||||
newTitle := "new title"
|
||||
if err := s.Update(e.ID, &EntityUpdate{Title: &newTitle}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, _ := s.Get(e.ID)
|
||||
if got.Title == nil || *got.Title != "new title" {
|
||||
t.Errorf("title: got %v", got.Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate_Description(t *testing.T) {
|
||||
s := testStore(t)
|
||||
e := &Entity{Body: "body", Glyph: GlyphNote}
|
||||
s.Create(e)
|
||||
|
||||
newDesc := "new desc"
|
||||
if err := s.Update(e.ID, &EntityUpdate{Description: &newDesc}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, _ := s.Get(e.ID)
|
||||
if got.Description == nil || *got.Description != "new desc" {
|
||||
t.Errorf("description: got %v", got.Description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbsorb_PreservesTargetTitle(t *testing.T) {
|
||||
s := testStore(t)
|
||||
target := &Entity{Body: "target body", Title: ptr("target title"), Glyph: GlyphNote}
|
||||
source := &Entity{Body: "source body", Title: ptr("source title"), Glyph: GlyphNote}
|
||||
s.Create(target)
|
||||
s.Create(source)
|
||||
|
||||
if err := s.Absorb(target.ID, source.ID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, _ := s.Get(target.ID)
|
||||
if got.Title == nil || *got.Title != "target title" {
|
||||
t.Errorf("target title should be preserved, got %v", got.Title)
|
||||
}
|
||||
if got.Body != "target body\nsource body" {
|
||||
t.Errorf("body: got %q", got.Body)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user