fix(ingestion): close the PR #159 review findings on the symlink tripwire
- Startup passed on ANY scan error: WalkDir stopped at the first unreadable dir and Scan discarded what it had found, while search kept walking and served a planted symlink. Scan now records unreadable dirs and keeps going; startup refuses on symlinks or unreadable dirs and only lets a missing brain dir pass. - Watch went silent while a scan error persisted, and marked a set as reported before Notify, so an ntfy outage lost the alert. It now alerts on unreadable dirs and scan errors too, and records a set as reported only after Notify succeeds (retried each tick). - Only the root .git is skipped; a nested .git is scanned. - A non-positive BRAIN_SYMLINK_SCAN_INTERVAL fell into time.NewTicker and crashlooped the server; it now falls back to the default. - Findings are quoted in alerts, so a newline in a name cannot merge two sets; alertingWarnings names the tripwire when ntfy is unset. - Test names match what they assert (two-set dedupe, symlinked entries inside a symlinked root, nested .git). Flipped: startup ignoring unreadable dirs, .git skipped at any depth, and no retry each turn a test red. Watch tests stable over -count=10 and under -race. Refs #157 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
)
|
||||
|
||||
// hyperguild#157: the server refuses to start while a symlink exists in the
|
||||
// brain dir (Mathias's chosen tripwire), naming it; a clean brain starts.
|
||||
// brain dir, or while any part of it cannot be scanned (which could hide
|
||||
// one); a missing brain dir is the only thing it lets pass.
|
||||
func TestStartupSymlinkCheckRefusesABrainWithASymlink(t *testing.T) {
|
||||
brain := t.TempDir()
|
||||
require.NoError(t, os.Symlink(t.TempDir(), filepath.Join(brain, "planted")))
|
||||
@@ -19,8 +21,35 @@ func TestStartupSymlinkCheckRefusesABrainWithASymlink(t *testing.T) {
|
||||
assert.Contains(t, err.Error(), "planted")
|
||||
}
|
||||
|
||||
func TestStartupSymlinkCheckPassesACleanBrain(t *testing.T) {
|
||||
func TestStartupSymlinkCheckRefusesABrainWithAnUnreadableDir(t *testing.T) {
|
||||
if os.Geteuid() == 0 {
|
||||
t.Skip("root reads mode-000 directories")
|
||||
}
|
||||
brain := t.TempDir()
|
||||
locked := filepath.Join(brain, "aaa")
|
||||
require.NoError(t, os.MkdirAll(locked, 0o755))
|
||||
require.NoError(t, os.Chmod(locked, 0o000))
|
||||
t.Cleanup(func() { _ = os.Chmod(locked, 0o755) })
|
||||
err := startupSymlinkCheck(brain)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "aaa")
|
||||
}
|
||||
|
||||
func TestStartupSymlinkCheckPassesACleanOrMissingBrain(t *testing.T) {
|
||||
brain := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(brain, "n.md"), []byte("x"), 0o644))
|
||||
assert.NoError(t, startupSymlinkCheck(brain))
|
||||
assert.NoError(t, startupSymlinkCheck(filepath.Join(brain, "missing")))
|
||||
}
|
||||
|
||||
func TestSymlinkScanIntervalFallsBackOnNonPositive(t *testing.T) {
|
||||
for _, v := range []string{"0", "-5", "", "junk"} {
|
||||
got := symlinkScanInterval(func(string) string { return v })
|
||||
assert.Positive(t, got, "env %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertingWarningsNamesTheSymlinkTripwire(t *testing.T) {
|
||||
w := strings.Join(alertingWarnings(func(string) string { return "" }), " ")
|
||||
assert.Contains(t, w, "symlink tripwire")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user