-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathconcurrent_unlock_test.go
More file actions
82 lines (70 loc) · 2.54 KB
/
Copy pathconcurrent_unlock_test.go
File metadata and controls
82 lines (70 loc) · 2.54 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
package cmd
import (
"io"
"os"
"path/filepath"
"testing"
"github.com/arimxyer/pass-cli/internal/vault"
)
// feedStdin redirects os.Stdin to a pipe carrying input (one line) for the
// duration of the test, and enables PASS_CLI_TEST so readPassword reads from it
// via the shared test scanner instead of a TTY. Only one password read per test
// process is safe (the scanner initializes once), so keep a single reader here.
func feedStdin(t *testing.T, input string) {
t.Helper()
t.Setenv("PASS_CLI_TEST", "1")
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("pipe: %v", err)
}
orig := os.Stdin
os.Stdin = r
t.Cleanup(func() { os.Stdin = orig; _ = r.Close() })
go func() {
_, _ = io.WriteString(w, input)
_ = w.Close()
}()
}
// unlockVaultWithSync's password branch overlaps the network pull (goroutine)
// with the master-password prompt (main thread), then joins before decrypting.
// This drives that branch end-to-end against a real, sync-enabled, initialized
// vault with no keychain — exercising the concurrency so `go test -race` proves
// the goroutine + join is data-race free and the vault still unlocks correctly.
func TestUnlockVaultWithSync_PasswordBranchConcurrent(t *testing.T) {
const masterPassword = "TestPass!1234"
tmpDir := t.TempDir()
vaultPath := filepath.Join(tmpDir, "vault.enc")
cfgPath := filepath.Join(tmpDir, "config.yml")
// Sync enabled with a bogus remote: SyncPull fails fast / no-ops (offline-
// friendly) and returns quickly, so the goroutine joins without real network.
cfg := "vault_path: " + vaultPath + "\nsync:\n enabled: true\n remote: \"mock-remote:bucket\"\n"
if err := os.WriteFile(cfgPath, []byte(cfg), 0600); err != nil {
t.Fatalf("write config: %v", err)
}
t.Setenv("PASS_CLI_CONFIG", cfgPath)
// Initialize the vault (no keychain) with a first service.
initVS, err := vault.New(vaultPath)
if err != nil {
t.Fatalf("vault.New (init): %v", err)
}
if err := initVS.Initialize([]byte(masterPassword), false, "", ""); err != nil {
t.Fatalf("Initialize: %v", err)
}
// A fresh, locked service is what a command would unlock.
vs, err := vault.New(vaultPath)
if err != nil {
t.Fatalf("vault.New (locked): %v", err)
}
if !vs.IsSyncEnabled() {
t.Fatal("expected sync to be enabled for the concurrent branch")
}
setOffline(t, false)
setVerbose(t, false)
feedStdin(t, masterPassword+"\n")
if err := unlockVaultWithSync(vs); err != nil {
t.Fatalf("unlockVaultWithSync: %v", err)
}
if !vs.IsUnlocked() {
t.Error("expected vault to be unlocked after the concurrent password branch")
}
}