From 1c9c218dbf96b6dc3dda5c4728b0e2ecaaee5596 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 5 Oct 2026 21:03:31 +0200 Subject: [PATCH] test(tunnel): pin the raced read, the walk error and the _index.md skip (#174) Review round 1 on PR #186: - The swap test's static note had the same title as the raced note, so the harness's "inside was read" floor passed without reading r.md (skipping r.md stayed green). The static note now has its own title. - An unreadable directory under wiki/ reaches the walk-error path, and swallowing that error stayed green. New test asserts the error. - The FIFO test now runs with and without a writer held open. - _index.md skip had no test; new test. - index.go: the double blank line left by removing readTitleAndCreated. Co-Authored-By: Claude Opus 5.5 --- ingestion/internal/brain/detect_root_test.go | 90 +++++++++++++++----- ingestion/internal/brain/index.go | 1 - 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/ingestion/internal/brain/detect_root_test.go b/ingestion/internal/brain/detect_root_test.go index 93d5162..c5f451c 100644 --- a/ingestion/internal/brain/detect_root_test.go +++ b/ingestion/internal/brain/detect_root_test.go @@ -19,25 +19,37 @@ import ( // every note with a blocking os.Open, so a FIFO named *.md anywhere under // wiki/ hung every brain_write and brain_update (wikiUpkeep -> AutoTunnel), // and a symlinked note's title became a tunnel candidate. -func TestDetectTunnelsReturnsPromptlyWithAFIFOUnderWiki(t *testing.T) { - b := t.TempDir() - facts := filepath.Join(b, "wiki", "w", "facts") - require.NoError(t, os.MkdirAll(facts, 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: Zqinside Phrase\n---\n"), 0o644)) - require.NoError(t, syscall.Mkfifo(filepath.Join(facts, "f.md"), 0o644)) - done := make(chan []brain.TunnelCandidate, 1) - go func() { - cs, _ := brain.DetectTunnels(b, "text mentioning zqinside phrase here") - done <- cs - }() - select { - case cs := <-done: - assert.Contains(t, fmt.Sprintf("%+v", cs), "Zqinside Phrase", "positive control") - case <-time.After(3 * time.Second): - if r, err := os.OpenFile(filepath.Join(facts, "f.md"), os.O_WRONLY|syscall.O_NONBLOCK, 0); err == nil { - _ = r.Close() - } - t.Fatal("DetectTunnels blocked on a FIFO") +func TestDetectTunnelsReturnsPromptlyWithAFIFOUnderWikiWithAndWithoutAWriter(t *testing.T) { + for _, withWriter := range []bool{false, true} { + t.Run(fmt.Sprintf("writer=%v", withWriter), func(t *testing.T) { + b := t.TempDir() + facts := filepath.Join(b, "wiki", "w", "facts") + require.NoError(t, os.MkdirAll(facts, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: Zqinside Phrase\n---\n"), 0o644)) + fifo := filepath.Join(facts, "f.md") + require.NoError(t, syscall.Mkfifo(fifo, 0o644)) + if withWriter { + // A writer held open: a reader that opens the FIFO gets no + // EOF and parks, unless the FIFO is never opened. + w, err := os.OpenFile(fifo, os.O_RDWR, 0) + require.NoError(t, err) + t.Cleanup(func() { _ = w.Close() }) + } + done := make(chan []brain.TunnelCandidate, 1) + go func() { + cs, _ := brain.DetectTunnels(b, "text mentioning zqinside phrase here") + done <- cs + }() + select { + case cs := <-done: + assert.Contains(t, fmt.Sprintf("%+v", cs), "Zqinside Phrase", "positive control") + case <-time.After(3 * time.Second): + if r, err := os.OpenFile(fifo, os.O_WRONLY|syscall.O_NONBLOCK, 0); err == nil { + _ = r.Close() + } + t.Fatal("DetectTunnels blocked on a FIFO") + } + }) } } @@ -45,16 +57,18 @@ func TestDetectTunnelsRefusesSymlinkedNotesAndSurvivesSwapRaces(t *testing.T) { b := t.TempDir() facts := filepath.Join(b, "wiki", "w", "facts") require.NoError(t, os.MkdirAll(facts, 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: Zqinside Phrase\n---\n"), 0o644)) + // The static note's title differs from the raced note's, so the + // harness's "inside was read" floor can only be met by reading r.md. + require.NoError(t, os.WriteFile(filepath.Join(facts, "ok.md"), []byte("---\ntitle: Zqstatic Words\n---\n"), 0o644)) require.NoError(t, os.Symlink(brainfstest.Outside(t), filepath.Join(facts, "out.md"))) inBrain := brainfstest.WriteSentinelAt(t, filepath.Join(b, "raw", "s.md")) require.NoError(t, os.Symlink(filepath.Join("..", "..", "..", "raw", "s.md"), filepath.Join(facts, "in.md"))) - content := "zqinside phrase and " + brainfstest.Sentinel + content := "zqstatic words, zqinside phrase and " + brainfstest.Sentinel cs, err := brain.DetectTunnels(b, content) require.NoError(t, err) got := fmt.Sprintf("%+v", cs) - assert.Contains(t, got, "Zqinside Phrase", "positive control") + assert.Contains(t, got, "Zqstatic Words", "positive control") assert.NotContains(t, got, brainfstest.Sentinel) p := filepath.Join(facts, "r.md") @@ -77,3 +91,35 @@ func TestDetectTunnelsSkipsAWikiThatIsNotARealDirectory(t *testing.T) { require.NoError(t, err) assert.Empty(t, cs) } + +// An unreadable directory under wiki/ is a walk error and DetectTunnels +// reports it, as filepath.WalkDir did; it is not silently dropped. +func TestDetectTunnelsReportsAnUnreadableDirectoryUnderWiki(t *testing.T) { + if os.Geteuid() == 0 { + t.Skip("root ignores directory permissions") + } + b := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(b, "wiki", "w", "facts"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(b, "wiki", "w", "facts", "ok.md"), []byte("---\ntitle: Zqinside Phrase\n---\n"), 0o644)) + locked := filepath.Join(b, "wiki", "other", "locked") + require.NoError(t, os.MkdirAll(locked, 0o755)) + require.NoError(t, os.Chmod(locked, 0o000)) + t.Cleanup(func() { _ = os.Chmod(locked, 0o755) }) + _, err := brain.DetectTunnels(b, "zqinside phrase") + assert.ErrorIs(t, err, os.ErrPermission) +} + +// _index.md files are generated, not notes: their title never becomes a +// candidate. +func TestDetectTunnelsSkipsIndexFiles(t *testing.T) { + b := t.TempDir() + w := filepath.Join(b, "wiki", "w") + require.NoError(t, os.MkdirAll(filepath.Join(w, "facts"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(w, "_index.md"), []byte("---\ntitle: Zqindex Words\n---\n"), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(w, "facts", "ok.md"), []byte("---\ntitle: Zqinside Phrase\n---\n"), 0o644)) + cs, err := brain.DetectTunnels(b, "zqindex words and zqinside phrase") + require.NoError(t, err) + got := fmt.Sprintf("%+v", cs) + assert.Contains(t, got, "Zqinside Phrase", "positive control") + assert.NotContains(t, got, "Zqindex") +} diff --git a/ingestion/internal/brain/index.go b/ingestion/internal/brain/index.go index 020b164..d700b74 100644 --- a/ingestion/internal/brain/index.go +++ b/ingestion/internal/brain/index.go @@ -116,7 +116,6 @@ func collectWingEntries(bfs *brainfs.Brain, wingRel string) ([]noteEntry, error) return out, nil } - // readTitleAndCreatedIn is readTitleAndCreated through the brain root, // regular files only (hyperguild#157). func readTitleAndCreatedIn(bfs *brainfs.Brain, rel, slug string) (string, string) {