pay_invoice_controller_test.go raw

   1  package controllers
   2  
   3  import (
   4  	"context"
   5  	"encoding/json"
   6  	"testing"
   7  
   8  	"github.com/getAlby/go-nostr"
   9  	"github.com/stretchr/testify/assert"
  10  	"github.com/stretchr/testify/require"
  11  
  12  	"github.com/getAlby/hub/constants"
  13  	"github.com/getAlby/hub/db"
  14  	"github.com/getAlby/hub/nip47/models"
  15  	"github.com/getAlby/hub/tests"
  16  	"github.com/getAlby/hub/transactions"
  17  )
  18  
  19  const nip47PayInvoiceJson = `
  20  {
  21  	"method": "pay_invoice",
  22  	"params": {
  23  		"invoice": "lntbs1230n1pnkqautdqyw3jsnp4q09a0z84kg4a2m38zjllw43h953fx5zvqe8qxfgw694ymkq26u8zcpp5yvnh6hsnlnj4xnuh2trzlnunx732dv8ta2wjr75pdfxf6p2vlyassp5hyeg97a3ft5u769kjwsn7p0e85h79pzz8kladmnqhpcypz2uawjs9qyysgqcqpcxq8zals8sq9yeg2pa9eywkgj50cyzxd5elatujuc0c0wh6j9nat5mn34pgk8u9ufpgs99tw9ldlfk42cqlkr48au3lmuh09269prg4qkggh4a8cyqpfl0y6j",
  24  		"metadata": {"a": 123}
  25  	}
  26  }
  27  `
  28  const nip47PayInvoiceZeroAmountJson = `
  29  {
  30  	"method": "pay_invoice",
  31  	"params": {
  32  		"invoice": "` + tests.MockZeroAmountInvoice + `",
  33  		"amount": 1234
  34  	}
  35  }
  36  `
  37  
  38  const nip47PayJsonNoInvoice = `
  39  {
  40  	"method": "pay_invoice",
  41  	"params": {
  42  		"something": "else"
  43  	}
  44  }
  45  `
  46  
  47  const nip47PayJsonExpiredInvoice = `
  48  {
  49  	"method": "pay_invoice",
  50  	"params": {
  51  		"invoice": "lntb1230n1pjypux0pp5xgxzcks5jtx06k784f9dndjh664wc08ucrganpqn52d0ftrh9n8sdqyw3jscqzpgxqyz5vqsp5rkx7cq252p3frx8ytjpzc55rkgyx2mfkzzraa272dqvr2j6leurs9qyyssqhutxa24r5hqxstchz5fxlslawprqjnarjujp5sm3xj7ex73s32sn54fthv2aqlhp76qmvrlvxppx9skd3r5ut5xutgrup8zuc6ay73gqmra29m"
  52  	}
  53  }
  54  `
  55  
  56  func TestHandlePayInvoiceEvent(t *testing.T) {
  57  	ctx := context.TODO()
  58  	svc, err := tests.CreateTestService(t)
  59  	require.NoError(t, err)
  60  	defer svc.Remove()
  61  
  62  	app, _, err := tests.CreateApp(svc)
  63  	assert.NoError(t, err)
  64  
  65  	appPermission := &db.AppPermission{
  66  		AppId: app.ID,
  67  		App:   *app,
  68  		Scope: constants.PAY_INVOICE_SCOPE,
  69  	}
  70  	err = svc.DB.Create(appPermission).Error
  71  	assert.NoError(t, err)
  72  
  73  	nip47Request := &models.Request{}
  74  	err = json.Unmarshal([]byte(nip47PayInvoiceJson), nip47Request)
  75  	assert.NoError(t, err)
  76  
  77  	dbRequestEvent := &db.RequestEvent{}
  78  	err = svc.DB.Create(&dbRequestEvent).Error
  79  	assert.NoError(t, err)
  80  
  81  	var publishedResponse *models.Response
  82  
  83  	publishResponse := func(response *models.Response, tags nostr.Tags) {
  84  		publishedResponse = response
  85  	}
  86  
  87  	NewTestNip47Controller(svc).
  88  		HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
  89  
  90  	assert.Equal(t, "123preimage", publishedResponse.Result.(payResponse).Preimage)
  91  
  92  	transactionType := constants.TRANSACTION_TYPE_OUTGOING
  93  	transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
  94  	transaction, err := transactionsSvc.LookupTransaction(ctx, "23277d5e13fce5534f9752c62fcf9337a2a6b0ebea9d21fa816a4c9d054cf93b", &transactionType, svc.LNClient, &app.ID)
  95  	assert.NoError(t, err)
  96  
  97  	type dummyMetadata struct {
  98  		A int `json:"a"`
  99  	}
 100  	var decodedMetadata dummyMetadata
 101  	err = json.Unmarshal(transaction.Metadata, &decodedMetadata)
 102  	assert.NoError(t, err)
 103  	assert.Equal(t, 123, decodedMetadata.A)
 104  }
 105  
 106  func TestHandlePayInvoiceEvent_ZeroAmount(t *testing.T) {
 107  	ctx := context.TODO()
 108  	svc, err := tests.CreateTestService(t)
 109  	require.NoError(t, err)
 110  	defer svc.Remove()
 111  
 112  	app, _, err := tests.CreateApp(svc)
 113  	assert.NoError(t, err)
 114  
 115  	appPermission := &db.AppPermission{
 116  		AppId: app.ID,
 117  		App:   *app,
 118  		Scope: constants.PAY_INVOICE_SCOPE,
 119  	}
 120  	err = svc.DB.Create(appPermission).Error
 121  	assert.NoError(t, err)
 122  
 123  	nip47Request := &models.Request{}
 124  	err = json.Unmarshal([]byte(nip47PayInvoiceZeroAmountJson), nip47Request)
 125  	assert.NoError(t, err)
 126  
 127  	dbRequestEvent := &db.RequestEvent{}
 128  	err = svc.DB.Create(&dbRequestEvent).Error
 129  	assert.NoError(t, err)
 130  
 131  	var publishedResponse *models.Response
 132  
 133  	publishResponse := func(response *models.Response, tags nostr.Tags) {
 134  		publishedResponse = response
 135  	}
 136  
 137  	NewTestNip47Controller(svc).
 138  		HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
 139  
 140  	assert.Equal(t, "123preimage", publishedResponse.Result.(payResponse).Preimage)
 141  
 142  	transactionType := constants.TRANSACTION_TYPE_OUTGOING
 143  	transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
 144  	transaction, err := transactionsSvc.LookupTransaction(ctx, tests.MockZeroAmountPaymentHash, &transactionType, svc.LNClient, &app.ID)
 145  	assert.NoError(t, err)
 146  	// from the request amount
 147  	assert.Equal(t, uint64(1234), transaction.AmountMsat)
 148  }
 149  
 150  func TestHandlePayInvoiceEvent_MalformedInvoice(t *testing.T) {
 151  	ctx := context.TODO()
 152  	svc, err := tests.CreateTestService(t)
 153  	require.NoError(t, err)
 154  	defer svc.Remove()
 155  
 156  	app, _, err := tests.CreateApp(svc)
 157  	assert.NoError(t, err)
 158  
 159  	appPermission := &db.AppPermission{
 160  		AppId: app.ID,
 161  		App:   *app,
 162  		Scope: constants.PAY_INVOICE_SCOPE,
 163  	}
 164  	err = svc.DB.Create(appPermission).Error
 165  	assert.NoError(t, err)
 166  
 167  	nip47Request := &models.Request{}
 168  	err = json.Unmarshal([]byte(nip47PayJsonNoInvoice), nip47Request)
 169  	assert.NoError(t, err)
 170  
 171  	dbRequestEvent := &db.RequestEvent{}
 172  	err = svc.DB.Create(&dbRequestEvent).Error
 173  	assert.NoError(t, err)
 174  
 175  	var publishedResponse *models.Response
 176  
 177  	publishResponse := func(response *models.Response, tags nostr.Tags) {
 178  		publishedResponse = response
 179  	}
 180  
 181  	NewTestNip47Controller(svc).
 182  		HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
 183  
 184  	assert.Nil(t, publishedResponse.Result)
 185  	assert.Equal(t, constants.ERROR_BAD_REQUEST, publishedResponse.Error.Code)
 186  	assert.Equal(t, "Failed to decode bolt11 invoice: bolt11 too short", publishedResponse.Error.Message)
 187  }
 188  
 189  func TestHandlePayInvoiceEvent_ExpiredInvoice(t *testing.T) {
 190  	ctx := context.TODO()
 191  	svc, err := tests.CreateTestService(t)
 192  	require.NoError(t, err)
 193  	defer svc.Remove()
 194  
 195  	app, _, err := tests.CreateApp(svc)
 196  	assert.NoError(t, err)
 197  
 198  	appPermission := &db.AppPermission{
 199  		AppId: app.ID,
 200  		App:   *app,
 201  		Scope: constants.PAY_INVOICE_SCOPE,
 202  	}
 203  	err = svc.DB.Create(appPermission).Error
 204  	assert.NoError(t, err)
 205  
 206  	nip47Request := &models.Request{}
 207  	err = json.Unmarshal([]byte(nip47PayJsonExpiredInvoice), nip47Request)
 208  	assert.NoError(t, err)
 209  
 210  	dbRequestEvent := &db.RequestEvent{}
 211  	err = svc.DB.Create(&dbRequestEvent).Error
 212  	assert.NoError(t, err)
 213  
 214  	var publishedResponse *models.Response
 215  
 216  	publishResponse := func(response *models.Response, tags nostr.Tags) {
 217  		publishedResponse = response
 218  	}
 219  
 220  	NewTestNip47Controller(svc).
 221  		HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{})
 222  
 223  	assert.Nil(t, publishedResponse.Result)
 224  	assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code)
 225  	assert.Equal(t, "this invoice has expired", publishedResponse.Error.Message)
 226  }
 227