package handler import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "vctp/internal/secrets" ) func newEncryptTestHandler() (*Handler, *secrets.Secrets) { logger := newTestLogger() key := []byte("0123456789abcdef0123456789abcdef") secret := secrets.New(logger, key) return &Handler{ Logger: logger, Secret: secret, }, secret } func decodeResponse(t *testing.T, rr *httptest.ResponseRecorder) map[string]string { t.Helper() var resp map[string]string if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil { t.Fatalf("failed to decode response body %q: %v", rr.Body.String(), err) } return resp } func TestEncryptDataRejectsWrongMethod(t *testing.T) { h, _ := newEncryptTestHandler() req := httptest.NewRequest(http.MethodGet, "/api/encrypt", nil) rr := httptest.NewRecorder() h.EncryptData(rr, req) if rr.Code != http.StatusMethodNotAllowed { t.Fatalf("expected %d, got %d", http.StatusMethodNotAllowed, rr.Code) } resp := decodeResponse(t, rr) if resp["status"] != "ERROR" { t.Fatalf("expected status ERROR, got %#v", resp) } } func TestEncryptDataRejectsInvalidJSON(t *testing.T) { h, _ := newEncryptTestHandler() req := httptest.NewRequest(http.MethodPost, "/api/encrypt", strings.NewReader("{")) rr := httptest.NewRecorder() h.EncryptData(rr, req) if rr.Code != http.StatusBadRequest { t.Fatalf("expected %d, got %d", http.StatusBadRequest, rr.Code) } resp := decodeResponse(t, rr) if resp["status"] != "ERROR" { t.Fatalf("expected status ERROR, got %#v", resp) } } func TestEncryptDataAcceptsPlaintextField(t *testing.T) { h, secret := newEncryptTestHandler() req := httptest.NewRequest(http.MethodPost, "/api/encrypt", strings.NewReader(`{"plaintext":"super-secret"}`)) rr := httptest.NewRecorder() h.EncryptData(rr, req) if rr.Code != http.StatusOK { t.Fatalf("expected %d, got %d", http.StatusOK, rr.Code) } resp := decodeResponse(t, rr) if resp["status"] != "OK" { t.Fatalf("expected status OK, got %#v", resp) } if resp["ciphertext"] == "" || resp["prefixed"] == "" { t.Fatalf("expected ciphertext+prefixed fields, got %#v", resp) } if !strings.HasPrefix(resp["prefixed"], encryptedValuePrefixV1) { t.Fatalf("expected prefixed value with %q, got %q", encryptedValuePrefixV1, resp["prefixed"]) } if !strings.EqualFold(resp["message"], resp["ciphertext"]) { t.Fatalf("expected message to mirror ciphertext, got %#v", resp) } plain, err := secret.Decrypt(resp["ciphertext"]) if err != nil { t.Fatalf("unable to decrypt ciphertext response: %v", err) } if string(plain) != "super-secret" { t.Fatalf("unexpected decrypted value %q", string(plain)) } } func TestEncryptDataAcceptsLegacyValueField(t *testing.T) { h, secret := newEncryptTestHandler() body := bytes.NewBufferString(`{"value":"legacy-input"}`) req := httptest.NewRequest(http.MethodPost, "/api/encrypt", body) rr := httptest.NewRecorder() h.EncryptData(rr, req) if rr.Code != http.StatusOK { t.Fatalf("expected %d, got %d", http.StatusOK, rr.Code) } resp := decodeResponse(t, rr) cipherText := resp["ciphertext"] if cipherText == "" { t.Fatalf("expected ciphertext in response, got %#v", resp) } plain, err := secret.Decrypt(cipherText) if err != nil { t.Fatalf("unable to decrypt ciphertext response: %v", err) } if string(plain) != "legacy-input" { t.Fatalf("unexpected decrypted value %q", string(plain)) } } func TestEncryptDataRejectsMissingPayloadValue(t *testing.T) { h, _ := newEncryptTestHandler() req := httptest.NewRequest(http.MethodPost, "/api/encrypt", strings.NewReader(`{}`)) rr := httptest.NewRecorder() h.EncryptData(rr, req) if rr.Code != http.StatusBadRequest { t.Fatalf("expected %d, got %d", http.StatusBadRequest, rr.Code) } resp := decodeResponse(t, rr) if resp["status"] != "ERROR" { t.Fatalf("expected status ERROR, got %#v", resp) } }