Files
mcdoc/internal/cache/cache_test.go
Kyle Isom 28afaa2c56 Implement mcdoc v0.1.0: public documentation server
Single-binary Go server that fetches markdown from Gitea (mc org),
renders to HTML with goldmark (GFM, chroma syntax highlighting,
heading anchors), and serves a navigable read-only documentation site.

Features:
- Boot fetch with retry, webhook refresh, 15-minute poll fallback
- In-memory cache with atomic per-repo swap
- chi router with htmx partial responses for SPA-like navigation
- HMAC-SHA256 webhook validation
- Responsive CSS, TOC generation, priority doc ordering
- $PORT env var support for MCP agent port assignment

33 tests across config, cache, render, and server packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 13:04:15 -07:00

166 lines
3.5 KiB
Go

package cache
import (
"testing"
"time"
"git.wntrmute.dev/mc/mcdoc/internal/render"
)
func TestSetAndGetRepo(t *testing.T) {
c := New()
info := &RepoInfo{
Name: "testrepo",
Description: "A test repo",
CommitSHA: "abc123",
FetchedAt: time.Now(),
Docs: []*Document{
{Repo: "testrepo", URLPath: "README", Title: "README", HTML: "<p>hello</p>"},
{Repo: "testrepo", URLPath: "ARCHITECTURE", Title: "Architecture", HTML: "<p>arch</p>"},
},
}
c.SetRepo(info)
got, ok := c.GetRepo("testrepo")
if !ok {
t.Fatal("expected repo to exist")
}
if got.Name != "testrepo" {
t.Fatalf("got name %q, want %q", got.Name, "testrepo")
}
if len(got.Docs) != 2 {
t.Fatalf("got %d docs, want 2", len(got.Docs))
}
}
func TestGetDocument(t *testing.T) {
c := New()
c.SetRepo(&RepoInfo{
Name: "r",
Docs: []*Document{
{Repo: "r", URLPath: "foo/bar", Title: "Bar", HTML: "<p>bar</p>"},
},
})
doc, ok := c.GetDocument("r", "foo/bar")
if !ok {
t.Fatal("expected document to exist")
}
if doc.Title != "Bar" {
t.Fatalf("got title %q, want %q", doc.Title, "Bar")
}
_, ok = c.GetDocument("r", "nonexistent")
if ok {
t.Fatal("expected document to not exist")
}
_, ok = c.GetDocument("nonexistent", "foo/bar")
if ok {
t.Fatal("expected repo to not exist")
}
}
func TestListReposSorted(t *testing.T) {
c := New()
c.SetRepo(&RepoInfo{Name: "mcr"})
c.SetRepo(&RepoInfo{Name: "abc"})
c.SetRepo(&RepoInfo{Name: "mcp"})
repos := c.ListRepos()
if len(repos) != 3 {
t.Fatalf("got %d repos, want 3", len(repos))
}
if repos[0].Name != "abc" || repos[1].Name != "mcp" || repos[2].Name != "mcr" {
t.Fatalf("unexpected order: %s, %s, %s", repos[0].Name, repos[1].Name, repos[2].Name)
}
}
func TestDocSortOrder(t *testing.T) {
c := New()
c.SetRepo(&RepoInfo{
Name: "r",
Docs: []*Document{
{URLPath: "CLAUDE"},
{URLPath: "zebra"},
{URLPath: "README"},
{URLPath: "ARCHITECTURE"},
{URLPath: "alpha"},
{URLPath: "RUNBOOK"},
},
})
repo, _ := c.GetRepo("r")
expected := []string{"README", "ARCHITECTURE", "RUNBOOK", "CLAUDE", "alpha", "zebra"}
for i, doc := range repo.Docs {
if doc.URLPath != expected[i] {
t.Errorf("position %d: got %q, want %q", i, doc.URLPath, expected[i])
}
}
}
func TestReadyState(t *testing.T) {
c := New()
if c.IsReady() {
t.Fatal("new cache should not be ready")
}
c.SetReady()
if !c.IsReady() {
t.Fatal("cache should be ready after SetReady")
}
}
func TestGetCommitSHA(t *testing.T) {
c := New()
if sha := c.GetCommitSHA("nope"); sha != "" {
t.Fatalf("expected empty sha, got %q", sha)
}
c.SetRepo(&RepoInfo{Name: "r", CommitSHA: "abc123"})
if sha := c.GetCommitSHA("r"); sha != "abc123" {
t.Fatalf("expected abc123, got %q", sha)
}
}
func TestAtomicRepoSwap(t *testing.T) {
c := New()
c.SetRepo(&RepoInfo{
Name: "r",
Docs: []*Document{{URLPath: "old", Title: "Old"}},
})
c.SetRepo(&RepoInfo{
Name: "r",
Docs: []*Document{{URLPath: "new", Title: "New"}},
})
repo, _ := c.GetRepo("r")
if len(repo.Docs) != 1 || repo.Docs[0].URLPath != "new" {
t.Fatal("expected atomic swap to replace docs")
}
}
func TestRemoveRepo(t *testing.T) {
c := New()
c.SetRepo(&RepoInfo{Name: "r"})
c.RemoveRepo("r")
_, ok := c.GetRepo("r")
if ok {
t.Fatal("expected repo to be removed")
}
}
// Ensure Document's Headings field works with the render package type.
func TestDocumentHeadingsType(t *testing.T) {
doc := &Document{
Headings: []render.Heading{
{Level: 1, ID: "title", Text: "Title"},
},
}
if doc.Headings[0].Text != "Title" {
t.Fatal("unexpected heading text")
}
}