package watcher import ( "context" "fmt" "os" "path/filepath" "regexp" "strings" "sync/atomic" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/mathiasbq/hyperguild/ingestion/internal/pipeline" "github.com/mathiasbq/hyperguild/ingestion/internal/wing" ) // These tests are the sprint's goal (hyperguild#209) stated as behaviour, with // the real pipeline and real file layout and only the model stubbed: a person // drops a clip into raw/ and does nothing else. func acceptanceCfg(t *testing.T) (Config, string, *atomic.Int32) { t.Helper() brainDir := t.TempDir() var calls atomic.Int32 // Each call extracts a source with its own title, as real clips have: two // sources with one title are deliberately merged into one page by the // pipeline's title resolution. pagesFor := func(n int32) []pipeline.RawPage { return []pipeline.RawPage{ {Title: fmt.Sprintf("Dark Factory Talk %d", n), Type: "source", Subtype: "video", Domain: "ai", Content: "## Summary\n\nA talk.\n\n## Entities Mentioned\n\n[[Jev]]\n\n## Concepts Introduced or Reinforced\n\n[[Dark Factory]]\n"}, {Title: "Jev", Type: "entity", Subtype: "tool", Content: "## Definition\n\nA decision model.\n\n## Sources\n"}, {Title: "Dark Factory", Type: "concept", Content: "## Definition\n\nA fully automated build line.\n\n## Sources\n"}, } } return Config{ BrainDir: brainDir, Resolver: wing.Router{BrainDir: brainDir, Default: "web-clip", ClientBlock: regexp.MustCompile(`(?i)acme-client`)}, Pipeline: pipeline.Config{ Schema: "# Schema", Complete: func(context.Context, string, string) (string, error) { return mustJSONPages(pagesFor(calls.Add(1))), nil }, }, }, brainDir, &calls } // topLevel lists the regular files directly in raw/: the inbox. func topLevel(t *testing.T, brainDir string) []string { t.Helper() entries, err := os.ReadDir(filepath.Join(brainDir, "raw")) require.NoError(t, err) var out []string for _, e := range entries { if e.Type().IsRegular() { out = append(out, e.Name()) } } return out } func TestAcceptance_ADroppedClipLandsInWebClipAndTheInboxIsEmpty(t *testing.T) { cfg, brainDir, calls := acceptanceCfg(t) put(t, brainDir, "dark-factory-talk.md", "Transcript of a talk about dark factories.") require.Empty(t, processDir(context.Background(), cfg, "2026-10-20")) assert.True(t, has(brainDir, "wiki/web-clip/sources/dark-factory-talk.md"), "the clip is a page in web-clip") assert.True(t, has(brainDir, "wiki/entities/jev.md"), "its entity is in the shared layer") assert.True(t, has(brainDir, "wiki/concepts/dark-factory.md"), "its concept is in the shared layer") assert.Empty(t, topLevel(t, brainDir), "raw/ holds nothing unprocessed") assert.False(t, has(brainDir, "raw/needs-review"), "and nothing waits for a human") assert.Equal(t, int32(1), calls.Load(), "one extraction, no classification step") } func TestAcceptance_TheSameEntityFromTwoClipsIsOnePage(t *testing.T) { cfg, brainDir, _ := acceptanceCfg(t) put(t, brainDir, "talk-one.md", "First talk.") require.Empty(t, processDir(context.Background(), cfg, "2026-10-20")) put(t, brainDir, "talk-two.md", "Second talk, different words.") require.Empty(t, processDir(context.Background(), cfg, "2026-10-20")) files, err := filepath.Glob(filepath.Join(brainDir, "wiki", "entities", "*.md")) require.NoError(t, err) assert.Len(t, files, 1) b, _ := os.ReadFile(filepath.Join(brainDir, "wiki/entities/jev.md")) assert.True(t, strings.Contains(string(b), "talk-one") && strings.Contains(string(b), "talk-two"), "both sources are cited") } func TestAcceptance_AClipThatLooksLikeClientMaterialIsParkedNotFiled(t *testing.T) { cfg, brainDir, calls := acceptanceCfg(t) put(t, brainDir, "kickoff.md", "Notes from the ACME-CLIENT kickoff call.") require.Empty(t, processDir(context.Background(), cfg, "2026-10-20")) assert.Zero(t, calls.Load(), "not even extracted") assert.False(t, has(brainDir, "wiki/web-clip"), "never filed into a shared wing") assert.True(t, has(brainDir, "raw/needs-review/kickoff.md"), "waits for the owner") assert.Empty(t, topLevel(t, brainDir)) } func TestAcceptance_ASubfolderNamingAnExistingWingPlacesThereAndATypoParks(t *testing.T) { cfg, brainDir, _ := acceptanceCfg(t) require.NoError(t, os.MkdirAll(filepath.Join(brainDir, "wiki", "homelab", "facts"), 0o755)) put(t, brainDir, "homelab/ops-note.md", "A note about the cluster.") put(t, brainDir, "hoemlab/typo-note.md", "A misfiled note.") require.Empty(t, processDir(context.Background(), cfg, "2026-10-20")) assert.True(t, has(brainDir, "wiki/homelab/sources/ops-note.md")) assert.False(t, has(brainDir, "wiki/hoemlab"), "a typo never creates a wing") assert.True(t, has(brainDir, "raw/needs-review/typo-note.md")) }