package main // `lchat install` (versions.go): staged builds land on sibling-service binary // paths with a .prev- backup, and the guardrails hold (unknown sibling, // unstaged version). import ( "os" "path/filepath" "strings" "testing" ) func TestInstallSibling(t *testing.T) { old := optDir optDir = t.TempDir() defer func() { optDir = old }() // Stage a fake build the way stage would lay it out. ver := "abc1234" if err := os.MkdirAll(filepath.Dir(versionBinPath(ver)), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(versionBinPath(ver), []byte("new-build"), 0o755); err != nil { t.Fatal(err) } // An existing home binary to be replaced. target := filepath.Join(optDir, "home") if err := os.WriteFile(target, []byte("old-build"), 0o755); err != nil { t.Fatal(err) } msg, err := installSibling("home", ver) if err != nil { t.Fatal(err) } if got, _ := os.ReadFile(target); string(got) != "new-build" { t.Fatalf("target = %q, want the staged build", got) } // The previous binary survives as a dated .prev- sibling. prevs, _ := filepath.Glob(target + ".prev-*") if len(prevs) != 1 { t.Fatalf("prev backups = %v, want exactly one", prevs) } if got, _ := os.ReadFile(prevs[0]); string(got) != "old-build" { t.Fatalf("backup = %q, want the old build", got) } if !strings.Contains(msg, "lchat-svc restart lchat-home") { t.Fatalf("summary should name the restart: %q", msg) } // First install (no existing binary): no backup, no error. if _, err := installSibling("push", ver); err != nil { t.Fatal(err) } if prevs, _ := filepath.Glob(filepath.Join(optDir, "push.prev-*")); len(prevs) != 0 { t.Fatalf("fresh install made a backup: %v", prevs) } // Guardrails. if _, err := installSibling("router", ver); err == nil { t.Fatal("unknown sibling must refuse") } if _, err := installSibling("home", "def5678"); err == nil { t.Fatal("unstaged version must refuse") } }