diff --git a/src/ui/main.go b/src/ui/main.go
index 499c55b6..1411ccdd 100644
--- a/src/ui/main.go
+++ b/src/ui/main.go
@@ -14,6 +14,7 @@ import (
"github.com/my-cloud/ruthenium/src/ui/server/index"
"github.com/my-cloud/ruthenium/src/ui/server/transaction"
"github.com/my-cloud/ruthenium/src/ui/server/transaction/info"
+ "github.com/my-cloud/ruthenium/src/ui/server/transaction/status"
"github.com/my-cloud/ruthenium/src/ui/server/transactions"
"github.com/my-cloud/ruthenium/src/ui/server/wallet/address"
"github.com/my-cloud/ruthenium/src/ui/server/wallet/amount"
@@ -46,8 +47,9 @@ func main() {
watch := tick.NewWatch()
http.Handle("/", index.NewHandler(*templatesPath, logger))
http.Handle("/transaction", transaction.NewHandler(host, logger))
- http.Handle("/transaction/info", info.NewHandler(host, settings, watch, logger))
http.Handle("/transactions", transactions.NewHandler(host, logger))
+ http.Handle("/transaction/info", info.NewHandler(host, settings, watch, logger))
+ http.Handle("/transaction/status", status.NewHandler(host, settings, watch, logger))
http.Handle("/wallet/address", address.NewHandler(logger))
http.Handle("/wallet/amount", amount.NewHandler(host, settings, watch, logger))
logger.Info("user interface server is running...")
diff --git a/src/ui/server/transaction/handler.go b/src/ui/server/transaction/handler.go
index a8bd8781..5f3af09a 100644
--- a/src/ui/server/transaction/handler.go
+++ b/src/ui/server/transaction/handler.go
@@ -28,9 +28,9 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request)
var transaction *verification.Transaction
err := decoder.Decode(&transaction)
if err != nil {
- handler.logger.Error(fmt.Errorf("failed to decode transaction request: %w", err).Error())
+ handler.logger.Error(fmt.Errorf("failed to decode transaction: %w", err).Error())
writer.WriteHeader(http.StatusBadRequest)
- jsonWriter.Write("invalid transaction request")
+ jsonWriter.Write("invalid transaction")
return
}
transactionRequest := validation.NewTransactionRequest(transaction, handler.host.Target())
@@ -42,7 +42,7 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request)
}
err = handler.host.AddTransaction(marshaledTransaction)
if err != nil {
- handler.logger.Error(fmt.Errorf("failed to create transaction: %w", err).Error())
+ handler.logger.Error(fmt.Errorf("failed to add transaction: %w", err).Error())
writer.WriteHeader(http.StatusInternalServerError)
return
}
diff --git a/src/ui/server/transaction/info/handler.go b/src/ui/server/transaction/info/handler.go
index 94d56be7..66375547 100644
--- a/src/ui/server/transaction/info/handler.go
+++ b/src/ui/server/transaction/info/handler.go
@@ -131,6 +131,7 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request)
return
}
writer.Header().Add("Content-Type", "application/json")
+ writer.WriteHeader(http.StatusOK)
server.NewIoWriter(writer, handler.logger).Write(string(marshaledResponse[:]))
default:
handler.logger.Error("invalid HTTP method")
diff --git a/src/ui/server/transaction/status/handler.go b/src/ui/server/transaction/status/handler.go
new file mode 100644
index 00000000..35f9c688
--- /dev/null
+++ b/src/ui/server/transaction/status/handler.go
@@ -0,0 +1,142 @@
+package status
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/my-cloud/ruthenium/src/log"
+ "github.com/my-cloud/ruthenium/src/node/clock"
+ "github.com/my-cloud/ruthenium/src/node/network"
+ "github.com/my-cloud/ruthenium/src/node/protocol/verification"
+ "github.com/my-cloud/ruthenium/src/ui/server"
+ "net/http"
+)
+
+type Handler struct {
+ host network.Neighbor
+ settings server.Settings
+ watch clock.Watch
+ logger log.Logger
+}
+
+func NewHandler(host network.Neighbor, settings server.Settings, watch clock.Watch, logger log.Logger) *Handler {
+ return &Handler{host, settings, watch, logger}
+}
+
+func (handler *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
+ switch req.Method {
+ case http.MethodPut:
+ jsonWriter := server.NewIoWriter(writer, handler.logger)
+ decoder := json.NewDecoder(req.Body)
+ var transaction *verification.Transaction
+ err := decoder.Decode(&transaction)
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to decode transaction: %w", err).Error())
+ writer.WriteHeader(http.StatusBadRequest)
+ jsonWriter.Write("invalid transaction")
+ return
+ } else if len(transaction.Outputs()) == 0 {
+ handler.logger.Error(errors.New("transaction has no output").Error())
+ writer.WriteHeader(http.StatusBadRequest)
+ jsonWriter.Write("invalid transaction")
+ return
+ }
+ lastOutputIndex := len(transaction.Outputs()) - 1
+ lastOutput := transaction.Outputs()[lastOutputIndex]
+ utxosBytes, err := handler.host.GetUtxos(lastOutput.Address())
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to get UTXOs: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ var utxos []*verification.Utxo
+ err = json.Unmarshal(utxosBytes, &utxos)
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to unmarshal UTXOs: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ genesisTimestamp, err := handler.host.GetFirstBlockTimestamp()
+ now := handler.watch.Now().UnixNano()
+ currentBlockHeight := (now - genesisTimestamp) / handler.settings.ValidationTimestamp()
+ currentBlockTimestamp := genesisTimestamp + currentBlockHeight*handler.settings.ValidationTimestamp()
+ progress := &Progress{
+ CurrentBlockTimestamp: currentBlockTimestamp,
+ ValidationTimestamp: handler.settings.ValidationTimestamp(),
+ }
+ for _, utxo := range utxos {
+ if utxo.TransactionId() == transaction.Id() && utxo.OutputIndex() == uint16(lastOutputIndex) {
+ progress.TransactionStatus = "confirmed"
+ handler.sendResponse(writer, progress)
+ return
+ }
+ }
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to get genesis timestamp: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ blocksBytes, err := handler.host.GetBlocks(uint64(currentBlockHeight))
+ if err != nil {
+ handler.logger.Error("failed to get blocks")
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ var blocks []*verification.Block
+ err = json.Unmarshal(blocksBytes, &blocks)
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to unmarshal blocks: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ if len(blocks) == 0 {
+ handler.logger.Error("failed to get last block, get blocks returned an empty list")
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ for _, validatedTransaction := range blocks[0].Transactions() {
+ if validatedTransaction.Id() == transaction.Id() {
+ progress.TransactionStatus = "validated"
+ handler.sendResponse(writer, progress)
+ return
+ }
+ }
+ transactionsBytes, err := handler.host.GetTransactions()
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to get transactions: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ var transactions []*verification.Transaction
+ err = json.Unmarshal(transactionsBytes, &transactions)
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to unmarshal transactions: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ for _, pendingTransaction := range transactions {
+ if pendingTransaction.Id() == transaction.Id() {
+ progress.TransactionStatus = "sent"
+ handler.sendResponse(writer, progress)
+ return
+ }
+ }
+ progress.TransactionStatus = "rejected"
+ handler.sendResponse(writer, progress)
+ default:
+ handler.logger.Error("invalid HTTP method")
+ writer.WriteHeader(http.StatusBadRequest)
+ }
+}
+
+func (handler *Handler) sendResponse(writer http.ResponseWriter, progress *Progress) {
+ marshaledResponse, err := json.Marshal(progress)
+ if err != nil {
+ handler.logger.Error(fmt.Errorf("failed to marshal progress: %w", err).Error())
+ writer.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ writer.Header().Add("Content-Type", "application/json")
+ writer.WriteHeader(http.StatusOK)
+ server.NewIoWriter(writer, handler.logger).Write(string(marshaledResponse[:]))
+}
diff --git a/src/ui/server/transaction/status/progress.go b/src/ui/server/transaction/status/progress.go
new file mode 100644
index 00000000..c61d676e
--- /dev/null
+++ b/src/ui/server/transaction/status/progress.go
@@ -0,0 +1,7 @@
+package status
+
+type Progress struct {
+ CurrentBlockTimestamp int64 `json:"current_block_timestamp"`
+ TransactionStatus string `json:"transaction_status"`
+ ValidationTimestamp int64 `json:"validation_timestamp"`
+}
diff --git a/src/ui/server/transactions/handler.go b/src/ui/server/transactions/handler.go
index 7c360f5d..17450202 100644
--- a/src/ui/server/transactions/handler.go
+++ b/src/ui/server/transactions/handler.go
@@ -27,6 +27,7 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request)
return
}
writer.Header().Add("Content-Type", "application/json")
+ writer.WriteHeader(http.StatusOK)
server.NewIoWriter(writer, handler.logger).Write(string(transactions[:]))
default:
handler.logger.Error("invalid HTTP method")
diff --git a/src/ui/server/wallet/amount/handler.go b/src/ui/server/wallet/amount/handler.go
index 28bfa16f..b2368f81 100644
--- a/src/ui/server/wallet/amount/handler.go
+++ b/src/ui/server/wallet/amount/handler.go
@@ -56,6 +56,7 @@ func (handler *Handler) ServeHTTP(writer http.ResponseWriter, req *http.Request)
return
}
writer.Header().Add("Content-Type", "application/json")
+ writer.WriteHeader(http.StatusOK)
server.NewIoWriter(writer, handler.logger).Write(string(marshaledAmount[:]))
default:
handler.logger.Error("invalid HTTP method")
diff --git a/templates/index.html b/templates/index.html
index 0840b40b..7ea515ca 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -2,6 +2,7 @@
+
Wallet
@@ -49,8 +50,8 @@ Send Tokens
-
-
+
+
-
+
+
@@ -106,6 +110,8 @@
Transactions Pool
});
$(function () {
+ let pendingTransaction;
+
$("#send_tokens_button").click(function () {
if (!keyPair) {
alert("The private key must be provided to send tokens")
@@ -113,7 +119,7 @@ Transactions Pool
}
const senderAddress = $("#sender_address").val();
- const atoms = $("#send_amount").val();
+ const atoms = $("#amount").val();
const result = atomsToParticles(atoms, 100000000);
if (result.err) {
alert(result.err);
@@ -151,7 +157,6 @@ Transactions Pool
"is_registered": false,
"value": value,
}
- console.log(isIncomeUpdateRequested)
const rest = {
"address": senderAddress,
"is_registered": isIncomeUpdateRequested,
@@ -172,6 +177,7 @@ Transactions Pool
success: function (response) {
if (response === "success") {
alert("Send success");
+ pendingTransaction = transaction
} else {
alert("Send failed: " + response)
}
@@ -205,6 +211,7 @@ Transactions Pool
setInterval(refresh_amount, 100)
setInterval(refresh_transactions, 100)
+ setInterval(refresh_progress, 100)
function refresh_amount() {
const $walletAmount = $("#wallet_amount");
@@ -239,6 +246,53 @@ Transactions Pool
}
})
}
+
+ function refresh_progress() {
+ const progressBar = document.querySelector('.progress-circle');
+ if (pendingTransaction === undefined) {
+ progressBar.style.background = `conic-gradient(white 100%, white 0)`;
+ progressBar.textContent = "";
+ } else {
+ $.ajax({
+ url: "/transaction/status",
+ type: "PUT",
+ contentType: "application/json",
+ data: JSON.stringify(pendingTransaction),
+ success: function (response) {
+ const now = new Date().getTime() * 1000000
+ let angle = (now - response.current_block_timestamp) / response.validation_timestamp * 100
+ let color1;
+ let color2;
+ switch (response.transaction_status) {
+ case "sent":
+ color1 = "lightseagreen";
+ color2 = "royalblue";
+ break;
+ case "validated":
+ color1 = "seagreen";
+ color2 = "lightseagreen";
+ break;
+ case "confirmed":
+ color1 = "seagreen";
+ color2 = "seagreen";
+ break;
+ case "rejected":
+ color1 = "brown";
+ color2 = "brown";
+ break;
+ default:
+ color1 = "white";
+ color2 = "white";
+ }
+ progressBar.textContent = response.transaction_status[0]
+ progressBar.style.background = `conic-gradient(${color1} ${angle}%, ${color2} 0)`;
+ },
+ error: function (response) {
+ console.error(response);
+ }
+ })
+ }
+ }
})
function atomsToParticles(atoms, particlesInOneAtom) {
@@ -419,4 +473,26 @@ Transactions Pool
width: 50%;
margin-left: 90px;
}
+
+ .progress {
+ width: 30px;
+ height: 30px;
+ margin-left: 30px;
+ margin-right: 30px;
+ border-radius: 50%;
+ position: absolute;
+ }
+
+ .progress-circle {
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ background: conic-gradient(white 0, white 0);
+ animation: progress 5s 1 forwards;
+ text-align: center;
+ font-weight: bold;
+ font-size: 22px;
+ text-transform: capitalize;
+ color: lightgrey;
+ }
diff --git a/test/ui/server/transaction/handler_test.go b/test/ui/server/transaction/handler_test.go
index fc53f657..2beb70cf 100644
--- a/test/ui/server/transaction/handler_test.go
+++ b/test/ui/server/transaction/handler_test.go
@@ -47,32 +47,10 @@ func Test_ServeHTTP_UndecipherableTransaction_BadRequest(t *testing.T) {
neighborMock := new(networktest.NeighborMock)
logger := logtest.NewLoggerMock()
handler := transaction.NewHandler(neighborMock, logger)
- transactionRequest := ""
- b, _ := json.Marshal(transactionRequest)
- body := bytes.NewReader(b)
+ marshalledData, _ := json.Marshal("")
+ body := bytes.NewReader(marshalledData)
recorder := httptest.NewRecorder()
- request := httptest.NewRequest("POST", urlTarget, body)
-
- // Act
- handler.ServeHTTP(recorder, request)
-
- // Assert
- isNeighborMethodCalled := len(neighborMock.AddTransactionCalls()) != 0
- test.Assert(t, !isNeighborMethodCalled, "Neighbor method is called whereas it should not.")
- expectedStatusCode := 400
- test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
-}
-
-func Test_ServeHTTP_InvalidTransaction_BadRequest(t *testing.T) {
- // Arrange
- neighborMock := new(networktest.NeighborMock)
- neighborMock.TargetFunc = func() string { return "0.0.0.0:0" }
- logger := logtest.NewLoggerMock()
- handler := transaction.NewHandler(neighborMock, logger)
- data, _ := json.Marshal("")
- body := bytes.NewReader(data)
- recorder := httptest.NewRecorder()
- request := httptest.NewRequest("POST", urlTarget, body)
+ request := httptest.NewRequest(http.MethodPost, urlTarget, body)
// Act
handler.ServeHTTP(recorder, request)
@@ -92,14 +70,11 @@ func Test_ServeHTTP_NodeError_InternalServerError(t *testing.T) {
neighborMock.AddTransactionFunc = func([]byte) error { return errors.New("") }
logger := logtest.NewLoggerMock()
handler := transaction.NewHandler(neighborMock, logger)
- address := "RecipientAddress"
- var value uint64 = 0
- var timestamp int64 = 0
- transactionRequest, _ := verification.NewRewardTransaction(address, false, timestamp, value)
+ transactionRequest, _ := verification.NewRewardTransaction("", false, 0, 0)
marshalledTransaction, _ := json.Marshal(transactionRequest)
body := bytes.NewReader(marshalledTransaction)
recorder := httptest.NewRecorder()
- request := httptest.NewRequest("POST", urlTarget, body)
+ request := httptest.NewRequest(http.MethodPost, urlTarget, body)
// Act
handler.ServeHTTP(recorder, request)
@@ -119,14 +94,11 @@ func Test_ServeHTTP_ValidTransaction_NeighborMethodCalled(t *testing.T) {
neighborMock.AddTransactionFunc = func([]byte) error { return nil }
logger := logtest.NewLoggerMock()
handler := transaction.NewHandler(neighborMock, logger)
- address := "RecipientAddress"
- var value uint64 = 0
- var timestamp int64 = 0
- transactionRequest, _ := verification.NewRewardTransaction(address, false, timestamp, value)
+ transactionRequest, _ := verification.NewRewardTransaction("", false, 0, 0)
marshalledTransaction, _ := json.Marshal(transactionRequest)
body := bytes.NewReader(marshalledTransaction)
recorder := httptest.NewRecorder()
- request := httptest.NewRequest("POST", urlTarget, body)
+ request := httptest.NewRequest(http.MethodPost, urlTarget, body)
// Act
handler.ServeHTTP(recorder, request)
diff --git a/test/ui/server/transaction/info/handler_test.go b/test/ui/server/transaction/info/handler_test.go
index ab8e42ad..7445a01b 100644
--- a/test/ui/server/transaction/info/handler_test.go
+++ b/test/ui/server/transaction/info/handler_test.go
@@ -26,11 +26,6 @@ func Test_ServeHTTP_InvalidHttpMethod_BadRequest(t *testing.T) {
neighborMock := new(networktest.NeighborMock)
watchMock := new(clocktest.WatchMock)
settings := new(servertest.SettingsMock)
- settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
- settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
- settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
- settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
- settings.ValidationTimestampFunc = func() int64 { return 1 }
handler := info.NewHandler(neighborMock, settings, watchMock, logger)
recorder := httptest.NewRecorder()
invalidHttpMethods := []string{http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace}
@@ -143,7 +138,7 @@ func Test_ServeHTTP_GetUtxosError_ReturnsInternalServerError(t *testing.T) {
test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
}
-func Test_ServeHTTP_GetFirstBlockTimestampFuncError_ReturnsInternalServerError(t *testing.T) {
+func Test_ServeHTTP_GetFirstBlockTimestampError_ReturnsInternalServerError(t *testing.T) {
// Arrange
logger := logtest.NewLoggerMock()
neighborMock := new(networktest.NeighborMock)
diff --git a/test/ui/server/transaction/status/handler_test.go b/test/ui/server/transaction/status/handler_test.go
new file mode 100644
index 00000000..0a212fd3
--- /dev/null
+++ b/test/ui/server/transaction/status/handler_test.go
@@ -0,0 +1,374 @@
+package info
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/my-cloud/ruthenium/src/node/protocol/verification"
+ "github.com/my-cloud/ruthenium/src/ui/server/transaction/status"
+ "github.com/my-cloud/ruthenium/test"
+ "github.com/my-cloud/ruthenium/test/log/logtest"
+ "github.com/my-cloud/ruthenium/test/node/clock/clocktest"
+ "github.com/my-cloud/ruthenium/test/node/network/networktest"
+ "github.com/my-cloud/ruthenium/test/ui/server/servertest"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+)
+
+const urlTarget = "/url-target"
+
+func Test_ServeHTTP_InvalidHttpMethod_BadRequest(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ watchMock := new(clocktest.WatchMock)
+ settings := new(servertest.SettingsMock)
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ invalidHttpMethods := []string{http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace}
+ for _, method := range invalidHttpMethods {
+ t.Run(method, func(t *testing.T) {
+ request := httptest.NewRequest(method, urlTarget, nil)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ expectedStatusCode := 400
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+ })
+ }
+}
+
+func Test_ServeHTTP_UndecipherableTransaction_BadRequest(t *testing.T) {
+ // Arrange
+ neighborMock := new(networktest.NeighborMock)
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ watchMock := new(clocktest.WatchMock)
+ logger := logtest.NewLoggerMock()
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ data := ""
+ marshalledData, _ := json.Marshal(data)
+ body := bytes.NewReader(marshalledData)
+ recorder := httptest.NewRecorder()
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ isNeighborMethodCalled := len(neighborMock.AddTransactionCalls()) != 0
+ test.Assert(t, !isNeighborMethodCalled, "Neighbor method is called whereas it should not.")
+ expectedStatusCode := 400
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+}
+
+func Test_ServeHTTP_GetUtxosError_ReturnsInternalServerError(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return nil, errors.New("") }
+ watchMock := new(clocktest.WatchMock)
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ isNeighborMethodCalled := len(neighborMock.GetUtxosCalls()) == 1
+ test.Assert(t, isNeighborMethodCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 500
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+}
+
+func Test_ServeHTTP_GetFirstBlockTimestampError_ReturnsInternalServerError(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ marshalledEmptyArray := []byte{91, 93}
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyArray, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, errors.New("") }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 500
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+}
+
+func Test_ServeHTTP_GetBlocksError_ReturnsInternalServerError(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ marshalledEmptyArray := []byte{91, 93}
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyArray, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, nil }
+ neighborMock.GetBlocksFunc = func(uint64) ([]byte, error) { return nil, errors.New("") }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1 && len(neighborMock.GetBlocksCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 500
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+}
+
+func Test_ServeHTTP_GetTransactionsError_ReturnsInternalServerError(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ marshalledEmptyArray := []byte{91, 93}
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyArray, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, nil }
+ blocks := []*verification.Block{verification.NewBlock(0, [32]byte{}, nil, nil, nil)}
+ marshalledBlocks, _ := json.Marshal(blocks)
+ neighborMock.GetBlocksFunc = func(uint64) ([]byte, error) { return marshalledBlocks, nil }
+ neighborMock.GetTransactionsFunc = func() ([]byte, error) { return nil, errors.New("") }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1 && len(neighborMock.GetBlocksCalls()) == 1 && len(neighborMock.GetTransactionsCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 500
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+}
+
+func Test_ServeHTTP_TransactionNotFound_ReturnsRejected(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ marshalledEmptyArray := []byte{91, 93}
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyArray, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, nil }
+ blocks := []*verification.Block{verification.NewBlock(0, [32]byte{}, nil, nil, nil)}
+ marshalledBlocks, _ := json.Marshal(blocks)
+ neighborMock.GetBlocksFunc = func(uint64) ([]byte, error) { return marshalledBlocks, nil }
+ neighborMock.GetTransactionsFunc = func() ([]byte, error) { return marshalledEmptyArray, nil }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1 && len(neighborMock.GetBlocksCalls()) == 1 && len(neighborMock.GetTransactionsCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 200
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+ expectedStatus := "rejected"
+ response := recorder.Body.Bytes()
+ var progress *status.Progress
+ err := json.Unmarshal(response, &progress)
+ fmt.Println(err)
+ actualStatus := progress.TransactionStatus
+ test.Assert(t, actualStatus == expectedStatus, fmt.Sprintf("Wrong response. expected: %s actual: %s", expectedStatus, actualStatus))
+}
+
+func Test_ServeHTTP_UtxoFound_ReturnsConfirmed(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ transactionId := transaction.Id()
+ inputInfo := verification.NewInputInfo(0, transactionId)
+ utxo := verification.NewUtxo(inputInfo, &verification.Output{}, 0)
+ marshalledEmptyUtxos, _ := json.Marshal([]*verification.Utxo{utxo})
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyUtxos, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, nil }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 200
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+ expectedStatus := "confirmed"
+ response := recorder.Body.Bytes()
+ var progress *status.Progress
+ err := json.Unmarshal(response, &progress)
+ fmt.Println(err)
+ actualStatus := progress.TransactionStatus
+ test.Assert(t, actualStatus == expectedStatus, fmt.Sprintf("Wrong response. expected: %s actual: %s", expectedStatus, actualStatus))
+}
+
+func Test_ServeHTTP_ValidatedTransactionFound_ReturnsValidated(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ marshalledEmptyArray := []byte{91, 93}
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyArray, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, nil }
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ blocks := []*verification.Block{verification.NewBlock(0, [32]byte{}, []*verification.Transaction{transaction}, nil, nil)}
+ marshalledBlocks, _ := json.Marshal(blocks)
+ neighborMock.GetBlocksFunc = func(uint64) ([]byte, error) { return marshalledBlocks, nil }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1 && len(neighborMock.GetBlocksCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 200
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+ expectedStatus := "validated"
+ response := recorder.Body.Bytes()
+ var progress *status.Progress
+ err := json.Unmarshal(response, &progress)
+ fmt.Println(err)
+ actualStatus := progress.TransactionStatus
+ test.Assert(t, actualStatus == expectedStatus, fmt.Sprintf("Wrong response. expected: %s actual: %s", expectedStatus, actualStatus))
+}
+
+func Test_ServeHTTP_PendingTransactionFound_ReturnsSent(t *testing.T) {
+ // Arrange
+ logger := logtest.NewLoggerMock()
+ neighborMock := new(networktest.NeighborMock)
+ marshalledEmptyArray := []byte{91, 93}
+ neighborMock.GetUtxosFunc = func(string) ([]byte, error) { return marshalledEmptyArray, nil }
+ neighborMock.GetFirstBlockTimestampFunc = func() (int64, error) { return 0, nil }
+ blocks := []*verification.Block{verification.NewBlock(0, [32]byte{}, nil, nil, nil)}
+ marshalledBlocks, _ := json.Marshal(blocks)
+ neighborMock.GetBlocksFunc = func(uint64) ([]byte, error) { return marshalledBlocks, nil }
+ transaction, _ := verification.NewRewardTransaction("", false, 0, 0)
+ transactions := []*verification.Transaction{transaction}
+ marshalledTransactions, _ := json.Marshal(transactions)
+ neighborMock.GetTransactionsFunc = func() ([]byte, error) { return marshalledTransactions, nil }
+ watchMock := new(clocktest.WatchMock)
+ watchMock.NowFunc = func() time.Time { return time.Unix(0, 0) }
+ settings := new(servertest.SettingsMock)
+ settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
+ settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
+ settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
+ settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
+ settings.ValidationTimestampFunc = func() int64 { return 1 }
+ handler := status.NewHandler(neighborMock, settings, watchMock, logger)
+ recorder := httptest.NewRecorder()
+ marshalledTransaction, _ := json.Marshal(transaction)
+ body := bytes.NewReader(marshalledTransaction)
+ request := httptest.NewRequest(http.MethodPut, urlTarget, body)
+
+ // Act
+ handler.ServeHTTP(recorder, request)
+
+ // Assert
+ areNeighborMethodsCalled := len(neighborMock.GetUtxosCalls()) == 1 && len(neighborMock.GetFirstBlockTimestampCalls()) == 1 && len(neighborMock.GetBlocksCalls()) == 1 && len(neighborMock.GetTransactionsCalls()) == 1
+ test.Assert(t, areNeighborMethodsCalled, "Neighbor method is not called whereas it should be.")
+ expectedStatusCode := 200
+ test.Assert(t, recorder.Code == expectedStatusCode, fmt.Sprintf("Wrong response status code. expected: %d actual: %d", expectedStatusCode, recorder.Code))
+ expectedStatus := "sent"
+ response := recorder.Body.Bytes()
+ var progress *status.Progress
+ err := json.Unmarshal(response, &progress)
+ fmt.Println(err)
+ actualStatus := progress.TransactionStatus
+ test.Assert(t, actualStatus == expectedStatus, fmt.Sprintf("Wrong response. expected: %s actual: %s", expectedStatus, actualStatus))
+}
diff --git a/test/ui/server/transactions/handler_test.go b/test/ui/server/transactions/handler_test.go
index ac41d1c2..8b8cfaa0 100644
--- a/test/ui/server/transactions/handler_test.go
+++ b/test/ui/server/transactions/handler_test.go
@@ -44,7 +44,7 @@ func Test_ServeHTTP_NodeError_InternalServerError(t *testing.T) {
logger := logtest.NewLoggerMock()
handler := transactions.NewHandler(neighborMock, logger)
recorder := httptest.NewRecorder()
- request := httptest.NewRequest("GET", urlTarget, nil)
+ request := httptest.NewRequest(http.MethodGet, urlTarget, nil)
// Act
handler.ServeHTTP(recorder, request)
@@ -63,7 +63,7 @@ func Test_ServeHTTP_ValidRequest_NeighborMethodCalled(t *testing.T) {
logger := logtest.NewLoggerMock()
handler := transactions.NewHandler(neighborMock, logger)
recorder := httptest.NewRecorder()
- request := httptest.NewRequest("GET", urlTarget, nil)
+ request := httptest.NewRequest(http.MethodGet, urlTarget, nil)
// Act
handler.ServeHTTP(recorder, request)
diff --git a/test/ui/server/wallet/amount/handler_test.go b/test/ui/server/wallet/amount/handler_test.go
index 346382d7..2b97b232 100644
--- a/test/ui/server/wallet/amount/handler_test.go
+++ b/test/ui/server/wallet/amount/handler_test.go
@@ -26,11 +26,6 @@ func Test_ServeHTTP_InvalidHttpMethod_BadRequest(t *testing.T) {
neighborMock := new(networktest.NeighborMock)
watchMock := new(clocktest.WatchMock)
settings := new(servertest.SettingsMock)
- settings.HalfLifeInNanosecondsFunc = func() float64 { return 0 }
- settings.IncomeBaseInParticlesFunc = func() uint64 { return 0 }
- settings.IncomeLimitInParticlesFunc = func() uint64 { return 0 }
- settings.ParticlesPerTokenFunc = func() uint64 { return 1 }
- settings.ValidationTimestampFunc = func() int64 { return 1 }
handler := amount.NewHandler(neighborMock, settings, watchMock, logger)
recorder := httptest.NewRecorder()
invalidHttpMethods := []string{http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace}