Files
patterm/internal/app/project_registry_test.go

163 lines
4.4 KiB
Go

package app
import (
"context"
"syscall"
"testing"
"github.com/hjbdev/patterm/internal/preset"
)
func TestSwitchProjectPreservesProjectProcessTrees(t *testing.T) {
t.Setenv("XDG_DATA_HOME", t.TempDir())
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
reg := newProjectRegistry(preset.Set{}, defaultSettings(), nil, 80, 24)
defer reg.Shutdown()
projectA, err := reg.Open(ctx, t.TempDir())
if err != nil {
t.Fatalf("open project A: %v", err)
}
projectB, err := reg.Open(ctx, t.TempDir())
if err != nil {
t.Fatalf("open project B: %v", err)
}
a, err := projectA.Session.Spawn(SpawnSpec{
Kind: KindCommand,
Argv: []string{"sh", "-c", "trap 'exit 0' TERM; while :; do sleep 1; done"},
Name: "a-loop",
}, 80, 24)
if err != nil {
t.Fatalf("spawn project A command: %v", err)
}
b, err := projectB.Session.Spawn(SpawnSpec{
Kind: KindCommand,
Argv: []string{"sh", "-c", "trap 'exit 0' TERM; while :; do sleep 1; done"},
Name: "b-loop",
}, 80, 24)
if err != nil {
t.Fatalf("spawn project B command: %v", err)
}
t.Cleanup(func() {
_ = projectA.Session.Kill(a.ID, syscall.SIGTERM)
_ = projectB.Session.Kill(b.ID, syscall.SIGTERM)
})
waitUntilLive(t, a)
waitUntilLive(t, b)
st := &uiState{
registry: reg,
project: projectA,
sess: projectA.Session,
launcher: projectA.Launcher,
pads: projectA.Pads,
trust: projectA.Trust,
timers: projectA.Host.timers,
chromeWake: make(chan struct{}, 1),
view: ClientView{
ID: "test",
ProjectKey: projectA.Key,
ProjectName: projectA.Name,
Cols: 80,
Rows: 24,
},
}
st.focusChildLocked(a)
projectA.Session.Subscribe(st)
st.switchProject(projectB)
if st.view.ProjectKey != projectB.Key {
t.Fatalf("view project key = %q, want %q", st.view.ProjectKey, projectB.Key)
}
if st.sess != projectB.Session {
t.Fatalf("ui session did not move to project B")
}
if projectA.Session.FindChild(a.ID) == nil {
t.Fatalf("project A child disappeared after switch")
}
if projectB.Session.FindChild(b.ID) == nil {
t.Fatalf("project B child disappeared after switch")
}
if !a.IsLive() {
t.Fatalf("project A child stopped after switch")
}
if !b.IsLive() {
t.Fatalf("project B child stopped after switch")
}
st.switchProject(projectA)
if st.view.ProjectKey != projectA.Key {
t.Fatalf("view project key after switching back = %q, want %q", st.view.ProjectKey, projectA.Key)
}
if projectA.Session.FindChild(a.ID) == nil || projectB.Session.FindChild(b.ID) == nil {
t.Fatalf("switching back should preserve both project process trees")
}
}
func TestProjectRegistryScratchpadsRouteByCallerProject(t *testing.T) {
t.Setenv("XDG_DATA_HOME", t.TempDir())
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
reg := newProjectRegistry(preset.Set{}, defaultSettings(), nil, 80, 24)
defer reg.Shutdown()
projectA, err := reg.Open(ctx, t.TempDir())
if err != nil {
t.Fatalf("open project A: %v", err)
}
projectB, err := reg.Open(ctx, t.TempDir())
if err != nil {
t.Fatalf("open project B: %v", err)
}
a, err := projectA.Session.Spawn(SpawnSpec{
Kind: KindCommand,
Argv: []string{"sh", "-c", "trap 'exit 0' TERM; while :; do sleep 1; done"},
Name: "a-caller",
}, 80, 24)
if err != nil {
t.Fatalf("spawn project A caller: %v", err)
}
b, err := projectB.Session.Spawn(SpawnSpec{
Kind: KindCommand,
Argv: []string{"sh", "-c", "trap 'exit 0' TERM; while :; do sleep 1; done"},
Name: "b-caller",
}, 80, 24)
if err != nil {
t.Fatalf("spawn project B caller: %v", err)
}
t.Cleanup(func() {
_ = projectA.Session.Kill(a.ID, syscall.SIGTERM)
_ = projectB.Session.Kill(b.ID, syscall.SIGTERM)
})
waitUntilLive(t, a)
waitUntilLive(t, b)
if _, err := reg.ScratchpadWrite(a.ID, "note.md", "project A", ""); err != nil {
t.Fatalf("write project A scratchpad: %v", err)
}
if _, err := reg.ScratchpadWrite(b.ID, "note.md", "project B", ""); err != nil {
t.Fatalf("write project B scratchpad: %v", err)
}
gotA, _, err := reg.ScratchpadRead(a.ID, "note.md")
if err != nil {
t.Fatalf("read project A scratchpad: %v", err)
}
gotB, _, err := reg.ScratchpadRead(b.ID, "note.md")
if err != nil {
t.Fatalf("read project B scratchpad: %v", err)
}
if gotA != "project A" || gotB != "project B" {
t.Fatalf("scratchpad routing leaked between projects: A=%q B=%q", gotA, gotB)
}
}