feat: wire sse_client_cpp_core for live updates from /api/boards/issues/stream
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
func TestWatcher_PathToEvent_IssuesCreate(t *testing.T) {
|
||||
ev := fsnotify.Event{
|
||||
Name: "/home/x/fn_registry/dev/issues/0119-frontmatter-migration.md",
|
||||
Op: fsnotify.Create,
|
||||
}
|
||||
board, id, action := classifyEvent(ev)
|
||||
if board != "issues" || id != "0119" || action != "created" {
|
||||
t.Fatalf("got (%q,%q,%q), want (issues,0119,created)", board, id, action)
|
||||
}
|
||||
if sseEventForAction(action) != "card_added" {
|
||||
t.Fatalf("sseEventForAction(created) != card_added")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcher_PathToEvent_FlowsRename(t *testing.T) {
|
||||
ev := fsnotify.Event{
|
||||
Name: "/x/dev/flows/0042-deploy-vps.md",
|
||||
Op: fsnotify.Rename,
|
||||
}
|
||||
board, id, action := classifyEvent(ev)
|
||||
if board != "flows" || id != "0042" || action != "updated" {
|
||||
t.Fatalf("got (%q,%q,%q), want (flows,0042,updated)", board, id, action)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcher_PathToEvent_Remove(t *testing.T) {
|
||||
ev := fsnotify.Event{
|
||||
Name: "/x/dev/issues/0050-foo.md",
|
||||
Op: fsnotify.Remove,
|
||||
}
|
||||
board, id, action := classifyEvent(ev)
|
||||
if board != "issues" || id != "0050" || action != "deleted" {
|
||||
t.Fatalf("got (%q,%q,%q), want (issues,0050,deleted)", board, id, action)
|
||||
}
|
||||
if sseEventForAction(action) != "card_removed" {
|
||||
t.Fatalf("sseEventForAction(deleted) != card_removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcher_PathToEvent_SkippedNames(t *testing.T) {
|
||||
cases := []string{
|
||||
"/x/dev/issues/README.md",
|
||||
"/x/dev/issues/INDEX.md",
|
||||
"/x/dev/issues/AGENT_GUIDE.md",
|
||||
"/x/dev/issues/notes.txt", // not .md
|
||||
"/x/somewhere-else/0001-foo.md", // not under dev/issues|flows
|
||||
}
|
||||
for _, p := range cases {
|
||||
ev := fsnotify.Event{Name: p, Op: fsnotify.Create}
|
||||
if board, _, _ := classifyEvent(ev); board != "" {
|
||||
t.Fatalf("expected ignored, got board=%q for %s", board, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcher_DetectsWrite(t *testing.T) {
|
||||
// Build a temp tree that *looks like* dev/issues/ so classifyEvent
|
||||
// will accept it (it matches by path substring "/dev/issues/").
|
||||
root := t.TempDir()
|
||||
issuesDirPath := filepath.Join(root, "dev", "issues")
|
||||
if err := os.MkdirAll(issuesDirPath, 0o755); err != nil {
|
||||
t.Fatalf("mkdir: %v", err)
|
||||
}
|
||||
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("new watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
if err := w.Add(issuesDirPath); err != nil {
|
||||
t.Fatalf("watch add: %v", err)
|
||||
}
|
||||
|
||||
hub := NewHub()
|
||||
ch := hub.Subscribe()
|
||||
defer hub.Unsubscribe(ch)
|
||||
|
||||
// Drive events in a goroutine via handleFsEvent so we exercise the
|
||||
// full pipeline (classify -> broadcast).
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
for {
|
||||
select {
|
||||
case ev, ok := <-w.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
handleFsEvent(hub, ev)
|
||||
case <-w.Errors:
|
||||
case <-time.After(2 * time.Second):
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
cardPath := filepath.Join(issuesDirPath, "0999-test.md")
|
||||
if err := os.WriteFile(cardPath, []byte("---\nstatus: pendiente\n---\n"), 0o644); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case ev := <-ch:
|
||||
if ev.Board != "issues" || ev.CardID != "0999" {
|
||||
t.Fatalf("unexpected event: %+v", ev)
|
||||
}
|
||||
if ev.Action != "created" && ev.Action != "updated" {
|
||||
t.Fatalf("expected created/updated, got action=%q", ev.Action)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("timeout waiting for write event")
|
||||
}
|
||||
|
||||
<-done
|
||||
}
|
||||
Reference in New Issue
Block a user