commit 6678d6a01acd7c130d546a3a3ecc8250b7d316d9
parent ccc069e14056a9562725f1f68aefc35371ec46b0
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Thu, 24 Apr 2025 13:14:01 +0200
add tests for API
Diffstat:
3 files changed, 178 insertions(+), 15 deletions(-)
diff --git a/cmd/mailbox-server/main_test.go b/cmd/mailbox-server/main_test.go
@@ -0,0 +1,153 @@
+package main_test
+
+import (
+ "bytes"
+ "crypto/ed25519"
+ "crypto/rand"
+ "crypto/sha512"
+ "encoding/binary"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
+ "taler.net/taler-mailbox/internal/gana"
+ "taler.net/taler-mailbox/pkg/rest"
+)
+
+var a mailbox.Mailbox
+
+var testWalletAlicePriv ed25519.PrivateKey
+var testWalletAlice ed25519.PublicKey
+var testWalletAliceString string
+
+func executeRequest(req *http.Request) *httptest.ResponseRecorder {
+ rr := httptest.NewRecorder()
+ a.Router.ServeHTTP(rr, req)
+ return rr
+}
+
+func checkResponseCode(t *testing.T, expected, actual int) {
+ if expected != actual {
+ t.Errorf("Expected response code %d, Got %d\n", expected, actual)
+ }
+}
+
+func TestMain(m *testing.M) {
+ a.Initialize("test-mailbox.conf")
+ testWalletAlice, testWalletAlicePriv, _ = ed25519.GenerateKey(nil)
+ h := sha512.New()
+ h.Write(testWalletAlice)
+ testWalletAliceString = gnunetutil.EncodeBinaryToString(h.Sum(nil))
+ code := m.Run()
+ // Purge DB
+ a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{})
+ os.Exit(code)
+}
+
+func TestEmptyMailbox(t *testing.T) {
+ a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{})
+ req, _ := http.NewRequest("GET", "/" + testWalletAliceString, nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusNoContent, response.Code)
+
+ body := response.Body.String()
+ if body != "" {
+ t.Errorf("Expected empty response, Got %s", body)
+ }
+}
+
+func TestPostMessage(t *testing.T) {
+ testMessage := make([]byte, 256)
+ a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{})
+ req, _ := http.NewRequest("POST", "/" + testWalletAliceString, bytes.NewReader(testMessage))
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusNoContent, response.Code)
+
+ body := response.Body.String()
+ if body != "" {
+ t.Errorf("Expected empty response, Got %s", body)
+ }
+}
+
+func TestPostThenDeleteMessage(t *testing.T) {
+ // testMessage := make([]byte, 256)
+ var deletionReq mailbox.MessageDeletionRequest
+ testMessages := make([]byte, 25600)
+ _, _ = rand.Read(testMessages)
+ a.Db.Where("1 = 1").Delete(&mailbox.InboxEntry{})
+
+ for i := 0; i < 100; i++ {
+ testMessage := testMessages[i*256:(i+1)*256]
+ req, _ := http.NewRequest("POST", "/" + testWalletAliceString, bytes.NewReader(testMessage))
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusNoContent, response.Code)
+
+ body := response.Body.String()
+ if body != "" {
+ t.Errorf("Expected empty response, Got %s", body)
+ }
+ }
+
+ req, _ := http.NewRequest("GET", "/" + testWalletAliceString, nil)
+ response := executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+
+ if response.Body.Len() != 25600 {
+ t.Errorf("Expected response of 25600 bytes, Got %d", response.Body.Len())
+ }
+
+ etag := response.Result().Header.Get("ETag")
+
+ if etag == "" {
+ t.Errorf("ETag missing!\n")
+ }
+
+ // Now delete 10 messages
+ h := sha512.New()
+ for i := 0; i < 10; i++ {
+ h.Write(testMessages[i*256:(i+1)*256])
+ }
+ var signed_msg [64+4+4]byte
+ size := signed_msg[0:4]
+ binary.BigEndian.PutUint32(size, 64+4+4)
+ purp := signed_msg[4:8]
+ binary.BigEndian.PutUint32(purp, gana.TALER_SIGNATURE_PURPOSE_MAILBOX_MESSAGES_DELETE)
+ checksum := h.Sum(nil)
+ copy(signed_msg[8:], checksum)
+ sig := ed25519.Sign(testWalletAlicePriv, signed_msg[0:])
+ if !ed25519.Verify(testWalletAlice, signed_msg[0:], sig) {
+ t.Errorf("Signature invalid!")
+ }
+ deletionReq.WalletSig = gnunetutil.EncodeBinaryToString(sig)
+ deletionReq.Count = 10
+ deletionReq.Checksum = gnunetutil.EncodeBinaryToString(checksum)
+ reqJson, _ := json.Marshal(deletionReq)
+
+ hAddress := gnunetutil.EncodeBinaryToString(testWalletAlice)
+ req, _ = http.NewRequest("DELETE", "/" + hAddress, bytes.NewBuffer(reqJson))
+ req.Header.Add("If-Match", etag)
+ response = executeRequest(req)
+
+ checkResponseCode(t, http.StatusNoContent, response.Code)
+
+ body := response.Body.String()
+ if body != "" {
+ t.Errorf("Expected empty response, Got %s", body)
+ }
+
+ req, _ = http.NewRequest("GET", "/" + testWalletAliceString, nil)
+ response = executeRequest(req)
+
+ checkResponseCode(t, http.StatusOK, response.Code)
+
+ if response.Body.Len() != 25600 - 2560 {
+ t.Errorf("Expected response of 25600 - 2560 bytes, Got %d", response.Body.Len())
+ }
+}
diff --git a/cmd/mailbox-server/test-mailbox.conf b/cmd/mailbox-server/test-mailbox.conf
@@ -0,0 +1,12 @@
+[mailbox]
+bind_to = localhost:11000
+production = false
+message_body_bytes = 256
+message_fee = KUDOS:0
+
+[mailbox-pq]
+host = localhost
+port = 5432
+user = mar33597
+password = secret
+db_name = taler-mailbox-test
diff --git a/pkg/rest/mailbox.go b/pkg/rest/mailbox.go
@@ -277,43 +277,41 @@ func (m *Mailbox) deleteMessagesResponse(w http.ResponseWriter, r *http.Request)
}
checksum, err := gnunetutil.DecodeStringToBinary(msg.Checksum, 64)
if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
+ w.WriteHeader(http.StatusBadRequest)
return
}
pk := ed25519.PublicKey(pkey)
sig, err := gnunetutil.DecodeStringToBinary(msg.WalletSig, 64)
if nil != err {
- w.WriteHeader(http.StatusForbidden)
+ w.WriteHeader(http.StatusBadRequest)
return
}
- var signed_msg bytes.Buffer
- size := make([]byte, 4)
+ var signed_msg [72]byte
+ size := signed_msg[0:4]
binary.BigEndian.PutUint32(size, 64+4+4)
- purp := make([]byte, 4)
+ purp := signed_msg[4:8]
binary.BigEndian.PutUint32(purp, gana.TALER_SIGNATURE_PURPOSE_MAILBOX_MESSAGES_DELETE)
- signed_msg.Write(size)
- signed_msg.Write(purp)
- signed_msg.Write(checksum)
- if !ed25519.Verify(pk, signed_msg.Bytes(), sig) {
+ copy(signed_msg[8:], checksum)
+ if !ed25519.Verify(pk, signed_msg[0:], sig) {
w.WriteHeader(http.StatusForbidden)
return
}
h := sha512.New()
h.Write(pkey)
h_mailbox := gnunetutil.EncodeBinaryToString(h.Sum(nil))
- err = m.Db.Where("h_mailbox = ? AND ID >= ? LIMIT ?", h_mailbox, etag_expected, msg.Count).Find(&entries).Error
+ err = m.Db.Where("h_mailbox = ? AND id >= ?", h_mailbox, etag_expected).Limit(msg.Count).Find(&entries).Error
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
- if entries[0].ID != uint(etag_expected) {
- w.WriteHeader(http.StatusPreconditionFailed)
- return
- }
if len(entries) != msg.Count {
w.WriteHeader(http.StatusNotFound)
return
}
+ if entries[0].ID != uint(etag_expected) {
+ w.WriteHeader(http.StatusPreconditionFailed)
+ return
+ }
h_all := sha512.New()
for _, entry := range entries {
h_all.Write(entry.Body)
@@ -347,7 +345,7 @@ func (m *Mailbox) setupHandlers() {
/* Mailbox API */
m.Router.HandleFunc("/{h_mailbox}", m.sendMessageResponse).Methods("POST")
m.Router.HandleFunc("/{h_mailbox}", m.getMessagesResponse).Methods("GET")
- m.Router.HandleFunc("/{h_mailbox}", m.deleteMessagesResponse).Methods("DELETE")
+ m.Router.HandleFunc("/{mailbox}", m.deleteMessagesResponse).Methods("DELETE")
}
// Initialize the Mailbox instance with cfgfile