internal/cli/admin_test.go (view raw)
1package cli
2
3import (
4 "bytes"
5 "crypto/rand"
6 "log"
7 "os"
8 "path"
9 "strings"
10 "testing"
11 "time"
12
13 "postern/internal/db"
14 "postern/internal/persistence"
15
16 "github.com/spf13/cobra"
17)
18
19// executeCommand helps invoke a cobra command with specific arguments and captures its stdout/stderr.
20func executeCommand(cmd *cobra.Command, args ...string) (string, error) {
21 buf := new(bytes.Buffer)
22 cmd.SetOut(buf)
23 cmd.SetErr(buf)
24 cmd.SetArgs(args)
25
26 err := cmd.Execute()
27 return buf.String(), err
28}
29
30// TestAddUserCommand checks the loop of creating a new user, finding it in the db and checking that the password works.
31func TestAddRemoveUserCommand(t *testing.T) {
32 f := t.TempDir()
33
34 // create temporary masterkey
35 mkeyPath := path.Join(f, "masterkey")
36 secretKey := make([]byte, 32)
37 _, _ = rand.Read(secretKey)
38 err := os.WriteFile(mkeyPath, secretKey, 0666)
39 if err != nil {
40 t.Fatal(err)
41 }
42
43 p, err := persistence.New(persistence.Config{
44 BlobRoot: path.Join(f, "blobs"),
45 MasterkeyFile: mkeyPath,
46 })
47 if err != nil {
48 log.Fatal(err)
49 }
50
51 posternDB, err := db.OpenDB(&db.Config{
52 DBPath: path.Join(f, "meta.db"),
53 })
54 if err != nil {
55 log.Fatal(err)
56 }
57
58 c, err := New(&Config{
59 DB: posternDB,
60 Persistence: p,
61 })
62 if err != nil {
63 t.Fatal(err)
64 }
65
66 // Start HTTP service on a temp socket for the test
67 tempSocket := path.Join(f, "test.sock")
68 c.socketPath = tempSocket
69 c.httpClient = newUnixHTTPClient(tempSocket)
70
71 // Start the HTTP service in background
72 service := NewHTTPService(tempSocket, c)
73 go func() {
74 _ = service.Start()
75 }()
76
77 timeout := time.After(10 * time.Second)
78 ticker := time.NewTicker(100 * time.Millisecond)
79 defer ticker.Stop()
80
81 for service.Status() != "running" {
82 select {
83 case <-timeout:
84 t.Fatal("timed out waiting for http service to start")
85 case <-ticker.C:
86 // Wait for next tick
87 }
88 }
89
90 // Run the cobra command
91 output, err := executeCommand(c.rootCmd(), "user", "add", "user1")
92 if err != nil {
93 t.Fatalf("expected no error, got: %v", err)
94 }
95
96 _, after, found := strings.Cut(output, "Password: ")
97 password := strings.TrimSpace(after)
98 if !found {
99 t.Fatal("expected to find a password in output. Found none")
100 }
101
102 // Test that the user was created by directly accessing the DB
103 u, err := c.db.GetUser(t.Context(), "user1")
104 if err != nil {
105 t.Fatalf("expected no error, got: %v", err)
106 }
107
108 err = u.VerifyPassword([]byte(password))
109 if err != nil {
110 t.Fatalf("password mismatch, got: %v", err)
111 }
112
113 // Try removing user
114 output, err = executeCommand(c.rootCmd(), "user", "remove", "user1")
115 if err != nil {
116 t.Fatalf("expected no error, got: %v", err)
117 }
118 if !strings.Contains(output, "removed") {
119 t.Fatal("expected to find 'removed' in output. Found none")
120 }
121
122 // Try removing user again (failure)
123 _, err = executeCommand(c.rootCmd(), "user", "remove", "user1")
124 if err == nil {
125 t.Errorf("expected error when deleting non-existing user, got none")
126 }
127
128 // Clean up
129 _ = service.Stop()
130}