1package subagents
2
3import (
4 "bufio"
5 "context"
6 "errors"
7 "fmt"
8 "os"
9 "os/exec"
10 "testing"
11 "time"
12)
13
14func TestResidentJournalLeaseExcludesOtherProcess(t *testing.T) {
15 if os.Getenv("ZUT_RESIDENT_LEASE_HELPER") == "1" {
16 journal, err := OpenResidentJournal(os.Getenv("ZUT_RESIDENT_LEASE_ROOT"), "child")
17 if err != nil {
18 t.Fatal(err)
19 }
20 fmt.Fprintln(os.Stdout, "ready")
21 if _, err := bufio.NewReader(os.Stdin).ReadString('\n'); err != nil {
22 t.Fatal(err)
23 }
24 if err := journal.Close(); err != nil {
25 t.Fatal(err)
26 }
27 return
28 }
29
30 root := t.TempDir()
31 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
32 defer cancel()
33 cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestResidentJournalLeaseExcludesOtherProcess$")
34 defer func() {
35 if cmd.Process != nil {
36 _ = cmd.Process.Kill()
37 }
38 _ = cmd.Wait()
39 }()
40 cmd.Env = append(os.Environ(), "ZUT_RESIDENT_LEASE_HELPER=1", "ZUT_RESIDENT_LEASE_ROOT="+root)
41 stdout, err := cmd.StdoutPipe()
42 if err != nil {
43 t.Fatal(err)
44 }
45 stdin, err := cmd.StdinPipe()
46 if err != nil {
47 t.Fatal(err)
48 }
49 if err := cmd.Start(); err != nil {
50 t.Fatal(err)
51 }
52 ready, err := bufio.NewReader(stdout).ReadString('\n')
53 if err != nil {
54 t.Fatal(err)
55 }
56 if ready != "ready\n" {
57 t.Fatalf("helper readiness = %q", ready)
58 }
59 if _, err := OpenResidentJournal(root, "child"); !errors.Is(err, ErrResidentLeaseBusy) {
60 t.Fatalf("OpenResidentJournal while other process owns lease = %v, want ErrResidentLeaseBusy", err)
61 }
62 if _, err := fmt.Fprintln(stdin); err != nil {
63 t.Fatal(err)
64 }
65 if err := stdin.Close(); err != nil {
66 t.Fatal(err)
67 }
68 if err := cmd.Wait(); err != nil {
69 t.Fatal(err)
70 }
71 journal, err := OpenResidentJournal(root, "child")
72 if err != nil {
73 t.Fatalf("OpenResidentJournal after owner exit: %v", err)
74 }
75 if err := journal.Close(); err != nil {
76 t.Fatal(err)
77 }
78}
79
80func TestResidentJournalLeaseExcludesSecondOwner(t *testing.T) {
81 root := t.TempDir()
82 first, err := OpenResidentJournal(root, "child")
83 if err != nil {
84 t.Fatal(err)
85 }
86 defer first.Close()
87 if _, err := OpenResidentJournal(root, "child"); !errors.Is(err, ErrResidentLeaseBusy) {
88 t.Fatalf("second OpenResidentJournal error = %v, want ErrResidentLeaseBusy", err)
89 }
90 if err := first.Close(); err != nil {
91 t.Fatal(err)
92 }
93 second, err := OpenResidentJournal(root, "child")
94 if err != nil {
95 t.Fatalf("OpenResidentJournal after Close: %v", err)
96 }
97 if err := second.Close(); err != nil {
98 t.Fatal(err)
99 }
100}