fluid-telegram/internal/secrets/secrets_test.go

117 lines
3.3 KiB
Go
Raw Normal View History

package secrets
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func testStore(t *testing.T, h http.Handler) *Store {
t.Helper()
srv := httptest.NewServer(h)
t.Cleanup(srv.Close)
t.Setenv("BAO_ADDR", srv.URL)
t.Setenv("BAO_TOKEN", "test-token")
s, err := NewFromEnv("hall-of-helix")
if err != nil {
t.Fatal(err)
}
return s
}
// The salt rule. Rotating it silently invalidates every longitudinal comparison
// the interface has made, with no visible failure -- so the tool must have no
// path that replaces an existing one.
func TestCreateIfAbsentNeverOverwrites(t *testing.T) {
var writes int
existing := map[string]string{"salt": "the-original"}
s := testStore(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
writes++
w.WriteHeader(http.StatusOK)
return
}
json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{"data": existing}})
}))
created, err := s.CreateIfAbsent(context.Background(), KeyRedactionSalt,
map[string]string{"salt": "a-replacement"})
if err != nil {
t.Fatal(err)
}
if created {
t.Error("reported creating a salt that already existed")
}
if writes != 0 {
t.Errorf("wrote over an existing salt (%d writes)", writes)
}
}
func TestCreateIfAbsentWritesWhenMissing(t *testing.T) {
var got map[string]any
s := testStore(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
json.NewDecoder(r.Body).Decode(&got)
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusNotFound)
}))
created, err := s.CreateIfAbsent(context.Background(), KeyRedactionSalt,
map[string]string{"salt": "fresh"})
if err != nil {
t.Fatal(err)
}
if !created {
t.Fatal("did not create a salt when none existed")
}
if got["data"].(map[string]any)["salt"] != "fresh" {
t.Errorf("wrote %v", got)
}
}
// A missing path is an ordinary first-run state, not an error.
func TestGetMissingIsNotAnError(t *testing.T) {
s := testStore(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
_, found, err := s.Get(context.Background(), KeyBotToken)
if err != nil || found {
t.Fatalf("found=%v err=%v", found, err)
}
}
// Ref is printed in plans and errors, so it must name a location and never
// carry a value.
func TestRefIsSafeToPrint(t *testing.T) {
s := testStore(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"data": map[string]any{"data": map[string]string{"token": "123:SECRET"}}})
}))
ref := s.Ref(KeyBotToken)
if strings.Contains(ref, "SECRET") || strings.Contains(ref, "test-token") {
t.Fatalf("Ref leaked a secret: %q", ref)
}
if !strings.Contains(ref, "hall-of-helix") || !strings.Contains(ref, KeyBotToken) {
t.Errorf("Ref should locate the secret: %q", ref)
}
}
// Errors name the path that failed, never what was at it.
func TestErrorsDoNotCarryValues(t *testing.T) {
s := testStore(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
_, _, err := s.Get(context.Background(), KeyOperatorSession)
if err == nil {
t.Fatal("expected an error")
}
if strings.Contains(err.Error(), "test-token") {
t.Fatalf("error leaked the bao token: %v", err)
}
}