1package app
2
3import (
4 "context"
5 "errors"
6 "os"
7 "path/filepath"
8 "testing"
9 "time"
10
11 "github.com/stretchr/testify/require"
12
13 "github.com/bnema/vev/internal/domain"
14 "github.com/bnema/vev/internal/persist"
15)
16
17func TestRunOfflineNamedKillUsesOpenCatalogueSeam(t *testing.T) {
18 stateRoot := t.TempDir()
19 t.Setenv("XDG_STATE_HOME", stateRoot)
20 stateDir := filepath.Join(stateRoot, "vev")
21
22 p := newTestPersister(t, stateDir)
23 now := time.Now().UnixNano()
24 require.NoError(t, p.Save(persist.Record{Name: "named", IncarnationID: domain.IncarnationID{1}, Cwd: t.TempDir(), CreatedAt: now, UpdatedAt: now}))
25 require.NoError(t, p.Close())
26
27 original := openCatalogue
28 t.Cleanup(func() { openCatalogue = original })
29 wantErr := errors.New("open seam called")
30 called := false
31 openCatalogue = func(gotStateDir string) (persist.OpenResult, error) {
32 called = true
33 require.Equal(t, stateDir, gotStateDir)
34 return persist.OpenResult{}, wantErr
35 }
36
37 err := runOfflineNamedKill(context.Background(), "named")
38 require.ErrorIs(t, err, wantErr)
39 require.True(t, called)
40}
41
42func TestRunKillOfflineUsesIncarnationDeletionProtocol(t *testing.T) {
43 tests := []struct {
44 name string
45 crashHook func(*testing.T, string)
46 }{
47 {name: "normal"},
48 {
49 name: "stray tmp is ignored",
50 crashHook: func(t *testing.T, stateDir string) {
51 t.Helper()
52 storePath := persist.StorePath(stateDir)
53 require.NoError(t, os.WriteFile(storePath+".tmp", []byte("partial rewrite"), 0o600))
54 },
55 },
56 }
57
58 for _, tt := range tests {
59 t.Run(tt.name, func(t *testing.T) {
60 stateRoot, runtimeRoot := t.TempDir(), t.TempDir()
61 t.Setenv("XDG_STATE_HOME", stateRoot)
62 t.Setenv("XDG_RUNTIME_DIR", runtimeRoot)
63 stateDir := filepath.Join(stateRoot, "vev")
64
65 id := domain.IncarnationID{1}
66 p := newTestPersister(t, stateDir)
67 now := time.Now().UnixNano()
68 require.NoError(t, p.Save(persist.Record{Name: "named", IncarnationID: id, Cwd: t.TempDir(), CreatedAt: now, UpdatedAt: now}))
69 require.NoError(t, p.Close())
70 source := filepath.Join(stateDir, "snapshots", "sessions", id.String())
71 require.NoError(t, os.MkdirAll(source, 0o700))
72 require.NoError(t, os.WriteFile(filepath.Join(source, "sentinel"), []byte("durable"), 0o600))
73 if tt.crashHook != nil {
74 tt.crashHook(t, stateDir)
75 }
76
77 require.NoError(t, runKill(context.Background(), "named", false, false))
78 require.NoDirExists(t, source)
79 })
80 }
81}