Files
Adithya Vardhan badb700837 chore: allow 0 limits for send/receive amount/volume (#486)
* chore: allow 0 limits for send/receive amount/volume

* fix: use pointers for claims

* fix: tests to set default as -1

* chore: add 0 check in outgoing exceeded tests

* chore: also default max account balance to -1

* chore: remove dbUri :P

* chore: remove print statements

* chore: add more tests for limits
2024-04-05 09:21:08 +02:00

311 lines
12 KiB
Go

package integration_tests
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"time"
"github.com/getAlby/lndhub.go/common"
"github.com/getAlby/lndhub.go/db"
"github.com/getAlby/lndhub.go/db/migrations"
"github.com/getAlby/lndhub.go/db/models"
"github.com/getAlby/lndhub.go/lib"
"github.com/getAlby/lndhub.go/lib/responses"
"github.com/getAlby/lndhub.go/lib/service"
"github.com/getAlby/lndhub.go/lnd"
"github.com/getAlby/lndhub.go/rabbitmq"
"github.com/golang-jwt/jwt"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/uptrace/bun/migrate"
)
//not used anymore
// const (
// lnd1RegtestAddress = "rpc.lnd1.regtest.getalby.com:443"
// lnd1RegtestMacaroonHex = "0201036c6e6402f801030a10e2133a1cac2c5b4d56e44e32dc64c8551201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620c4f9783e0873fa50a2091806f5ebb919c5dc432e33800b401463ada6485df0ed"
// lnd2RegtestAddress = "rpc.lnd2.regtest.getalby.com:443"
// lnd2RegtestMacaroonHex = "0201036C6E6402F801030A101782922F4358E80655920FC7A7C3E9291201301A160A0761646472657373120472656164120577726974651A130A04696E666F120472656164120577726974651A170A08696E766F69636573120472656164120577726974651A210A086D616361726F6F6E120867656E6572617465120472656164120577726974651A160A076D657373616765120472656164120577726974651A170A086F6666636861696E120472656164120577726974651A160A076F6E636861696E120472656164120577726974651A140A057065657273120472656164120577726974651A180A067369676E6572120867656E657261746512047265616400000620628FFB2938C8540DD3AA5E578D9B43456835FAA176E175FFD4F9FBAE540E3BE9"
// // Use lnd3 for a funding client when testing out fee handling for payments done by lnd-1, since lnd3 doesn't have a direct channel to lnd1.
// // This will cause payment to be routed through lnd2, which will charge a fee (lnd default fee 1 sat base + 1 ppm).
// lnd3RegtestAddress = "rpc.lnd3.regtest.getalby.com:443"
// lnd3RegtestMacaroonHex = "0201036c6e6402f801030a102a5aa69a5efdf4b4a55a5304b164641f1201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620defbb5a809262297fd661a9ab6d3deb4b7acca4f1309c79addb952f0dc2d8c82"
// simnetLnd1PubKey = "0242898f86064c2fd72de22059c947a83ba23e9d97aedeae7b6dba647123f1d71b"
// simnetLnd2PubKey = "025c1d5d1b4c983cc6350fc2d756fbb59b4dc365e45e87f8e3afe07e24013e8220"
// simnetLnd3PubKey = "03c7092d076f799ab18806743634b4c9bb34e351bdebc91d5b35963f3dc63ec5aa"
// )
const (
mockLNDAddress = "mock.lnd.local"
mockLNDMacaroonHex = "omnomnom"
)
func LndHubTestServiceInit(lndClientMock lnd.LightningClientWrapper) (svc *service.LndhubService, err error) {
dbUri, ok := os.LookupEnv("DATABASE_URI")
if !ok {
dbUri = "postgresql://user:password@localhost/lndhub?sslmode=disable"
}
c := &service.Config{
DatabaseUri: dbUri,
DatabaseMaxConns: 1,
DatabaseMaxIdleConns: 1,
DatabaseConnMaxLifetime: 10,
MaxFeeAmount: 1e6,
JWTSecret: []byte("SECRET"),
JWTAccessTokenExpiry: 3600,
JWTRefreshTokenExpiry: 3600,
MaxSendAmount: -1,
MaxSendVolume: -1,
MaxReceiveAmount: -1,
MaxReceiveVolume: -1,
MaxAccountBalance: -1,
}
rabbitmqUri, ok := os.LookupEnv("RABBITMQ_URI")
var rabbitmqClient rabbitmq.Client
if ok {
c.RabbitMQUri = rabbitmqUri
c.RabbitMQLndhubInvoiceExchange = "test_lndhub_invoices"
c.RabbitMQLndInvoiceExchange = "test_lnd_invoices"
amqpClient, err := rabbitmq.DialAMQP(c.RabbitMQUri)
if err != nil {
return nil, err
}
rabbitmqClient, err = rabbitmq.NewClient(amqpClient,
rabbitmq.WithLndInvoiceExchange(c.RabbitMQLndInvoiceExchange),
rabbitmq.WithLndHubInvoiceExchange(c.RabbitMQLndhubInvoiceExchange),
rabbitmq.WithLndInvoiceConsumerQueueName(c.RabbitMQInvoiceConsumerQueueName),
)
if err != nil {
return nil, err
}
}
dbConn, err := db.Open(c)
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
ctx := context.Background()
migrator := migrate.NewMigrator(dbConn, migrations.Migrations)
err = migrator.Init(ctx)
if err != nil {
return nil, fmt.Errorf("failed to init migrations: %w", err)
}
_, err = migrator.Migrate(ctx)
if err != nil {
return nil, fmt.Errorf("failed to migrate: %w", err)
}
logger := lib.Logger(c.LogFilePath)
svc = &service.LndhubService{
Config: c,
DB: dbConn,
LndClient: lndClientMock,
Logger: logger,
RabbitMQClient: rabbitmqClient,
}
svc.InvoicePubSub = service.NewPubsub()
return svc, nil
}
func clearTable(svc *service.LndhubService, tableName string) error {
dbConn, err := db.Open(svc.Config)
if err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
_, err = dbConn.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
return err
}
// unsafe parse jwt method to pull out userId claim
// should be used only in integration_tests package
func getUserIdFromToken(token string) int64 {
parsedToken, _, _ := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{})
claims, _ := parsedToken.Claims.(jwt.MapClaims)
return int64(claims["id"].(float64))
}
// since svc.invoicesFor excludes erroneous invoices, this is used for testing
func invoicesFor(svc *service.LndhubService, userId int64, invoiceType string) ([]models.Invoice, error) {
var invoices []models.Invoice
query := svc.DB.NewSelect().Model(&invoices).Where("user_id = ?", userId)
if invoiceType != "" {
query.Where("type = ? AND state <> ?", invoiceType, common.InvoiceStateInitialized)
}
query.OrderExpr("id DESC").Limit(100)
err := query.Scan(context.Background())
if err != nil {
return nil, err
}
return invoices, nil
}
func createUsers(svc *service.LndhubService, usersToCreate int) (logins []ExpectedCreateUserResponseBody, tokens []string, err error) {
logins = []ExpectedCreateUserResponseBody{}
tokens = []string{}
for i := 0; i < usersToCreate; i++ {
user, err := svc.CreateUser(context.Background(), "", "")
if err != nil {
return nil, nil, err
}
var login ExpectedCreateUserResponseBody
login.Login = user.Login
login.Password = user.Password
logins = append(logins, login)
token, _, err := svc.GenerateToken(context.Background(), login.Login, login.Password, "")
if err != nil {
return nil, nil, err
}
tokens = append(tokens, token)
}
return logins, tokens, nil
}
type TestSuite struct {
suite.Suite
echo *echo.Echo
}
func checkErrResponse(suite *TestSuite, rec *httptest.ResponseRecorder) *responses.ErrorResponse {
errorResponse := &responses.ErrorResponse{}
assert.Equal(suite.T(), http.StatusBadRequest, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(errorResponse))
return errorResponse
}
func (suite *TestSuite) createAddInvoiceReq(amt int, memo, token string) *ExpectedAddInvoiceResponseBody {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&ExpectedAddInvoiceRequestBody{
Amount: amt,
Memo: memo,
}))
req := httptest.NewRequest(http.MethodPost, "/addinvoice", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
invoiceResponse := &ExpectedAddInvoiceResponseBody{}
assert.Equal(suite.T(), http.StatusOK, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(invoiceResponse))
return invoiceResponse
}
func (suite *TestSuite) createInvoiceReq(amt int, memo, userLogin string) *ExpectedAddInvoiceResponseBody {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&ExpectedAddInvoiceRequestBody{
Amount: amt,
Memo: memo,
}))
req := httptest.NewRequest(http.MethodPost, "/invoice/"+userLogin, &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
suite.echo.ServeHTTP(rec, req)
invoiceResponse := &ExpectedAddInvoiceResponseBody{}
assert.Equal(suite.T(), http.StatusOK, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(invoiceResponse))
return invoiceResponse
}
func (suite *TestSuite) createInvoiceReqError(amt int, memo, userLogin string) *responses.ErrorResponse {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&ExpectedAddInvoiceRequestBody{
Amount: amt,
Memo: memo,
}))
req := httptest.NewRequest(http.MethodPost, "/invoice/"+userLogin, &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
suite.echo.ServeHTTP(rec, req)
return checkErrResponse(suite, rec)
}
func (suite *TestSuite) createKeySendReq(amount int64, memo, destination, token string) *ExpectedKeySendResponseBody {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(ExpectedKeySendRequestBody{
Amount: amount,
Destination: destination,
Memo: memo,
//add memo as WHATSAT_MESSAGE custom record
CustomRecords: map[string]string{fmt.Sprint(service.TLV_WHATSAT_MESSAGE): memo},
}))
req := httptest.NewRequest(http.MethodPost, "/keysend", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
keySendResponse := &ExpectedKeySendResponseBody{}
assert.Equal(suite.T(), http.StatusOK, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(keySendResponse))
return keySendResponse
}
func (suite *TestSuite) createKeySendReqError(amount int64, memo, destination, token string) *responses.ErrorResponse {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(ExpectedKeySendRequestBody{
Amount: amount,
Destination: destination,
Memo: memo,
}))
req := httptest.NewRequest(http.MethodPost, "/keysend", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
return checkErrResponse(suite, rec)
}
func (suite *TestSuite) createPayInvoiceReq(payReq *ExpectedPayInvoiceRequestBody, token string) *ExpectedPayInvoiceResponseBody {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(payReq))
req := httptest.NewRequest(http.MethodPost, "/payinvoice", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
payInvoiceResponse := &ExpectedPayInvoiceResponseBody{}
assert.Equal(suite.T(), http.StatusOK, rec.Code)
assert.NoError(suite.T(), json.NewDecoder(rec.Body).Decode(payInvoiceResponse))
return payInvoiceResponse
}
func (suite *TestSuite) createPayInvoiceReqError(payReq string, token string) *responses.ErrorResponse {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&ExpectedPayInvoiceRequestBody{
Invoice: payReq,
}))
req := httptest.NewRequest(http.MethodPost, "/payinvoice", &buf)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
return checkErrResponse(suite, rec)
}
func (suite *TestSuite) createPayInvoiceReqWithCancel(payReq string, token string) {
rec := httptest.NewRecorder()
var buf bytes.Buffer
assert.NoError(suite.T(), json.NewEncoder(&buf).Encode(&ExpectedPayInvoiceRequestBody{
Invoice: payReq,
}))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req := httptest.NewRequest(http.MethodPost, "/payinvoice", &buf).WithContext(ctx)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
suite.echo.ServeHTTP(rec, req)
}