Files
mcias/internal/server/vault_test.go
Kyle Isom 41d01edfb4 Migrate module path from kyle/ to mc/ org
All import paths updated from git.wntrmute.dev/kyle/mcias to
git.wntrmute.dev/mc/mcias to match the Gitea organization.
Includes main module and clients/go submodule.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:03:46 -07:00

172 lines
4.4 KiB
Go

package server
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.wntrmute.dev/mc/mcias/internal/vault"
)
func TestHandleHealthSealed(t *testing.T) {
srv, _, _, _ := newTestServer(t)
srv.vault.Seal()
req := httptest.NewRequest(http.MethodGet, "/v1/health", nil)
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("health status = %d, want 200", rr.Code)
}
var resp map[string]string
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode health: %v", err)
}
if resp["status"] != "sealed" {
t.Fatalf("health status = %q, want sealed", resp["status"])
}
}
func TestHandleHealthUnsealed(t *testing.T) {
srv, _, _, _ := newTestServer(t)
req := httptest.NewRequest(http.MethodGet, "/v1/health", nil)
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("health status = %d, want 200", rr.Code)
}
var resp map[string]string
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode health: %v", err)
}
if resp["status"] != "ok" {
t.Fatalf("health status = %q, want ok", resp["status"])
}
}
func TestVaultStatusEndpoint(t *testing.T) {
srv, _, _, _ := newTestServer(t)
// Unsealed
req := httptest.NewRequest(http.MethodGet, "/v1/vault/status", nil)
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status code = %d, want 200", rr.Code)
}
var resp map[string]bool
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode: %v", err)
}
if resp["sealed"] {
t.Fatal("vault should be unsealed")
}
// Seal and check again
srv.vault.Seal()
req = httptest.NewRequest(http.MethodGet, "/v1/vault/status", nil)
rr = httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status code = %d, want 200", rr.Code)
}
resp = nil
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode: %v", err)
}
if !resp["sealed"] {
t.Fatal("vault should be sealed")
}
}
func TestSealedMiddlewareAPIReturns503(t *testing.T) {
srv, _, _, _ := newTestServer(t)
srv.vault.Seal()
req := httptest.NewRequest(http.MethodGet, "/v1/accounts", nil)
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusServiceUnavailable {
t.Fatalf("sealed API status = %d, want 503", rr.Code)
}
var resp map[string]string
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode: %v", err)
}
if resp["code"] != "vault_sealed" {
t.Fatalf("error code = %q, want vault_sealed", resp["code"])
}
}
func TestSealedMiddlewareUIRedirects(t *testing.T) {
srv, _, _, _ := newTestServer(t)
srv.vault.Seal()
req := httptest.NewRequest(http.MethodGet, "/dashboard", nil)
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusFound {
t.Fatalf("sealed UI status = %d, want 302", rr.Code)
}
loc := rr.Header().Get("Location")
if loc != "/unseal" {
t.Fatalf("redirect location = %q, want /unseal", loc)
}
}
func TestUnsealBadPassphrase(t *testing.T) {
srv, _, _, _ := newTestServer(t)
// Start sealed.
v := vault.NewSealed()
srv.vault = v
body := `{"passphrase":"wrong-passphrase"}`
req := httptest.NewRequest(http.MethodPost, "/v1/vault/unseal", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusUnauthorized {
t.Fatalf("unseal with bad passphrase status = %d, want 401", rr.Code)
}
}
func TestSealAlreadySealedNoop(t *testing.T) {
srv, _, priv, _ := newTestServer(t)
// Seal via API (needs admin token)
adminToken, _ := issueAdminToken(t, srv, priv, "admin")
req := httptest.NewRequest(http.MethodPost, "/v1/vault/seal", nil)
req.Header.Set("Authorization", "Bearer "+adminToken)
rr := httptest.NewRecorder()
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("seal status = %d, want 200", rr.Code)
}
var resp map[string]string
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
t.Fatalf("decode: %v", err)
}
if resp["status"] != "sealed" {
t.Fatalf("seal response status = %q, want sealed", resp["status"])
}
// Vault should be sealed now
if !srv.vault.IsSealed() {
t.Fatal("vault should be sealed after seal API call")
}
}