all repos — postern

@ 50dc541db37adabbbb220f4abf62aba899c48f5d

Modern mail management

clone
git clone https:///postern.git

internal/cli/kv_test.go (view raw)

  1package cli
  2
  3import (
  4	"fmt"
  5	"path"
  6	"strings"
  7	"testing"
  8	"time"
  9
 10	"postern/internal/db"
 11)
 12
 13// newKVTestCommand sets up a DB-backed Command with a running HTTP service and
 14// returns it. The service is stopped when the test finishes.
 15func newKVTestCommand(t *testing.T) *Command {
 16	t.Helper()
 17	f := t.TempDir()
 18
 19	posternDB, err := db.OpenDB(&db.Config{
 20		DBPath: path.Join(f, "meta.db"),
 21	})
 22	if err != nil {
 23		t.Fatal(err)
 24	}
 25
 26	c, err := New(&Config{
 27		DB: posternDB,
 28	})
 29	if err != nil {
 30		t.Fatal(err)
 31	}
 32
 33	tempSocket := path.Join(f, "test.sock")
 34	c.socketPath = tempSocket
 35	c.httpClient = newUnixHTTPClient(tempSocket)
 36
 37	service := NewHTTPService(tempSocket, c)
 38	go func() {
 39		_ = service.Start()
 40	}()
 41
 42	timeout := time.After(10 * time.Second)
 43	ticker := time.NewTicker(100 * time.Millisecond)
 44	defer ticker.Stop()
 45	for service.Status() != "running" {
 46		select {
 47		case <-timeout:
 48			t.Fatal("timed out waiting for http service to start")
 49		case <-ticker.C:
 50		}
 51	}
 52	t.Cleanup(func() { _ = service.Stop() })
 53
 54	return c
 55}
 56
 57func TestKVListCommand(t *testing.T) {
 58	c := newKVTestCommand(t)
 59	ctx := t.Context()
 60
 61	seed := map[string]any{
 62		"counter": 42,
 63		"message": "hello world",
 64		"a/b":     "slash value",
 65	}
 66	for key, value := range seed {
 67		if err := c.db.KVSet(ctx, key, value); err != nil {
 68			t.Fatalf("seeding %q: %v", key, err)
 69		}
 70	}
 71
 72	out, err := executeCommand(c.rootCmd(), "kv", "list")
 73	if err != nil {
 74		t.Fatalf("kv list: %v", err)
 75	}
 76
 77	for wantKey, wantValue := range seed {
 78		if !strings.Contains(out, wantKey) {
 79			t.Errorf("kv list output missing %q; got:\n%s", wantKey, out)
 80		}
 81		if !strings.Contains(out, fmt.Sprint(wantValue)) {
 82			t.Errorf("kv list output missing %q; got:\n%s", wantValue, out)
 83		}
 84	}
 85}
 86
 87func TestKVGetCommand(t *testing.T) {
 88	c := newKVTestCommand(t)
 89	ctx := t.Context()
 90
 91	if err := c.db.KVSet(ctx, "counter", 42); err != nil {
 92		t.Fatal(err)
 93	}
 94	if err := c.db.KVSet(ctx, "a/b", "slash value"); err != nil {
 95		t.Fatal(err)
 96	}
 97
 98	// Plain key.
 99	out, err := executeCommand(c.rootCmd(), "kv", "get", "counter")
100	if err != nil {
101		t.Fatalf("kv get counter: %v", err)
102	}
103	if got := strings.TrimSpace(out); got != "42" {
104		t.Errorf("kv get counter = %q, want %q", got, "42")
105	}
106
107	// Key containing a slash.
108	out, err = executeCommand(c.rootCmd(), "kv", "get", "a/b")
109	if err != nil {
110		t.Fatalf("kv get a/b: %v", err)
111	}
112	if got := strings.TrimSpace(out); got != "slash value" {
113		t.Errorf("kv get a/b = %q, want %q", got, "slash value")
114	}
115
116	// Missing key should error.
117	if _, err := executeCommand(c.rootCmd(), "kv", "get", "missing"); err == nil {
118		t.Error("kv get missing: expected error, got nil")
119	}
120}
121
122func TestKVPopCommand(t *testing.T) {
123	c := newKVTestCommand(t)
124	ctx := t.Context()
125
126	if err := c.db.KVSet(ctx, "counter", 42); err != nil {
127		t.Fatal(err)
128	}
129	if err := c.db.KVSet(ctx, "a/b", "slash value"); err != nil {
130		t.Fatal(err)
131	}
132
133	// Pop a plain key and confirm it returns the value and is deleted.
134	out, err := executeCommand(c.rootCmd(), "kv", "pop", "counter")
135	if err != nil {
136		t.Fatalf("kv pop counter: %v", err)
137	}
138	if got := strings.TrimSpace(out); got != "42" {
139		t.Errorf("kv pop counter = %q, want %q", got, "42")
140	}
141	if _, err := c.db.KVGet(ctx, "counter"); err == nil {
142		t.Error("counter still present after kv pop")
143	}
144
145	// Pop a key containing a slash.
146	out, err = executeCommand(c.rootCmd(), "kv", "pop", "a/b")
147	if err != nil {
148		t.Fatalf("kv pop a/b: %v", err)
149	}
150	if got := strings.TrimSpace(out); got != "slash value" {
151		t.Errorf("kv pop a/b = %q, want %q", got, "slash value")
152	}
153	if _, err := c.db.KVGet(ctx, "a/b"); err == nil {
154		t.Error("a/b still present after kv pop")
155	}
156
157	// Popping a missing key should error.
158	if _, err := executeCommand(c.rootCmd(), "kv", "pop", "missing"); err == nil {
159		t.Error("kv pop missing: expected error, got nil")
160	}
161}