- brain_reject on a parked pdf/txt moves the bytes; it no longer prepends YAML - raw/ notes are read through a cap (readCapped); an endless reader test fails instead of exhausting memory - a byte-order mark no longer hides a note's wing: key - INGEST_WING_ROUTING is parsed strictly; a typo stops the server rather than leaving routing silently off - tests for the reserved-wing check in every disguise (Sources, sources., ...) Mutation-checked. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
132 lines
4.1 KiB
Go
132 lines
4.1 KiB
Go
// ingestion/cmd/server/main_test.go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/claudewatcher"
|
|
)
|
|
|
|
func TestClaudeSink_IngestWritesToNonIndexedArchiveNotWiki(t *testing.T) {
|
|
dir := t.TempDir()
|
|
sink := &claudeSink{brainDir: dir, logger: slog.New(slog.NewTextHandler(os.Stderr, nil))}
|
|
|
|
err := sink.Ingest(context.Background(), claudewatcher.Batch{
|
|
Host: "koala",
|
|
FilePath: "/host-home-claude/projects/-home-mathias-dev/abc.jsonl",
|
|
SessionID: "abc",
|
|
ProjectID: "-home-mathias-dev",
|
|
Turns: []claudewatcher.Turn{
|
|
{Type: "assistant", Content: "did a thing"},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
got, err := os.ReadFile(filepath.Join(dir, "archive", "claude-sessions", "koala", "session-koala-abc.md"))
|
|
require.NoError(t, err, "raw session dump must land in the non-indexed archive")
|
|
assert.Contains(t, string(got), "did a thing")
|
|
|
|
_, err = os.Stat(filepath.Join(dir, "wiki", "claude-sessions"))
|
|
assert.True(t, os.IsNotExist(err), "raw transcripts must never land under wiki/ (ai-sessions#10 — BM25 pollution)")
|
|
}
|
|
|
|
// --- #107: an unconfigured alert must announce itself ----------------------
|
|
|
|
func TestAlertingWarnings(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
env map[string]string
|
|
want []string // substrings that must appear across the warnings
|
|
none bool // true = expect no warnings at all
|
|
}{
|
|
{
|
|
// The failure this exists to prevent: two alerts dark in
|
|
// production, discovered only by firing a real event and watching
|
|
// nothing arrive (#100).
|
|
name: "notifier unconfigured names what will not fire",
|
|
env: map[string]string{},
|
|
want: []string{"BRAIN_NTFY_URL", "brain_edit_root", "audit"},
|
|
},
|
|
{
|
|
name: "configured notifier is silent",
|
|
env: map[string]string{"BRAIN_NTFY_URL": "http://ntfy/brain-alerts"},
|
|
none: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := alertingWarnings(func(k string) string { return tt.env[k] })
|
|
if tt.none {
|
|
assert.Empty(t, got)
|
|
return
|
|
}
|
|
joined := strings.Join(got, " ")
|
|
for _, w := range tt.want {
|
|
assert.Contains(t, joined, w)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// hyperguild#158: the claude-session archive was written with os.MkdirAll +
|
|
// os.WriteFile by path, so a symlinked archive directory redirected it.
|
|
func TestClaudeSinkDoesNotWriteThroughASymlinkedArchiveDir(t *testing.T) {
|
|
for name, mk := range map[string]func(t *testing.T, dir string) (link, target string){
|
|
"outside": func(t *testing.T, _ string) (string, string) { o := t.TempDir(); return o, o },
|
|
"in-brain": func(t *testing.T, dir string) (string, string) {
|
|
require.NoError(t, os.MkdirAll(filepath.Join(dir, "raw"), 0o755))
|
|
return filepath.Join("..", "raw"), filepath.Join(dir, "raw")
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
link, target := mk(t, dir)
|
|
require.NoError(t, os.Symlink(link, filepath.Join(dir, "archive")))
|
|
sink := &claudeSink{brainDir: dir, logger: slog.New(slog.NewTextHandler(os.Stderr, nil))}
|
|
err := sink.Ingest(context.Background(), claudewatcher.Batch{Host: "koala", SessionID: "abc",
|
|
Turns: []claudewatcher.Turn{{Type: "assistant", Content: "x"}}})
|
|
assert.Error(t, err)
|
|
ents, _ := os.ReadDir(target)
|
|
assert.Empty(t, ents, "the archive was written at the link's target")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEnvFlag(t *testing.T) {
|
|
tests := []struct {
|
|
name, set string
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{"unset is off", "", false, false},
|
|
{"1", "1", true, false},
|
|
{"true", "true", true, false},
|
|
{"TRUE", "TRUE", true, false},
|
|
{"0", "0", false, false},
|
|
{"false", "false", false, false},
|
|
{"a typo is an error, not silently off", "ture", false, true},
|
|
{"yes is not a bool here", "yes", false, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Setenv("TEST_ENV_FLAG", tt.set)
|
|
got, err := envFlag("TEST_ENV_FLAG")
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|