broker_production_environment_test.go RawHistoryFolder
1package app
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "strings"
8 "syscall"
9 "testing"
10 "time"
11
12 "github.com/bnema/vev/internal/adapters/ipc"
13 "github.com/bnema/vev/internal/platform"
14 "github.com/stretchr/testify/require"
15)
16
17// Exercise both real detached re-execs, which change cwd to the user's home.
18// A long development root must preserve its configuration/state identity while
19// all roles independently resolve the same portable, isolated runtime path.
20func TestProductionBrokerDevelopmentEnvironmentReexec(t *testing.T) {
21 for _, explicit := range []bool{false, true} {
22 name := "implicit"
23 if explicit {
24 name = "explicit"
25 }
26 t.Run(name, func(t *testing.T) {
27 t.Chdir(t.TempDir())
28 t.Setenv("VEV_ENV", "test")
29 t.Setenv("VEV_ENV_ROOT", "")
30 if explicit {
31 t.Setenv("VEV_ENV_ROOT", filepath.Join(t.TempDir(), strings.Repeat("r", 100)))
32 }
33 for _, key := range []string{"XDG_CONFIG_HOME", "XDG_STATE_HOME", "XDG_RUNTIME_DIR"} {
34 t.Setenv(key, "")
35 }
36 require.NoError(t, platform.ActivateDevelopmentEnvironment())
37 root := os.Getenv("VEV_ENV_ROOT")
38 require.True(t, filepath.IsAbs(root))
39 records := shortTempDir(t, "vevenv")
40 t.Setenv(brokerHelperEnv, "1")
41 t.Setenv(brokerHelperRecordDirEnv, records)
42 sandbox := &brokerSubprocessSandbox{t: t, recordDir: records}
43 runtime := ipc.SocketDir()
44 require.NotEqual(t, filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "vev"), runtime)
45 t.Cleanup(func() {
46 for _, role := range []string{productionBrokerServeCommand, productionBrokerLauncherCommand} {
47 for _, pid := range sandbox.recorded(role) {
48 _ = syscall.Kill(pid, syscall.SIGKILL)
49 _ = waitForProcessExit(pid, 5*time.Second)
50 }
51 }
52 _ = os.RemoveAll(runtime)
53 })
54 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
55 defer cancel()
56 service, err := connectProductionBroker(ctx)
57 require.NoError(t, err)
58 defer service.Close()
59 require.NotZero(t, service.Snapshot().Epoch)
60 require.Len(t, sandbox.recorded(productionBrokerLauncherCommand), 1)
61 require.Len(t, sandbox.recorded(productionBrokerServeCommand), 1)
62 require.FileExists(t, filepath.Join(root, "test", "config", "vev", "broker.json"))
63 require.FileExists(t, filepath.Join(root, "test", "state", "vev", "broker", "state", "state.json"))
64 })
65 }
66}
67
68func TestProductionBrokerRejectsInsecureShortRuntimeParent(t *testing.T) {
69 for _, kind := range []string{"symlink", "public-directory"} {
70 t.Run(kind, func(t *testing.T) {
71 t.Setenv("XDG_CONFIG_HOME", t.TempDir())
72 t.Setenv("XDG_RUNTIME_DIR", filepath.Join(t.TempDir(), strings.Repeat("x", 110)))
73 runtime := ipc.SocketDir()
74 t.Cleanup(func() { _ = os.RemoveAll(runtime) })
75 if kind == "symlink" {
76 require.NoError(t, os.Symlink(t.TempDir(), runtime))
77 } else {
78 require.NoError(t, os.Mkdir(runtime, 0o700))
79 require.NoError(t, os.Chmod(runtime, 0o755))
80 }
81 service, err := connectProductionBroker(context.Background())
82 require.Nil(t, service)
83 require.ErrorContains(t, err, "secure broker runtime parent")
84 _, err = os.Lstat(filepath.Join(runtime, "broker"))
85 require.ErrorIs(t, err, os.ErrNotExist)
86 })
87 }
88}