feat: navigate individual sections

This commit is contained in:
2026-02-23 13:39:00 +01:00
parent 0d9b7c4e7b
commit a5683428e0
4 changed files with 406 additions and 4 deletions

View File

@ -443,3 +443,139 @@ func TestGroupNotesByFolder(t *testing.T) {
t.Fatalf("unexpected notes group: %+v", groups[1])
}
}
func TestHandlerNotes_SectionLinks(t *testing.T) {
env := newTestEnv(t)
sectionNote := &note.Note{
ID: "s1",
Title: "Sections",
Content: "# Sections\nIntro\n## 2026-01-01 - First\nFirst body\n## Glossary\nTerm A\n## Glossary\nTerm B\n",
Path: "notes/sections.md",
UpdatedAt: time.Date(2026, 1, 4, 10, 0, 0, 0, time.UTC),
}
if err := env.storage.Create(sectionNote); err != nil {
t.Fatalf("create note: %v", err)
}
req := httptest.NewRequest(http.MethodGet, "/notes/s1", nil)
rec := httptest.NewRecorder()
env.handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
if !strings.Contains(body, `href="/notes/s1/sections/2026-01-01-first"`) {
t.Fatalf("expected link for first section")
}
if !strings.Contains(body, `href="/notes/s1/sections/glossary"`) {
t.Fatalf("expected link for glossary section")
}
if !strings.Contains(body, `href="/notes/s1/sections/glossary-2"`) {
t.Fatalf("expected link for duplicate glossary section")
}
}
func TestHandlerNotes_SectionRoute(t *testing.T) {
env := newTestEnv(t)
sectionNote := &note.Note{
ID: "s1",
Title: "Sections",
Content: "# Sections\nIntro\n## 2026-01-01 - First\nFirst body\n## Glossary\nTerm A\n## Glossary\nTerm B\n",
Path: "notes/sections.md",
UpdatedAt: time.Date(2026, 1, 4, 10, 0, 0, 0, time.UTC),
}
if err := env.storage.Create(sectionNote); err != nil {
t.Fatalf("create note: %v", err)
}
req := httptest.NewRequest(http.MethodGet, "/notes/s1/sections/glossary", nil)
rec := httptest.NewRecorder()
env.handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
if !strings.Contains(body, "<h2>Glossary</h2>") {
t.Fatalf("expected glossary heading to be rendered")
}
if !strings.Contains(body, "Term A") {
t.Fatalf("expected first glossary content")
}
if strings.Contains(body, "First body") {
t.Fatalf("expected other section content to be excluded")
}
if strings.Contains(body, "Term B") {
t.Fatalf("expected second glossary content to be excluded")
}
}
func TestHandlerNotes_SectionRoute_Duplicate(t *testing.T) {
env := newTestEnv(t)
sectionNote := &note.Note{
ID: "s1",
Title: "Sections",
Content: "# Sections\nIntro\n## Glossary\nTerm A\n## Glossary\nTerm B\n",
Path: "notes/sections.md",
UpdatedAt: time.Date(2026, 1, 4, 10, 0, 0, 0, time.UTC),
}
if err := env.storage.Create(sectionNote); err != nil {
t.Fatalf("create note: %v", err)
}
req := httptest.NewRequest(http.MethodGet, "/notes/s1/sections/glossary-2", nil)
rec := httptest.NewRecorder()
env.handler.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d", rec.Code)
}
body := rec.Body.String()
if !strings.Contains(body, "<h2>Glossary</h2>") {
t.Fatalf("expected glossary heading to be rendered")
}
if !strings.Contains(body, "Term B") {
t.Fatalf("expected second glossary content")
}
if strings.Contains(body, "Term A") {
t.Fatalf("expected first glossary content to be excluded")
}
}
func TestHandlerNotes_SectionRoute_NotFound(t *testing.T) {
env := newTestEnv(t)
sectionNote := &note.Note{
ID: "s1",
Title: "Sections",
Content: "# Sections\n## Alpha\nA\n",
Path: "notes/sections.md",
UpdatedAt: time.Date(2026, 1, 4, 10, 0, 0, 0, time.UTC),
}
if err := env.storage.Create(sectionNote); err != nil {
t.Fatalf("create note: %v", err)
}
req := httptest.NewRequest(http.MethodGet, "/notes/s1/sections/missing", nil)
rec := httptest.NewRecorder()
env.handler.ServeHTTP(rec, req)
if rec.Code != http.StatusNotFound {
t.Fatalf("expected status 404, got %d", rec.Code)
}
}