Skip to content

Commit

Permalink
Update to v0.16.16 & add e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lil5 committed Dec 3, 2024
1 parent e706340 commit d892469
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ docker_setup:
docker_remove:
docker compose down -v --remove-orphans

e2e_test:
docker compose up -d
go test --tags e2e ./...
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: tigerbeetle_api

services:
tigerbeetle:
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.0
image: ghcr.io/tigerbeetle/tigerbeetle:0.16.16
volumes:
- tigerbeetle:/data
ports:
Expand All @@ -16,6 +16,7 @@ services:
retries: 5
environment:
- TB_ADDRESSES=0.0.0.0:3033
# command: format --cluster=0 --replica=0 --replica-count=1 /data/0_0.tigerbeetle
command: start --addresses=0.0.0.0:3033 /data/0_0.tigerbeetle

volumes:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/jinzhu/configor v1.2.2
github.com/samber/lo v1.47.0
github.com/stretchr/testify v1.9.0
github.com/tigerbeetle/tigerbeetle-go v0.16.0
github.com/tigerbeetle/tigerbeetle-go v0.16.16
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tigerbeetle/tigerbeetle-go v0.16.0 h1:A6yelqhpbwSA8l5kwZx1Xb/FPCUmd4W3zpgKnidqM7s=
github.com/tigerbeetle/tigerbeetle-go v0.16.0/go.mod h1:d6G7n4OlD7GLHd62x0VlWPXeI/L0SoNNTfm/ee24GJI=
github.com/tigerbeetle/tigerbeetle-go v0.16.16 h1:lbaebZVP9PGPrS8pPkT6BzdJ0Oaxl07s0jW3Woh1oA8=
github.com/tigerbeetle/tigerbeetle-go v0.16.16/go.mod h1:d6G7n4OlD7GLHd62x0VlWPXeI/L0SoNNTfm/ee24GJI=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
Expand Down
279 changes: 279 additions & 0 deletions suite_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
//go:build e2e

package main

import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"log/slog"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"strings"
"testing"
"time"

"github.com/gin-gonic/gin"
"github.com/lil5/tigerbeetle_api/app"
"github.com/stretchr/testify/suite"
tigerbeetle_go "github.com/tigerbeetle/tigerbeetle-go"
"github.com/tigerbeetle/tigerbeetle-go/pkg/types"
)

const LEDGER = 99

type MyTestSuite struct {
suite.Suite
server app.Server
}

var tbAddresses *string
var tbClusterId *int

// listen for 'go test' command --> run test methods
func TestMyTestSuite(t *testing.T) {
tbAddresses = flag.String("tb_addresses", "127.0.0.1:3033", "Tigerbeetle server address")
tbClusterId = flag.Int("tb_cluster_id", 0, "Tigerbeetle server address")
flag.Parse()

slog.SetLogLoggerLevel(slog.LevelDebug)
slog.Info("Starting test", "tbAddresses", *tbAddresses, "tbClusterId", *tbClusterId)

suite.Run(t, new(MyTestSuite))
}

// run once, before test suite methods
func (s *MyTestSuite) SetupSuite() {
slog.Info("SetupSuite()")

// setup tigerbeetle server
err := exec.Command("/bin/bash", "-c", "docker compose up -d").Run()
if err != nil {
log.Fatal(err)
}

// connect to tigerbeetle server
tb, err := tigerbeetle_go.NewClient(types.ToUint128(uint64(*tbClusterId)), strings.Split(*tbAddresses, ","))
if err != nil {
slog.Error("unable to connect to tigerbeetle:", "err", err)
os.Exit(1)
}

s.server = app.Server{TB: tb}

gin.SetMode(gin.TestMode)
}

// run once, after test suite methods
func (s *MyTestSuite) TearDownSuite() {
log.Println("TearDownSuite()")

// stop the tb client
s.server.TB.Close()
// stop the tb server
err := exec.Command("/bin/bash", "-c", "docker compose down").Run()
if err != nil {
log.Fatal(err)
}

}

func (s *MyTestSuite) TestGetID() {
id, err := s.RunGetID()
s.Nil(err, "body: %s, err: %v", id, err)
s.Len(id, 31)
}

func (s *MyTestSuite) RunGetID() (string, error) {
c, resultFunc := MockGinContext(http.MethodPost, "/", nil)
s.server.GetID(c)
result := resultFunc()
if result.Response.StatusCode != http.StatusOK {
return "", fmt.Errorf("Unexpected status code: %d, body: %s", result.Response.StatusCode, result.Body)
}
json := result.BodyJSON()

return json["id"].(string), nil
}

