package controllers import ( "context" "encoding/json" "testing" "github.com/getAlby/go-nostr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/getAlby/hub/constants" "github.com/getAlby/hub/db" "github.com/getAlby/hub/nip47/models" "github.com/getAlby/hub/tests" "github.com/getAlby/hub/transactions" ) const nip47PayInvoiceJson = ` { "method": "pay_invoice", "params": { "invoice": "lntbs1230n1pnkqautdqyw3jsnp4q09a0z84kg4a2m38zjllw43h953fx5zvqe8qxfgw694ymkq26u8zcpp5yvnh6hsnlnj4xnuh2trzlnunx732dv8ta2wjr75pdfxf6p2vlyassp5hyeg97a3ft5u769kjwsn7p0e85h79pzz8kladmnqhpcypz2uawjs9qyysgqcqpcxq8zals8sq9yeg2pa9eywkgj50cyzxd5elatujuc0c0wh6j9nat5mn34pgk8u9ufpgs99tw9ldlfk42cqlkr48au3lmuh09269prg4qkggh4a8cyqpfl0y6j", "metadata": {"a": 123} } } ` const nip47PayInvoiceZeroAmountJson = ` { "method": "pay_invoice", "params": { "invoice": "` + tests.MockZeroAmountInvoice + `", "amount": 1234 } } ` const nip47PayJsonNoInvoice = ` { "method": "pay_invoice", "params": { "something": "else" } } ` const nip47PayJsonExpiredInvoice = ` { "method": "pay_invoice", "params": { "invoice": "lntb1230n1pjypux0pp5xgxzcks5jtx06k784f9dndjh664wc08ucrganpqn52d0ftrh9n8sdqyw3jscqzpgxqyz5vqsp5rkx7cq252p3frx8ytjpzc55rkgyx2mfkzzraa272dqvr2j6leurs9qyyssqhutxa24r5hqxstchz5fxlslawprqjnarjujp5sm3xj7ex73s32sn54fthv2aqlhp76qmvrlvxppx9skd3r5ut5xutgrup8zuc6ay73gqmra29m" } } ` func TestHandlePayInvoiceEvent(t *testing.T) { ctx := context.TODO() svc, err := tests.CreateTestService(t) require.NoError(t, err) defer svc.Remove() app, _, err := tests.CreateApp(svc) assert.NoError(t, err) appPermission := &db.AppPermission{ AppId: app.ID, App: *app, Scope: constants.PAY_INVOICE_SCOPE, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) nip47Request := &models.Request{} err = json.Unmarshal([]byte(nip47PayInvoiceJson), nip47Request) assert.NoError(t, err) dbRequestEvent := &db.RequestEvent{} err = svc.DB.Create(&dbRequestEvent).Error assert.NoError(t, err) var publishedResponse *models.Response publishResponse := func(response *models.Response, tags nostr.Tags) { publishedResponse = response } NewTestNip47Controller(svc). HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{}) assert.Equal(t, "123preimage", publishedResponse.Result.(payResponse).Preimage) transactionType := constants.TRANSACTION_TYPE_OUTGOING transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher) transaction, err := transactionsSvc.LookupTransaction(ctx, "23277d5e13fce5534f9752c62fcf9337a2a6b0ebea9d21fa816a4c9d054cf93b", &transactionType, svc.LNClient, &app.ID) assert.NoError(t, err) type dummyMetadata struct { A int `json:"a"` } var decodedMetadata dummyMetadata err = json.Unmarshal(transaction.Metadata, &decodedMetadata) assert.NoError(t, err) assert.Equal(t, 123, decodedMetadata.A) } func TestHandlePayInvoiceEvent_ZeroAmount(t *testing.T) { ctx := context.TODO() svc, err := tests.CreateTestService(t) require.NoError(t, err) defer svc.Remove() app, _, err := tests.CreateApp(svc) assert.NoError(t, err) appPermission := &db.AppPermission{ AppId: app.ID, App: *app, Scope: constants.PAY_INVOICE_SCOPE, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) nip47Request := &models.Request{} err = json.Unmarshal([]byte(nip47PayInvoiceZeroAmountJson), nip47Request) assert.NoError(t, err) dbRequestEvent := &db.RequestEvent{} err = svc.DB.Create(&dbRequestEvent).Error assert.NoError(t, err) var publishedResponse *models.Response publishResponse := func(response *models.Response, tags nostr.Tags) { publishedResponse = response } NewTestNip47Controller(svc). HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{}) assert.Equal(t, "123preimage", publishedResponse.Result.(payResponse).Preimage) transactionType := constants.TRANSACTION_TYPE_OUTGOING transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher) transaction, err := transactionsSvc.LookupTransaction(ctx, tests.MockZeroAmountPaymentHash, &transactionType, svc.LNClient, &app.ID) assert.NoError(t, err) // from the request amount assert.Equal(t, uint64(1234), transaction.AmountMsat) } func TestHandlePayInvoiceEvent_MalformedInvoice(t *testing.T) { ctx := context.TODO() svc, err := tests.CreateTestService(t) require.NoError(t, err) defer svc.Remove() app, _, err := tests.CreateApp(svc) assert.NoError(t, err) appPermission := &db.AppPermission{ AppId: app.ID, App: *app, Scope: constants.PAY_INVOICE_SCOPE, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) nip47Request := &models.Request{} err = json.Unmarshal([]byte(nip47PayJsonNoInvoice), nip47Request) assert.NoError(t, err) dbRequestEvent := &db.RequestEvent{} err = svc.DB.Create(&dbRequestEvent).Error assert.NoError(t, err) var publishedResponse *models.Response publishResponse := func(response *models.Response, tags nostr.Tags) { publishedResponse = response } NewTestNip47Controller(svc). HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{}) assert.Nil(t, publishedResponse.Result) assert.Equal(t, constants.ERROR_BAD_REQUEST, publishedResponse.Error.Code) assert.Equal(t, "Failed to decode bolt11 invoice: bolt11 too short", publishedResponse.Error.Message) } func TestHandlePayInvoiceEvent_ExpiredInvoice(t *testing.T) { ctx := context.TODO() svc, err := tests.CreateTestService(t) require.NoError(t, err) defer svc.Remove() app, _, err := tests.CreateApp(svc) assert.NoError(t, err) appPermission := &db.AppPermission{ AppId: app.ID, App: *app, Scope: constants.PAY_INVOICE_SCOPE, } err = svc.DB.Create(appPermission).Error assert.NoError(t, err) nip47Request := &models.Request{} err = json.Unmarshal([]byte(nip47PayJsonExpiredInvoice), nip47Request) assert.NoError(t, err) dbRequestEvent := &db.RequestEvent{} err = svc.DB.Create(&dbRequestEvent).Error assert.NoError(t, err) var publishedResponse *models.Response publishResponse := func(response *models.Response, tags nostr.Tags) { publishedResponse = response } NewTestNip47Controller(svc). HandlePayInvoiceEvent(ctx, nip47Request, dbRequestEvent.ID, app, publishResponse, nostr.Tags{}) assert.Nil(t, publishedResponse.Result) assert.Equal(t, constants.ERROR_INTERNAL, publishedResponse.Error.Code) assert.Equal(t, "this invoice has expired", publishedResponse.Error.Message) }