-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathserve_test.go
More file actions
139 lines (121 loc) · 3.71 KB
/
Copy pathserve_test.go
File metadata and controls
139 lines (121 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package agent
import (
"os"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/arimxyer/pass-cli/internal/envmap"
)
// shortSocketDir returns a short base dir for a unix socket. Unix socket paths have
// a ~104-char limit on macOS/BSD, and t.TempDir() embeds the long test name.
func shortSocketDir(t *testing.T) string {
t.Helper()
dir, err := os.MkdirTemp("", "pc")
if err != nil {
t.Fatalf("MkdirTemp: %v", err)
}
t.Cleanup(func() { _ = os.RemoveAll(dir) })
return dir
}
// startTestAgent spins up a real socket-backed agent on a temp path and returns a
// stop func. PASS_CLI_AGENT_SOCK points SocketPath()/DialResolver at it.
func startTestAgent(t *testing.T) func() {
t.Helper()
if runtime.GOOS == "windows" {
t.Skip("unix-socket transport; Windows named pipe is Phase 2f")
}
sockPath := filepath.Join(shortSocketDir(t), "a.sock")
t.Setenv("PASS_CLI_AGENT_SOCK", sockPath)
ln, err := Listen(SocketPath())
if err != nil {
t.Fatalf("Listen: %v", err)
}
srv := NewServer(New(newTestVault(t), Options{}), ln, nil)
go srv.Serve()
// Wait for the socket to accept a connection.
deadline := time.Now().Add(2 * time.Second)
for {
if _, ok := DialResolver(); ok {
break
}
if time.Now().After(deadline) {
t.Fatal("agent socket did not come up")
}
time.Sleep(5 * time.Millisecond)
}
return srv.Stop
}
func TestSocketResolver_ResolvesOverSocket(t *testing.T) {
stop := startTestAgent(t)
defer stop()
r, ok := DialResolver()
if !ok {
t.Fatal("expected agent to be reachable")
}
defer func() { _ = r.Close() }()
values, err := r.ResolveValues([]envmap.Mapping{
{Service: "github"},
{Service: "github", Field: "username"},
}, "password")
if err != nil {
t.Fatalf("ResolveValues over socket: %v", err)
}
if len(values) != 2 || values[0] != testSecret || values[1] != "octocat" {
t.Errorf("values = %v, want [%s octocat]", values, testSecret)
}
}
func TestDialResolver_FallbackWhenAbsent(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("unix-socket transport")
}
// Point at a path with no listener.
t.Setenv("PASS_CLI_AGENT_SOCK", filepath.Join(shortSocketDir(t), "x.sock"))
if _, ok := DialResolver(); ok {
t.Error("expected DialResolver to report unreachable when no agent is listening")
}
}
func TestListen_RefusesSecondAgent(t *testing.T) {
stop := startTestAgent(t)
defer stop()
// A second Listen on the same live socket must fail ("already running").
if _, err := Listen(SocketPath()); err == nil {
t.Error("expected second Listen to fail while an agent is running")
}
}
func TestServer_ShutdownStops(t *testing.T) {
stop := startTestAgent(t)
defer stop()
r, ok := DialResolver()
if !ok {
t.Fatal("agent should be reachable")
}
// A resolve works before shutdown.
if _, err := r.ResolveValues([]envmap.Mapping{{Service: "github"}}, "password"); err != nil {
t.Fatalf("pre-shutdown resolve failed: %v", err)
}
}
// TestServer_StopStopsServerPromptly verifies that a `stop` (shutdown) request
// stops the server right away (freeing the socket) so clients fall back to
// direct-open and a fresh agent can rebind immediately.
func TestServer_StopStopsServerPromptly(t *testing.T) {
stop := startTestAgent(t)
defer stop()
if _, ok := DialResolver(); !ok {
t.Fatal("agent should be reachable before stop")
}
if err := Stop(); err != nil {
t.Fatalf("Stop: %v", err)
}
// The socket must become unreachable quickly (server stopped on the shutdown).
deadline := time.Now().Add(2 * time.Second)
for {
if _, ok := DialResolver(); !ok {
return // server stopped — clients will now fall back to direct-open
}
if time.Now().After(deadline) {
t.Fatal("agent still reachable after stop — server did not stop promptly")
}
time.Sleep(5 * time.Millisecond)
}
}