func (s *MyTestSuite) TestCalls() {
accountID1, _ := s.RunGetID()
accountID2, _ := s.RunGetID()
s.Run("CreateAccounts", func() {
creditsMustNotExceedDebits := false
debitsMustNotExceedCredits := false
c, resultFunc := MockGinContext(http.MethodPost, "/", &gin.H{
"accounts": []gin.H{
{
"user_data_128": nil,
"user_data_64": nil,
"user_data_32": nil,
"id": accountID1,
"debits_pending": 0,
"debits_posted": 0,
"credits_pending": 0,
"credits_posted": 0,
"ledger": LEDGER,
"code": 1,
"flags": gin.H{
"linked": false,
"credits_must_not_exceed_debits": creditsMustNotExceedDebits,
"debits_must_not_exceed_credits": debitsMustNotExceedCredits,
"history": true,
},
"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
}, {
"user_data_128": nil,
"user_data_64": nil,
"user_data_32": nil,
"id": accountID2,
"debits_pending": 0,
"debits_posted": 0,
"credits_pending": 0,
"credits_posted": 0,
"ledger": LEDGER,
"code": 1,
"flags": gin.H{
"linked": false,
"credits_must_not_exceed_debits": creditsMustNotExceedDebits,
"debits_must_not_exceed_credits": debitsMustNotExceedCredits,
"history": true,
},
"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
},
},
})
s.server.CreateAccounts(c)
result := resultFunc()

s.Equal(http.StatusOK, result.Response.StatusCode, result.Body)
})

s.Run("LookupAccounts empty", func() {
c, resultFunc := MockGinContext(http.MethodPost, "/", &gin.H{
"account_ids": []string{accountID1, accountID2},
})
s.server.LookupAccounts(c)
result := resultFunc()

json := result.BodyJSON()
s.Equal(http.StatusOK, result.Response.StatusCode)
jsonAccounts := json["accounts"].([]any)
s.Len(jsonAccounts, 2)
s.Equal(0.0, (jsonAccounts[0].(map[string]any))["debits_posted"])
s.Equal(0.0, (jsonAccounts[0].(map[string]any))["credits_posted"])
s.Equal(0.0, (jsonAccounts[1].(map[string]any))["debits_posted"])
s.Equal(0.0, (jsonAccounts[1].(map[string]any))["credits_posted"])
})

s.Run("CreateTransfer", func() {
slog.Info("Creating transfer, take out 10 from account 2 and put 10 in account 1")
id, _ := s.RunGetID()
c, resultFunc := MockGinContext(http.MethodPost, "/", &gin.H{
"transfers": []gin.H{{
"user_data_128": nil,
"user_data_64": nil,
"user_data_32": nil,
"id": id,
"debit_account_id": accountID1,
"credit_account_id": accountID2,
"amount": 10,
"pending_id": nil,
"ledger": LEDGER,
"code": 1,
"transfer_flags": gin.H{
"linked": false,
"pending": false,
"post_pending_transfer": false,
"void_pending_transfer": false,
"balancing_debit": false,
"balancing_credit": false,
},
"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
}},
})
s.server.CreateTransfers(c)
result := resultFunc()

s.Equal(http.StatusOK, result.Response.StatusCode)
})

s.Run("LookupAccounts after 1 transfer", func() {
c, resultFunc := MockGinContext(http.MethodPost, "/", &gin.H{
"account_ids": []string{accountID1, accountID2},
})
s.server.LookupAccounts(c)
result := resultFunc()

json := result.BodyJSON()
s.Equal(http.StatusOK, result.Response.StatusCode)
jsonAccounts := json["accounts"].([]any)
s.Len(jsonAccounts, 2)

s.Equal(10.0, (jsonAccounts[0].(map[string]any))["debits_posted"])
s.Equal(0.0, (jsonAccounts[0].(map[string]any))["credits_posted"])
s.Equal(0.0, (jsonAccounts[1].(map[string]any))["debits_posted"])
s.Equal(10.0, (jsonAccounts[1].(map[string]any))["credits_posted"])
})
}

// utility functions
// ----------------------------------------------------------------
type mockGinContextResponse struct {
Response *http.Response
Body string
}

func (r mockGinContextResponse) BodyJSON() gin.H {
body := gin.H{}
json.Unmarshal([]byte(r.Body), &body)

return body
}

func MockGinContext(method string, url string, bodyJSON *gin.H) (*gin.Context, func() mockGinContextResponse) {
body := bytes.NewBuffer([]byte{})
if bodyJSON != nil {
json_data, _ := json.Marshal(bodyJSON)
body = bytes.NewBuffer(json_data)
}

r := httptest.NewRequest(method, url, body)
rr := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rr)
c.Request = r

// ro.ServeHTTP(rr, c.Request)
resultFunc := func() mockGinContextResponse {
body := rr.Body.String()
res := rr.Result()
return mockGinContextResponse{
Response: res,
Body: body,
}
}

return c, resultFunc
}

/* UserData64: lo.ToPtr(add.Date.Format(time.RFC3339Nano)),
UserData128: posting.RelatedID,
ID: id,
DebitAccountID: posting.AccountDebit,
CreditAccountID: posting.AccountCredit,
PendingID: posting.PendingID,
Amount: posting.Amount,
Ledger: posting.Ledger,
Code: posting.Code,
TransferFlags: &TransferFlags{
Linked: i != lastIndexPostings,
Pending: posting.CreatePending,
PostPendingTransfer: posting.SubmitPending,
VoidPendingTransfer: posting.VoidPending,
BalancingDebit: false,
BalancingCredit: false,
},*/

0 comments on commit d892469

Please sign in to comment.