diff --git a/access_keys_test.go b/access_keys_test.go new file mode 100644 index 0000000..a5f109c --- /dev/null +++ b/access_keys_test.go @@ -0,0 +1,77 @@ +package buxclient + +import ( + "context" + "testing" + + buxmodels "github.com/BuxOrg/bux-models" + "github.com/stretchr/testify/assert" + + "github.com/BuxOrg/go-buxclient/fixtures" +) + +// TestAccessKeys will test the AccessKey methods +func TestAccessKeys(t *testing.T) { + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/access-key", + Result: fixtures.MarshallForTestHandler(fixtures.AccessKey), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + + t.Run("GetAccessKey", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, true) + + // when + accessKey, err := client.GetAccessKey(context.Background(), fixtures.AccessKey.ID) + + // then + assert.NoError(t, err) + assert.Equal(t, accessKey, fixtures.AccessKey) + }) + + t.Run("GetAccessKeys", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/access-key/search", + Result: fixtures.MarshallForTestHandler([]*buxmodels.AccessKey{fixtures.AccessKey}), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, true) + + // when + accessKeys, err := client.GetAccessKeys(context.Background(), fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, accessKeys, []*buxmodels.AccessKey{fixtures.AccessKey}) + }) + + t.Run("CreateAccessKey", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, true) + + // when + accessKey, err := client.CreateAccessKey(context.Background(), fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, accessKey, fixtures.AccessKey) + }) + + t.Run("RevokeAccessKey", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, true) + + // when + accessKey, err := client.RevokeAccessKey(context.Background(), fixtures.AccessKey.ID) + + // then + assert.NoError(t, err) + assert.Equal(t, accessKey, fixtures.AccessKey) + }) +} diff --git a/buxclient_test.go b/buxclient_test.go index 7fca5c9..2b81cdf 100644 --- a/buxclient_test.go +++ b/buxclient_test.go @@ -2,7 +2,6 @@ package buxclient import ( "context" - "encoding/json" "io" "net/http" "net/http/httptest" @@ -10,32 +9,13 @@ import ( buxmodels "github.com/BuxOrg/bux-models" "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/libsv/go-bt/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/BuxOrg/go-buxclient/fixtures" "github.com/BuxOrg/go-buxclient/transports" ) -const ( - accessKeyString = `7779d24ca6f8821f225042bf55e8f80aa41b08b879b72827f51e41e6523b9cd0` - adminKeyXpub = "xprv9s21ZrQH143K4Z8JnrQ7XsYxzKbFNsAEPyHMaMU2fbMtoY1YmsJLFo3XBkg2m7e9UJLS6xvd2HjZ5WN9fQbMSGU7uXEE2pksvbQYCXswLB5" - destinationJSON = `{"id":"90d10acb85f37dd009238fe7ec61a1411725825c82099bd8432fcb47ad8326ce","xpub_id":"9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36","locking_script":"76a9140e0eb4911d79e9b7683f268964f595b66fa3604588ac","type":"pubkeyhash","chain":0,"num":245,"address":"12HL5RyEy3Rt6SCwxgpiFSTigem1Pzbq22","metadata":{"test":"test value"}}}` - draftTxJSON = `{"created_at":"2022-02-09T16:28:39.000639Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":null,"id":"fe6fe12c25b81106b7332d58fe87dab7bc6e56c8c21ca45b4de05f673f3f653c","hex":"010000000141e3be4d5a3f25e11157bfdd100e7c3497b9be2b80b57eb55e5376b075e7dc5d0200000000ffffffff02e8030000000000001976a9147ff514e6ae3deb46e6644caac5cdd0bf2388906588ac170e0000000000001976a9143dbdb346aaf1c3dc501a2f8c186c3d3e8a87764588ac00000000","xpub_id":"9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36","expires_at":"2022-02-09T16:29:08.991801Z","metadata":{"testkey":"test-value"},"configuration":{"change_destinations":[{"created_at":"2022-02-09T16:28:38.997313Z","updated_at":"0001-01-01T00:00:00Z","deleted_at":null,"id":"252e8a915a5f05effab827a887e261a2416a76f3d3aada946a70a575c0bb76a7","xpub_id":"9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36","locking_script":"76a9143dbdb346aaf1c3dc501a2f8c186c3d3e8a87764588ac","type":"pubkeyhash","chain":1,"num":100,"address":"16dTUJwi7qT3JqzAUMcDHaVV3sB4fH85Ep","draft_id":"fe6fe12c25b81106b7332d58fe87dab7bc6e56c8c21ca45b4de05f673f3f653c"}],"change_destinations_strategy":"","change_minimum_satoshis":0,"change_number_of_destinations":0,"change_satoshis":3607,"expires_in":0,"fee":97,"fee_unit":{"satoshis":1,"bytes":2},"from_utxos":null,"inputs":[{"created_at":"2022-01-28T13:45:02.352Z","updated_at":"2022-02-09T16:28:38.993207Z","deleted_at":null,"id":"efe383eea1a6f7925afb2621b69ea9ba6bd0623e8d61827bad994f8be85161fc","transaction_id":"5ddce775b076535eb57eb5802bbeb997347c0e10ddbf5711e1253f5a4dbee341","xpub_id":"9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36","output_index":2,"satoshis":4704,"script_pub_key":"76a914c746bf0f295375cbea4a5ef25b36c84ff9801bac88ac","type":"pubkeyhash","draft_id":"fe6fe12c25b81106b7332d58fe87dab7bc6e56c8c21ca45b4de05f673f3f653c","reserved_at":"2022-02-09T16:28:38.993205Z","spending_tx_id":null,"destination":{"created_at":"2022-01-28T13:45:02.324Z","updated_at":"0001-01-01T00:00:00Z","metadata":{"client_id":"8","run":90,"run_id":"3108aa426fc7102488bb0ffd","xbench":"destination for testing"},"deleted_at":null,"id":"b8bfa56e37c90f1b25df2e571f727cfec80dd17c5d1845c4b93e21034f7f6a0b","xpub_id":"9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36","locking_script":"76a914c746bf0f295375cbea4a5ef25b36c84ff9801bac88ac","type":"pubkeyhash","chain":0,"num":212,"address":"1KAgDiUasnC7roCjQZM1XLJUpq4BYHjdp6","draft_id":""}}],"miner":"","outputs":[{"satoshis":1000,"scripts":[{"address":"1CfaQw9udYNPccssFJFZ94DN8MqNZm9nGt","satoshis":1000,"script":"76a9147ff514e6ae3deb46e6644caac5cdd0bf2388906588ac","script_type":"pubkeyhash"}],"to":"1CfaQw9udYNPccssFJFZ94DN8MqNZm9nGt","op_return":null},{"satoshis":3607,"scripts":[{"address":"16dTUJwi7qT3JqzAUMcDHaVV3sB4fH85Ep","satoshis":3607,"script":"76a9143dbdb346aaf1c3dc501a2f8c186c3d3e8a87764588ac","script_type":""}],"to":"16dTUJwi7qT3JqzAUMcDHaVV3sB4fH85Ep","op_return":null}],"send_all_to":{},"sync":null},"status":"draft"}` - requestTypeHTTP = "http" - serverURL = "https://example.com/" - testAddress = "1CfaQw9udYNPccssFJFZ94DN8MqNZm9nGt" - testAddress2 = "1PnRDRF517hhrFJ5VvR7QGcpQhc7qRshFA" - transactionJSON = `{"id":"041479f86c475603fd510431cf702bc8c9849a9c350390eb86b467d82a13cc24","created_at":"2022-01-28T13:45:01.711Z","updated_at":null,"deleted_at":null,"hex":"0100000004afcafa163824904aa3bbc403b30db56a08f29ffa53b16b1b4b4914b9bd7d7610010000006a4730440220710c2b2fe5a0ece2cbc962635d0fb6dabf95c94db0b125c3e2613cede9738666022067e9cc0f4f706c3a2781990981a50313fb0aad18c1e19a757125eec2408ecadb412103dcd8d28545c9f80af54648fcca87972d89e3e7ed7b482465dd78b62c784ad533ffffffff783452c4038c46a4d68145d829f09c70755edd8d4b3512d7d6a27db08a92a76b000000006b483045022100ee7e24859274013e748090a022bf51200ab216771b5d0d57c0d074843dfa62bd02203933c2bd2880c2f8257befff44dc19cb1f3760c6eea44fc0f8094ff94bce652a41210375680e36c45658bd9b0694a48f5756298cf95b77f50bada14ef1cba6d7ea1d3affffffff25e893beb8240ede7661c02cb959799d364711ba638eccdf12e3ce60faa2fd0f010000006b483045022100fc380099ac7f41329aaeed364b95baa390be616243b80a8ef444ae0ddc76fa3a0220644a9677d40281827fa4602269720a5a453fbe77409be40293c3f8248534e5f8412102398146eff37de36ed608b2ee917a3d4b4a424722f9a00f1b48c183322a8ef2a1ffffffff00e6f915a5a3678f01229e5c320c64755f242be6cebfac54e2f77ec5e0eec581000000006b483045022100951511f81291ac234926c866f777fe8e77bc00661031675978ddecf159cc265902207a5957dac7c89493e2b7df28741ce3291e19dc8bba4b13082c69d0f2b79c70ab4121031d674b3ad42b28f3a445e9970bd9ae8fe5d3fb89ee32452d9f6dc7916ea184bfffffffff04c7110000000000001976a91483615db3fb9b9cbbf4cd407100833511a1cb278588ac30060000000000001976a914296a5295e70697e844fb4c2113b41a501d41452e88ac96040000000000001976a914e73e21935fc48df0d1cf8b73f2e8bbd23b78244a88ac27020000000000001976a9140b2b03751813e3467a28ce916cbb102d84c6eec588ac00000000","block_hash":"","block_height":0,"fee":354,"number_of_inputs":4,"number_of_outputs":4,"total_value":6955,"metadata":{"client_id":"8","run":76,"run_id":"3108aa426fc7102488bb0ffd","xbench":"is awesome"},"output_value":1725,"direction":"incoming"}` - transactionsJSON = `[{"id":"caae6e799210dfea7591e3d55455437eb7e1091bb01463ae1e7ddf9e29c75eda","created_at":"2022-01-28T13:44:59.376Z","updated_at":null,"deleted_at":null,"hex":"0100000001cf4faa628ce1abdd2cfc641c948898bb7a3dbe043999236c3ea4436a0c79f5dc000000006a47304402206aeca14175e4477031970c1cda0af4d9d1206289212019b54f8e1c9272b5bac2022067c4d32086146ca77640f02a989f51b3c6738ebfa24683c4a923f647cf7f1c624121036295a81525ba33e22c6497c0b758e6a84b60d97c2d8905aa603dd364915c3a0effffffff023e030000000000001976a914f7fc6e0b05e91c3610efd0ce3f04f6502e2ed93d88ac99030000000000001976a914550e06a3aa71ba7414b53922c13f96a882bf027988ac00000000","block_hash":"","block_height":0,"fee":97,"number_of_inputs":1,"number_of_outputs":2,"total_value":733,"metadata":{"client_id":"8","run":14,"run_id":"3108aa426fc7102488bb0ffd","xbench":"is awesome"},"output_value":921,"direction":"incoming"},{"id":"5f4fd2be162769852e8bd1362bb8d815a89e137707b4985249876a7f0ebbb071","created_at":"2022-01-28T13:44:59.996Z","updated_at":null,"deleted_at":null,"hex":"01000000016c0c005d516ccd1f1029fa5b61be51a0feaee6e2b07804ceba71047e06edb2df000000006b483045022100ab020464941452dff13bf4ff40a6218825b8dc3502d7860857ee0dd9407e490402206325d24bd46c09b246ebe8493257f2b91d4157de58adfdedf42ba72d6de9aaf5412103a06808b0c597ee6c572baf4f167166e9fed4b8ca66d651d2345b12e0ae5344b3ffffffff0208020000000000001976a914c3367acfc659588393c68dae3eb435c5d0a088b988ac46120000000000001976a91492fc673e0630962068c8b7d909fbfeeb77e3ea3288ac00000000","block_hash":"","block_height":0,"fee":97,"number_of_inputs":1,"number_of_outputs":2,"total_value":423,"metadata":{"client_id":"8","run":32,"run_id":"3108aa426fc7102488bb0ffd","xbench":"is awesome"},"output_value":4678,"direction":"incoming"}]` - txID = "041479f86c475603fd510431cf702bc8c9849a9c350390eb86b467d82a13cc24" - xPrivString = "xprv9s21ZrQH143K3N6qVJQAu4EP51qMcyrKYJLkLgmYXgz58xmVxVLSsbx2DfJUtjcnXK8NdvkHMKfmmg5AJT2nqqRWUrjSHX29qEJwBgBPkJQ" - xPubID = "9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36" - xpubJSON = `{"data":{"xpub":{"id":"0092de4d2aafa59a71a1f90342c138e1c4f19cd1b10e2d17422b34a1d06733e0"}}}` - xPubJSON = `{"id":"0092de4d2aafa59a71a1f90342c138e1c4f19cd1b10e2d17422b34a1d06733e0"}` - xPubString = "xpub661MyMwAqRbcFrBJbKwBGCB7d3fr2SaAuXGM95BA62X41m6eW2ehRQGW4xLi9wkEXUGnQZYxVVj4PxXnyrLk7jdqvBAs1Qq9gf6ykMvjR7J" -) - // localRoundTripper is an http.RoundTripper that executes HTTP transactions // by using handler directly, instead of going over an HTTP connection. type localRoundTripper struct { @@ -85,10 +65,18 @@ func TestNewBuxClient(t *testing.T) { assert.Nil(t, client) }) + t.Run("invalid xpriv", func(t *testing.T) { + client, err := New( + WithXPriv("invalid-xpriv"), + ) + assert.Error(t, err) + assert.Nil(t, client) + }) + t.Run("valid client", func(t *testing.T) { client, err := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) require.NoError(t, err) assert.IsType(t, BuxClient{}, *client) @@ -96,27 +84,45 @@ func TestNewBuxClient(t *testing.T) { t.Run("valid xPub client", func(t *testing.T) { client, err := New( - WithXPub(xPubString), - WithHTTP(serverURL), + WithXPub(fixtures.XPubString), + WithHTTP(fixtures.ServerURL), ) require.NoError(t, err) assert.IsType(t, BuxClient{}, *client) }) + t.Run("invalid xPub client", func(t *testing.T) { + client, err := New( + WithXPub("invalid-xpub"), + WithHTTP(fixtures.ServerURL), + ) + assert.Error(t, err) + assert.Nil(t, client) + }) + t.Run("valid access keys", func(t *testing.T) { client, err := New( - WithAccessKey(accessKeyString), - WithHTTP(serverURL), + WithAccessKey(fixtures.AccessKeyString), + WithHTTP(fixtures.ServerURL), ) require.NoError(t, err) assert.IsType(t, BuxClient{}, *client) }) + t.Run("invalid access keys", func(t *testing.T) { + client, err := New( + WithAccessKey("invalid-access-key"), + WithHTTP(fixtures.ServerURL), + ) + assert.Error(t, err) + assert.Nil(t, client) + }) + t.Run("valid access key WIF", func(t *testing.T) { - wifKey, _ := bitcoin.PrivateKeyToWif(accessKeyString) + wifKey, _ := bitcoin.PrivateKeyToWif(fixtures.AccessKeyString) client, err := New( WithAccessKey(wifKey.String()), - WithHTTP(serverURL), + WithHTTP(fixtures.ServerURL), ) require.NoError(t, err) assert.IsType(t, BuxClient{}, *client) @@ -127,8 +133,8 @@ func TestNewBuxClient(t *testing.T) { func TestSetAdminKey(t *testing.T) { t.Run("invalid", func(t *testing.T) { client, _ := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) err := client.SetAdminKey("") assert.Error(t, err) @@ -136,27 +142,27 @@ func TestSetAdminKey(t *testing.T) { t.Run("valid", func(t *testing.T) { client, _ := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) - err := client.SetAdminKey(xPrivString) + err := client.SetAdminKey(fixtures.XPrivString) assert.NoError(t, err) }) t.Run("invalid with", func(t *testing.T) { _, err := New( - WithXPriv(xPrivString), + WithXPriv(fixtures.XPrivString), WithAdminKey("rest"), - WithHTTP(serverURL), + WithHTTP(fixtures.ServerURL), ) assert.Error(t, err) }) t.Run("valid with", func(t *testing.T) { _, err := New( - WithXPriv(xPrivString), - WithAdminKey(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithAdminKey(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) assert.NoError(t, err) }) @@ -166,8 +172,8 @@ func TestSetAdminKey(t *testing.T) { func TestSetSignRequest(t *testing.T) { t.Run("true", func(t *testing.T) { client, _ := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) client.SetSignRequest(true) assert.True(t, client.IsSignRequest()) @@ -175,291 +181,29 @@ func TestSetSignRequest(t *testing.T) { t.Run("false", func(t *testing.T) { client, _ := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) client.SetSignRequest(false) assert.False(t, client.IsSignRequest()) }) - t.Run("false", func(t *testing.T) { - _, err := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), - ) - require.NoError(t, err) - }) -} - -// TestDraftTransaction will test the DraftTransaction method -func TestDraftTransaction(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/transaction", - Result: draftTxJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("draft transaction", func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - config := &buxmodels.TransactionConfig{ - Outputs: []*buxmodels.TransactionOutput{{ - Satoshis: 1000, - To: testAddress, - }}, - } - metadata := &buxmodels.Metadata{ - "test-key": "test-value", - } - - draft, err := client.DraftTransaction(context.Background(), config, metadata) - assert.NoError(t, err) - checkDraftTransactionOutput(t, draft) - }) -} - -// TestNewXpub will test the NewXpub method -func TestNewXpub(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/xpub", - Result: xpubJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("NewXpub", func(t *testing.T) { - client := getTestBuxClient(transportHandler, true) - metadata := &buxmodels.Metadata{ - "test-key": "test-value", - } - err := client.NewXpub(context.Background(), xPubString, metadata) - assert.NoError(t, err) - }) -} - -// TestDraftToRecipients will test the DraftToRecipients method -func TestDraftToRecipients(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/transaction", - Result: draftTxJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("DraftToRecipients "+transportHandler.Type, func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - - recipients := []*transports.Recipients{{ - Satoshis: 1000, - To: testAddress, - }} - metadata := &buxmodels.Metadata{ - "test-key": "test-value", - } - - draft, err := client.DraftToRecipients(context.Background(), recipients, metadata) - assert.NoError(t, err) - checkDraftTransactionOutput(t, draft) - }) -} - -func checkDraftTransactionOutput(t *testing.T, draft *buxmodels.DraftTransaction) { - assert.IsType(t, buxmodels.DraftTransaction{}, *draft) - assert.Equal(t, xPubID, draft.XpubID) - assert.Equal(t, buxmodels.DraftStatusDraft, draft.Status) - assert.Len(t, draft.Configuration.Inputs, 1) - assert.Len(t, draft.Configuration.Outputs, 2) - assert.Equal(t, uint64(1000), draft.Configuration.Outputs[0].Satoshis) -} - -// TestNewDestination will test the NewDestination method -func TestNewDestination(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/destination", - Result: destinationJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("new destination"+transportHandler.Type, func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - - destination, err := client.NewDestination(context.Background(), nil) - assert.NoError(t, err) - assert.IsType(t, buxmodels.Destination{}, *destination) - assert.Equal(t, "90d10acb85f37dd009238fe7ec61a1411725825c82099bd8432fcb47ad8326ce", destination.ID) - assert.Equal(t, xPubID, destination.XpubID) - assert.Equal(t, "76a9140e0eb4911d79e9b7683f268964f595b66fa3604588ac", destination.LockingScript) - assert.Equal(t, uint32(0), destination.Chain) - assert.Equal(t, uint32(245), destination.Num) - assert.Equal(t, "12HL5RyEy3Rt6SCwxgpiFSTigem1Pzbq22", destination.Address) - }) -} - -// TestGetTransaction will test the GetTransaction method -func TestGetTransaction(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/transaction", - Result: transactionJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("get transaction", func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - - transaction, err := client.GetTransaction(context.Background(), txID) - assert.NoError(t, err) - assert.IsType(t, buxmodels.Transaction{}, *transaction) - assert.Equal(t, txID, transaction.ID) - assert.Equal(t, uint64(354), transaction.Fee) - assert.Equal(t, uint32(4), transaction.NumberOfInputs) - assert.Equal(t, uint32(4), transaction.NumberOfOutputs) - assert.Equal(t, uint64(6955), transaction.TotalValue) - }) -} - -// TestGetTransactions will test the GetTransactions method -func TestGetTransactions(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/transaction/search", - Result: transactionsJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("get transactions "+transportHandler.Type, func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - - conditions := map[string]interface{}{ - "fee": map[string]interface{}{ - "$lt": 100, - }, - "total_value": map[string]interface{}{ - "$lt": 740, - }, - } - metadata := &buxmodels.Metadata{ - "run_id": "3108aa426fc7102488bb0ffd", - } - transactions, err := client.GetTransactions(context.Background(), conditions, metadata, &transports.QueryParams{}) - assert.NoError(t, err) - assert.IsType(t, []*buxmodels.Transaction{}, transactions) - assert.Len(t, transactions, 2) - assert.Equal(t, "caae6e799210dfea7591e3d55455437eb7e1091bb01463ae1e7ddf9e29c75eda", transactions[0].ID) - assert.Equal(t, uint64(97), transactions[0].Fee) - assert.Equal(t, uint64(733), transactions[0].TotalValue) - assert.Equal(t, "5f4fd2be162769852e8bd1362bb8d815a89e137707b4985249876a7f0ebbb071", transactions[1].ID) - assert.Equal(t, uint64(97), transactions[1].Fee) - assert.Equal(t, uint64(423), transactions[1].TotalValue) - }) -} - -// TestRecordTransaction will test the RecordTransaction method -func TestRecordTransaction(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Path: "/transaction/record", - Result: transactionJSON, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("record transaction"+transportHandler.Type, func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - - hex := "" - metadata := &buxmodels.Metadata{ - "test-key": "test-value", - } - transaction, err := client.RecordTransaction(context.Background(), hex, "", metadata) - assert.NoError(t, err) - assert.IsType(t, buxmodels.Transaction{}, *transaction) - assert.Equal(t, txID, transaction.ID) - }) -} - -// TestSendToRecipients will test the SendToRecipients method -func TestSendToRecipients(t *testing.T) { - transportHandler := testTransportHandler{ - Type: requestTypeHTTP, - Queries: []*testTransportHandlerRequest{{ - Path: "/transaction", - Result: func(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "application/json") - mustWrite(w, draftTxJSON) - }, - }, { - Path: "/transaction/record", - Result: func(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "application/json") - mustWrite(w, transactionJSON) - }, - }}, - ClientURL: serverURL, - Client: WithHTTPClient, - } - - t.Run("send to recipients", func(t *testing.T) { - client := getTestBuxClient(transportHandler, false) - - recipients := []*transports.Recipients{{ - To: testAddress, - Satoshis: 1234, - }, { - To: testAddress2, - Satoshis: 4321, - }} - metadata := &buxmodels.Metadata{ - "test-key": "test-value", - } - transaction, err := client.SendToRecipients(context.Background(), recipients, metadata) - require.NoError(t, err) - assert.IsType(t, buxmodels.Transaction{}, *transaction) - assert.Equal(t, txID, transaction.ID) - }) -} - -// TestFinalizeTransaction will test the FinalizeTransaction method -func TestFinalizeTransaction(t *testing.T) { - t.Run("finalize transaction", func(t *testing.T) { - httpclient := &http.Client{Transport: localRoundTripper{handler: http.NewServeMux()}} + t.Run("false by default", func(t *testing.T) { client, err := New( - WithXPriv(xPrivString), - WithHTTPClient(serverURL, httpclient), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) require.NoError(t, err) - - var draft *buxmodels.DraftTransaction - err = json.Unmarshal([]byte(draftTxJSON), &draft) - require.NoError(t, err) - - var draftHex string - draftHex, err = client.FinalizeTransaction(draft) - require.NoError(t, err) - - var txDraft *bt.Tx - txDraft, err = bt.NewTxFromString(draftHex) - require.NoError(t, err) - assert.Len(t, txDraft.Inputs, 1) - // assert.Len(t, txDraft.GetInputs(), 1) - assert.Len(t, txDraft.Outputs, 2) - // todo check the signature + assert.False(t, client.IsSignRequest()) }) } // TestGetTransport will test the GetTransport method func TestGetTransport(t *testing.T) { - t.Run(requestTypeHTTP, func(t *testing.T) { + t.Run("GetTransport", func(t *testing.T) { client, _ := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), ) transport := client.GetTransport() assert.IsType(t, &transports.TransportHTTP{}, *transport) @@ -467,9 +211,9 @@ func TestGetTransport(t *testing.T) { t.Run("client GetTransport", func(t *testing.T) { client, _ := New( - WithXPriv(xPrivString), - WithHTTP(serverURL), - WithAdminKey(xPrivString), + WithXPriv(fixtures.XPrivString), + WithHTTP(fixtures.ServerURL), + WithAdminKey(fixtures.XPrivString), WithSignRequest(false), ) transport := client.GetTransport() @@ -527,9 +271,11 @@ func TestAuthenticationWithOnlyAccessKey(t *testing.T) { }, }, { - caseTitle: "GetTransaction", - path: "/transaction", - clientMethod: func(c *BuxClient) (any, error) { return c.GetTransaction(context.Background(), txID) }, + caseTitle: "GetTransaction", + path: "/transaction", + clientMethod: func(c *BuxClient) (any, error) { + return c.GetTransaction(context.Background(), fixtures.Transaction.ID) + }, }, { caseTitle: "GetTransactions", @@ -541,22 +287,22 @@ func TestAuthenticationWithOnlyAccessKey(t *testing.T) { } for _, test := range testCases { - t.Run("httpClient."+test.caseTitle, func(t *testing.T) { + t.Run(test.caseTitle, func(t *testing.T) { transportHandler := testTransportHandler{ - Type: requestTypeHTTP, + Type: fixtures.RequestType, Queries: []*testTransportHandlerRequest{{ - Path: "/transaction", + Path: test.path, Result: func(w http.ResponseWriter, req *http.Request) { assertAuthHeaders(t, req) w.Header().Set("Content-Type", "application/json") mustWrite(w, "{}") }, }}, - ClientURL: serverURL, + ClientURL: fixtures.ServerURL, Client: WithHTTPClient, } - client := getTestBuxClientWithOpts(transportHandler, WithAccessKey(accessKeyString)) + client := getTestBuxClientWithOpts(transportHandler, WithAccessKey(fixtures.AccessKeyString)) _, err := test.clientMethod(client) if err != nil { @@ -577,10 +323,10 @@ func assertAuthHeaders(t *testing.T, req *http.Request) { func getTestBuxClient(transportHandler testTransportHandler, adminKey bool) *BuxClient { opts := []ClientOps{ - WithXPriv(xPrivString), + WithXPriv(fixtures.XPrivString), } if adminKey { - opts = append(opts, WithAdminKey(adminKeyXpub)) + opts = append(opts, WithAdminKey(fixtures.XPrivString)) } return getTestBuxClientWithOpts(transportHandler, opts...) diff --git a/destinations_test.go b/destinations_test.go new file mode 100644 index 0000000..2794499 --- /dev/null +++ b/destinations_test.go @@ -0,0 +1,125 @@ +package buxclient + +import ( + "context" + "testing" + + buxmodels "github.com/BuxOrg/bux-models" + "github.com/stretchr/testify/assert" + + "github.com/BuxOrg/go-buxclient/fixtures" +) + +// TestDestinations will test the Destinations methods +func TestDestinations(t *testing.T) { + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/destination", + Result: fixtures.MarshallForTestHandler(fixtures.Destination), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + + t.Run("GetDestinationByID", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.GetDestinationByID(context.Background(), fixtures.Destination.ID) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) + + t.Run("GetDestinationByAddress", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.GetDestinationByAddress(context.Background(), fixtures.Destination.Address) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) + + t.Run("GetDestinationByLockingScript", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.GetDestinationByLockingScript(context.Background(), fixtures.Destination.LockingScript) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) + + t.Run("GetDestinations", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/destination/search", + Result: fixtures.MarshallForTestHandler([]*buxmodels.Destination{fixtures.Destination}), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + + // when + destinations, err := client.GetDestinations(context.Background(), fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, destinations, []*buxmodels.Destination{fixtures.Destination}) + }) + + t.Run("NewDestination", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.NewDestination(context.Background(), fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) + + t.Run("UpdateDestinationMetadataByID", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.UpdateDestinationMetadataByID(context.Background(), fixtures.Destination.ID, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) + + t.Run("UpdateDestinationMetadataByAddress", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.UpdateDestinationMetadataByAddress(context.Background(), fixtures.Destination.Address, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) + + t.Run("UpdateDestinationMetadataByLockingScript", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + destination, err := client.UpdateDestinationMetadataByLockingScript(context.Background(), fixtures.Destination.LockingScript, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, destination, fixtures.Destination) + }) +} diff --git a/fixtures/fixtures.go b/fixtures/fixtures.go new file mode 100644 index 0000000..5cb50cf --- /dev/null +++ b/fixtures/fixtures.go @@ -0,0 +1,191 @@ +package fixtures + +import ( + "encoding/json" + + buxmodels "github.com/BuxOrg/bux-models" + common "github.com/BuxOrg/bux-models/common" +) + +const ( + RequestType = "http" + ServerURL = "https://example.com/" + XPubString = "xpub661MyMwAqRbcFrBJbKwBGCB7d3fr2SaAuXGM95BA62X41m6eW2ehRQGW4xLi9wkEXUGnQZYxVVj4PxXnyrLk7jdqvBAs1Qq9gf6ykMvjR7J" + XPrivString = "xprv9s21ZrQH143K3N6qVJQAu4EP51qMcyrKYJLkLgmYXgz58xmVxVLSsbx2DfJUtjcnXK8NdvkHMKfmmg5AJT2nqqRWUrjSHX29qEJwBgBPkJQ" + AccessKeyString = "7779d24ca6f8821f225042bf55e8f80aa41b08b879b72827f51e41e6523b9cd0" + PaymailAddress = "address@paymail.com" +) + +func MarshallForTestHandler(object any) string { + json, err := json.Marshal(object) + if err != nil { + // as this is just for tests, empty string will make the tests fail, + // so it's acceptable as an "error" here, in case there's a problem with marshall + return "" + } + return string(json) +} + +var TestMetadata = &buxmodels.Metadata{"test-key": "test-value"} + +var Xpub = &buxmodels.Xpub{ + Model: common.Model{Metadata: *TestMetadata}, + ID: "cba0be1e753a7609e1a2f792d2e80ea6fce241be86f0690ec437377477809ccc", + CurrentBalance: 16680, + NextInternalNum: 2, + NextExternalNum: 1, +} + +var AccessKey = &buxmodels.AccessKey{ + Model: common.Model{Metadata: *TestMetadata}, + ID: "access-key-id", + XpubID: Xpub.ID, + Key: AccessKeyString, +} + +var Destination = &buxmodels.Destination{ + Model: common.Model{Metadata: *TestMetadata}, + ID: "90d10acb85f37dd009238fe7ec61a1411725825c82099bd8432fcb47ad8326ce", + XpubID: Xpub.ID, + LockingScript: "76a9140e0eb4911d79e9b7683f268964f595b66fa3604588ac", + Type: "pubkeyhash", + Chain: 1, + Num: 19, + Address: "18oETbMcqRB9S7NEGZgwsHKpoTpB3nKBMa", + DraftID: "3a0e1fdd9ac6046c0c82aa36b462e477a455880ceeb08d3aabb1bf031553d1df", +} + +var Transaction = &buxmodels.Transaction{ + Model: common.Model{Metadata: *TestMetadata}, + ID: "caae6e799210dfea7591e3d55455437eb7e1091bb01463ae1e7ddf9e29c75eda", + Hex: "0100000001cf4faa628ce1abdd2cfc641c948898bb7a3dbe043999236c3ea4436a0c79f5dc000000006a47304402206aeca14175e4477031970c1cda0af4d9d1206289212019b54f8e1c9272b5bac2022067c4d32086146ca77640f02a989f51b3c6738ebfa24683c4a923f647cf7f1c624121036295a81525ba33e22c6497c0b758e6a84b60d97c2d8905aa603dd364915c3a0effffffff023e030000000000001976a914f7fc6e0b05e91c3610efd0ce3f04f6502e2ed93d88ac99030000000000001976a914550e06a3aa71ba7414b53922c13f96a882bf027988ac00000000", + XpubInIDs: []string{Xpub.ID}, + XpubOutIDs: []string{Xpub.ID}, + BlockHash: "00000000000000000896d2b93efa4476c4bd47ed7a554aeac6b38044745a6257", + BlockHeight: 825599, + Fee: 97, + NumberOfInputs: 4, + NumberOfOutputs: 2, + DraftID: "fe6fe12c25b81106b7332d58fe87dab7bc6e56c8c21ca45b4de05f673f3f653c", + TotalValue: 6955, + OutputValue: 1725, + Outputs: map[string]int64{"680d975a403fd9ec90f613e87d17802c029d2d930df1c8373cdcdda2f536a1c0": 62}, + Status: "confirmed", + TransactionDirection: "incoming", +} + +var DraftTx = &buxmodels.DraftTransaction{ + Model: common.Model{Metadata: *TestMetadata}, + ID: "3a0e1fdd9ac6046c0c82aa36b462e477a455880ceeb08d3aabb1bf031553d1df", + Hex: "010000000123462f14e60556718916a8cff9dbf2258195a928777c0373200dba1cee105bdb0100000000ffffffff020c000000000000001976a914c4b15e7f65e3e6a062c1d21b7f1d7d2cd3b18e8188ac0b000000000000001976a91455873fd2baa7b51a624f6416b1d824939d99151a88ac00000000", + XpubID: Xpub.ID, + Configuration: buxmodels.TransactionConfig{ + ChangeDestinations: []*buxmodels.Destination{Destination}, + ChangeStrategy: "", + ChangeMinimumSatoshis: 0, + ChangeNumberOfDestinations: 0, + ChangeSatoshis: 11, + Fee: 1, + FeeUnit: &buxmodels.FeeUnit{ + Satoshis: 1, + Bytes: 1000, + }, + FromUtxos: []*buxmodels.UtxoPointer{{ + TransactionID: "caae6e799210dfea7591e3d55455437eb7e1091bb01463ae1e7ddf9e29c75eda", + OutputIndex: 1, + }}, + IncludeUtxos: []*buxmodels.UtxoPointer{{ + TransactionID: "caae6e799210dfea7591e3d55455437eb7e1091bb01463ae1e7ddf9e29c75eda", + OutputIndex: 1, + }}, + Inputs: []*buxmodels.TransactionInput{{ + Utxo: buxmodels.Utxo{ + UtxoPointer: buxmodels.UtxoPointer{ + TransactionID: "db5b10ee1cba0d2073037c7728a9958125f2dbf9cfa81689715605e6142f4623", + OutputIndex: 1, + }, + ID: "041479f86c475603fd510431cf702bc8c9849a9c350390eb86b467d82a13cc24", + XpubID: "9fe44728bf16a2dde3748f72cc65ea661f3bf18653b320d31eafcab37cf7fb36", + Satoshis: 24, + ScriptPubKey: "76a914673d3a53dade2723c48b446578681e253b5c548b88ac", + Type: "pubkeyhash", + DraftID: "3a0e1fdd9ac6046c0c82aa36b462e477a455880ceeb08d3aabb1bf031553d1df", + SpendingTxID: "", + }, + Destination: *Destination, + }}, + Outputs: []*buxmodels.TransactionOutput{ + { + PaymailP4: &buxmodels.PaymailP4{ + Alias: "dorzepowski", + Domain: "damiano.4chain.space", + FromPaymail: "test3@kuba.4chain.space", + Note: "paymail_note", + PubKey: "1DSsgJdB2AnWaFNgSbv4MZC2m71116JafG", + ReceiveEndpoint: "https://damiano.serveo.net/v1/bsvalias/receive-transaction/{alias}@{domain.tld}", + ReferenceID: "9b48dde1821fa82cf797372a297363c8", + ResolutionType: "p2p", + }, + Satoshis: 12, + Scripts: []*buxmodels.ScriptOutput{{ + Address: "1Jw1vRUq6pYqiMBAT6x3wBfebXCrXv6Qbr", + Satoshis: 12, + Script: "76a914c4b15e7f65e3e6a062c1d21b7f1d7d2cd3b18e8188ac", + ScriptType: "pubkeyhash", + }}, + To: "pubkeyhash", + UseForChange: false, + }, + { + Satoshis: 11, + Scripts: []*buxmodels.ScriptOutput{{ + Address: "18oETbMcqRB9S7NEGZgwsHKpoTpB3nKBMa", + Satoshis: 11, + Script: "76a91455873fd2baa7b51a624f6416b1d824939d99151a88ac", + ScriptType: "pubkeyhash", + }}, + To: "18oETbMcqRB9S7NEGZgwsHKpoTpB3nKBMa", + }, + }, + SendAllTo: &buxmodels.TransactionOutput{ + OpReturn: &buxmodels.OpReturn{ + Hex: "0100000001cf4faa628ce1abdd2cfc641c948898bb7a3dbe043999236c3ea4436a0c79f5dc000000006a47304402206aeca14175e4477031970c1cda0af4d9d1206289212019b54f8e1c9272b5bac2022067c4d32086146ca77640f02a989f51b3c6738ebfa24683c4a923f647cf7f1c624121036295a81525ba33e22c6497c0b758e6a84b60d97c2d8905aa603dd364915c3a0effffffff023e030000000000001976a914f7fc6e0b05e91c3610efd0ce3f04f6502e2ed93d88ac99030000000000001976a914550e06a3aa71ba7414b53922c13f96a882bf027988ac00000000", + HexParts: []string{"0100000001cf4faa628ce1abdd2cfc641c948898bb7a3dbe043999236c3ea4436a0c79f5dc000000006a47304402206aeca14175e4477031970c1cda0af4d9d1206289212019b54f8e1c9272b5bac2022067c4d32086146ca77640f02a989f51b3c6738ebfa24683c4a923f647cf7f1c624121036295a81525ba33e22c6497c0b758e6a84b60d97c2d8905aa603dd364915c3a0effffffff023e030000000000001976a914f7fc6e0b05e91c3610efd0ce3f04f6502e2ed93d88ac99030000000000001976a914550e06a3aa71ba7414b53922c13f96a882bf027988ac00000000"}, + Map: &buxmodels.MapProtocol{ + App: "app_protocol", + Keys: map[string]interface{}{"test-key": "test-value"}, + Type: "app_protocol_type", + }, + StringParts: []string{"string", "parts"}, + }, + PaymailP4: &buxmodels.PaymailP4{ + Alias: "alias", + Domain: "domain.tld", + FromPaymail: "alias@paymail.com", + Note: "paymail_note", + PubKey: "1DSsgJdB2AnWaFNgSbv4MZC2m71116JafG", + ReceiveEndpoint: "https://bsvalias.example.org/alias@domain.tld/payment-destination-response", + ReferenceID: "3d7c2ca83a46", + ResolutionType: "resolution_type", + }, + Satoshis: 1220, + Script: "script", + Scripts: []*buxmodels.ScriptOutput{{ + Address: "12HL5RyEy3Rt6SCwxgpiFSTigem1Pzbq22", + Satoshis: 1220, + Script: "script", + ScriptType: "pubkeyhash", + }}, + To: "1DSsgJdB2AnWaFNgSbv4MZC2m71116JafG", + UseForChange: false, + }, + Sync: &buxmodels.SyncConfig{ + Broadcast: true, + BroadcastInstant: true, + PaymailP2P: true, + SyncOnChain: true, + }, + }, + Status: "draft", + FinalTxID: "caae6e799210dfea7591e3d55455437eb7e1091bb01463ae1e7ddf9e29c75eda", +} diff --git a/paymail_addresses_test.go b/paymail_addresses_test.go new file mode 100644 index 0000000..7cd4275 --- /dev/null +++ b/paymail_addresses_test.go @@ -0,0 +1,32 @@ +package buxclient + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/BuxOrg/go-buxclient/fixtures" +) + +// TestPaymailAddresses will test Paymail Addresses methods +func TestPaymailAddresses(t *testing.T) { + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/paymail", + Result: "null", + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + + t.Run("NewPaymail", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + err := client.NewPaymail(context.Background(), fixtures.XPubString, fixtures.PaymailAddress, "", "", fixtures.TestMetadata) + + // then + assert.NoError(t, err) + }) +} diff --git a/transactions.go b/transactions.go index d1641cf..3c4bebc 100644 --- a/transactions.go +++ b/transactions.go @@ -4,7 +4,6 @@ import ( "context" buxmodels "github.com/BuxOrg/bux-models" - buxerrors "github.com/BuxOrg/bux-models/bux-errors" "github.com/BuxOrg/go-buxclient/transports" ) @@ -67,8 +66,6 @@ func (b *BuxClient) SendToRecipients(ctx context.Context, recipients []*transpor draft, err := b.DraftToRecipients(ctx, recipients, metadata) if err != nil { return nil, err - } else if draft == nil { - return nil, transports.WrapError(buxerrors.ErrDraftNotFound) } var hex string diff --git a/transactions_test.go b/transactions_test.go new file mode 100644 index 0000000..e2a85df --- /dev/null +++ b/transactions_test.go @@ -0,0 +1,324 @@ +package buxclient + +import ( + "context" + "net/http" + "testing" + + buxmodels "github.com/BuxOrg/bux-models" + "github.com/libsv/go-bt/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/BuxOrg/go-buxclient/fixtures" + "github.com/BuxOrg/go-buxclient/transports" +) + +// TestTransactions will test the Transaction methods +func TestTransactions(t *testing.T) { + t.Run("GetTransaction", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction", + Result: fixtures.MarshallForTestHandler(fixtures.Transaction), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + + // when + tx, err := client.GetTransaction(context.Background(), fixtures.Transaction.ID) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.Transaction, tx) + }) + + t.Run("GetTransactions", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction/search", + Result: fixtures.MarshallForTestHandler([]*buxmodels.Transaction{fixtures.Transaction}), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + conditions := map[string]interface{}{ + "fee": map[string]interface{}{ + "$lt": 100, + }, + "total_value": map[string]interface{}{ + "$lt": 740, + }, + } + + // when + txs, err := client.GetTransactions(context.Background(), conditions, fixtures.TestMetadata, nil) + + // then + assert.NoError(t, err) + assert.Equal(t, []*buxmodels.Transaction{fixtures.Transaction}, txs) + }) + + t.Run("GetTransactionsCount", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction/count", + Result: "1", + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + conditions := map[string]interface{}{ + "fee": map[string]interface{}{ + "$lt": 100, + }, + "total_value": map[string]interface{}{ + "$lt": 740, + }, + } + + // when + count, err := client.GetTransactionsCount(context.Background(), conditions, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, int64(1), count) + }) + + t.Run("RecordTransaction", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction/record", + Result: fixtures.MarshallForTestHandler(fixtures.Transaction), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + + // when + tx, err := client.RecordTransaction(context.Background(), fixtures.Transaction.Hex, "", fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.Transaction, tx) + }) + + t.Run("UpdateTransactionMetadata", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction", + Result: fixtures.MarshallForTestHandler(fixtures.Transaction), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + + // when + tx, err := client.UpdateTransactionMetadata(context.Background(), fixtures.Transaction.ID, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.Transaction, tx) + }) + + t.Run("SendToRecipients", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Queries: []*testTransportHandlerRequest{ + { + Path: "/transaction/record", + Result: func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + mustWrite(w, fixtures.MarshallForTestHandler(fixtures.Transaction)) + }, + }, + { + Path: "/transaction", + Result: func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + mustWrite(w, fixtures.MarshallForTestHandler(fixtures.DraftTx)) + }, + }, + }, + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + recipients := []*transports.Recipients{{ + OpReturn: fixtures.DraftTx.Configuration.Outputs[0].OpReturn, + Satoshis: 1000, + To: fixtures.Destination.Address, + }} + + // when + tx, err := client.SendToRecipients(context.Background(), recipients, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.Transaction, tx) + }) + + t.Run("SendToRecipients - nil draft", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Queries: []*testTransportHandlerRequest{ + { + Path: "/transaction/record", + Result: func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + mustWrite(w, fixtures.MarshallForTestHandler(fixtures.Transaction)) + }, + }, + { + Path: "/transaction", + Result: func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + mustWrite(w, "nil") + }, + }, + }, + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + recipients := []*transports.Recipients{{ + OpReturn: fixtures.DraftTx.Configuration.Outputs[0].OpReturn, + Satoshis: 1000, + To: fixtures.Destination.Address, + }} + + // when + tx, err := client.SendToRecipients(context.Background(), recipients, fixtures.TestMetadata) + + // then + assert.Error(t, err) + assert.Nil(t, tx) + }) + + t.Run("SendToRecipients - FinalizeTransaction error", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Queries: []*testTransportHandlerRequest{ + { + Path: "/transaction/record", + Result: func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + mustWrite(w, fixtures.MarshallForTestHandler(fixtures.Transaction)) + }, + }, + { + Path: "/transaction", + Result: func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + mustWrite(w, fixtures.MarshallForTestHandler(buxmodels.DraftTransaction{})) + }, + }, + }, + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + recipients := []*transports.Recipients{{ + OpReturn: fixtures.DraftTx.Configuration.Outputs[0].OpReturn, + Satoshis: 1000, + To: fixtures.Destination.Address, + }} + + // when + tx, err := client.SendToRecipients(context.Background(), recipients, fixtures.TestMetadata) + + // then + assert.Error(t, err) + assert.Nil(t, tx) + }) + + t.Run("FinalizeTransaction", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction/record", + Result: fixtures.MarshallForTestHandler(fixtures.Transaction), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + + // when + signedHex, err := client.FinalizeTransaction(fixtures.DraftTx) + + txDraft, btErr := bt.NewTxFromString(signedHex) + require.NoError(t, btErr) + + // then + assert.NoError(t, err) + assert.Len(t, txDraft.Inputs, len(fixtures.DraftTx.Configuration.Inputs)) + assert.Len(t, txDraft.Outputs, len(fixtures.DraftTx.Configuration.Outputs)) + }) + + t.Run("UnreserveUtxos", func(t *testing.T) { + // given + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/utxo/unreserve", + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + client := getTestBuxClient(transportHandler, false) + + // when + err := client.UnreserveUtxos(context.Background(), fixtures.DraftTx.Configuration.Outputs[0].PaymailP4.ReferenceID) + + // then + assert.NoError(t, err) + }) +} + +// TestDraftTransactions will test the DraftTransaction methods +func TestDraftTransactions(t *testing.T) { + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/transaction", + Result: fixtures.MarshallForTestHandler(fixtures.DraftTx), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + + t.Run("DraftToRecipients", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + recipients := []*transports.Recipients{{ + OpReturn: fixtures.DraftTx.Configuration.Outputs[0].OpReturn, + Satoshis: 1000, + To: fixtures.Destination.Address, + }} + + // when + draft, err := client.DraftToRecipients(context.Background(), recipients, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.DraftTx, draft) + }) + + t.Run("DraftTransaction", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, false) + + // when + draft, err := client.DraftTransaction(context.Background(), &fixtures.DraftTx.Configuration, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.DraftTx, draft) + }) +} diff --git a/transports/authentication.go b/transports/authentication.go index a35deeb..287e0f4 100644 --- a/transports/authentication.go +++ b/transports/authentication.go @@ -8,17 +8,18 @@ import ( buxmodels "github.com/BuxOrg/bux-models" buxerrors "github.com/BuxOrg/bux-models/bux-errors" - "github.com/BuxOrg/go-buxclient/utils" "github.com/bitcoinschema/go-bitcoin/v2" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bk/bip32" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" "github.com/libsv/go-bt/v2/sighash" + + "github.com/BuxOrg/go-buxclient/utils" ) // SetSignature will set the signature on the header for the request -func SetSignature(header *http.Header, xPriv *bip32.ExtendedKey, bodyString string) ResponseError { +func setSignature(header *http.Header, xPriv *bip32.ExtendedKey, bodyString string) ResponseError { // Create the signature authData, err := createSignature(xPriv, bodyString) if err != nil { @@ -84,7 +85,7 @@ func SignInputs(dt *buxmodels.DraftTransaction, xPriv *bip32.ExtendedKey) (signe // Get the unlocking script var s *bscript.Script - if s, err = GetUnlockingScript( + if s, err = getUnlockingScript( txDraft, uint32(index), privateKey, ); err != nil { resError = WrapError(err) @@ -106,7 +107,7 @@ func SignInputs(dt *buxmodels.DraftTransaction, xPriv *bip32.ExtendedKey) (signe } // GetUnlockingScript will generate an unlocking script -func GetUnlockingScript(tx *bt.Tx, inputIndex uint32, privateKey *bec.PrivateKey) (*bscript.Script, error) { +func getUnlockingScript(tx *bt.Tx, inputIndex uint32, privateKey *bec.PrivateKey) (*bscript.Script, error) { sigHashFlags := sighash.AllForkID sigHash, err := tx.CalcInputSignatureHash(inputIndex, sigHashFlags) diff --git a/transports/transports.go b/transports/transports.go index 011af49..2527c8c 100644 --- a/transports/transports.go +++ b/transports/transports.go @@ -25,7 +25,7 @@ type ClientOps func(c *Client) // addSignature will add the signature to the request func addSignature(header *http.Header, xPriv *bip32.ExtendedKey, bodyString string) ResponseError { - return SetSignature(header, xPriv, bodyString) + return setSignature(header, xPriv, bodyString) } // NewTransport create a new transport service object diff --git a/xpubs.go b/xpubs.go index 6e4ea2a..435cc61 100644 --- a/xpubs.go +++ b/xpubs.go @@ -10,7 +10,6 @@ import ( // NewXpub registers a new xpub - admin key needed func (b *BuxClient) NewXpub(ctx context.Context, rawXPub string, metadata *buxmodels.Metadata) transports.ResponseError { return b.transport.NewXpub(ctx, rawXPub, metadata) - //return nil } // GetXPub gets the current xpub diff --git a/xpubs_test.go b/xpubs_test.go new file mode 100644 index 0000000..d9f349a --- /dev/null +++ b/xpubs_test.go @@ -0,0 +1,56 @@ +package buxclient + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/BuxOrg/go-buxclient/fixtures" +) + +// TestXpub will test the Xpub methods +func TestXpub(t *testing.T) { + transportHandler := testTransportHandler{ + Type: fixtures.RequestType, + Path: "/xpub", + Result: fixtures.MarshallForTestHandler(fixtures.Xpub), + ClientURL: fixtures.ServerURL, + Client: WithHTTPClient, + } + + t.Run("NewXpub", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, true) + + // when + err := client.NewXpub(context.Background(), fixtures.XPubString, fixtures.TestMetadata) + + // then + assert.NoError(t, err) + }) + + t.Run("GetXPub", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, true) + + // when + xpub, err := client.GetXPub(context.Background()) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.Xpub, xpub) + }) + + t.Run("UpdateXPubMetadata", func(t *testing.T) { + // given + client := getTestBuxClient(transportHandler, true) + + // when + xpub, err := client.UpdateXPubMetadata(context.Background(), fixtures.TestMetadata) + + // then + assert.NoError(t, err) + assert.Equal(t, fixtures.Xpub, xpub) + }) +}