Skip to content
This repository has been archived by the owner on Oct 21, 2024. It is now read-only.

Commit

Permalink
feat(BUX-120, BUX-148, BUX-150): broadcast, query subtasks (#2)
Browse files Browse the repository at this point in the history
* feat: prepares query tx with unit tests

* feat: arc query tx - fixes unit tests

* feat: initializes SubmitTransaction in broadcaster and arc client

* feat: creates a paylaod wrapper for httpclient

* feat: finishes submit-tx func

* feat: adds acceptance test for query_tx

* fix: change to correct models

* feat: refactors broadcast package

* feat: finishes query tx tests

* fix: adds given/when/then and proper names

* fix: modifies packages inside files to expose correct methods

* refactor(BUX-120): move components across packages to cut circular dependencies (#3)

* refactor(BUX-120): move components across packages to cut circular dependencies

* refactor(BUX-120): move API to broadcast package, move builder to broadcast-client package

---------

Co-authored-by: Damian Orzepowski <damiano@MacBook-Pro.localdomain>

* feat: working examples

* feat: finishes acceptance_test

* fix: cr requests

* fix: adds WithHttpClient option to builder instead of passing it as param

---------

Co-authored-by: Damian Orzepowski <damian.orzepowski@softwarelab.com.pl>
Co-authored-by: Damian Orzepowski <damiano@MacBook-Pro.localdomain>
  • Loading branch information
3 people authored Aug 2, 2023
1 parent 8f2ad7b commit ebb8aea
Show file tree
Hide file tree
Showing 29 changed files with 1,148 additions and 173 deletions.
22 changes: 0 additions & 22 deletions broadcast/arc/arc_client.go

This file was deleted.

14 changes: 14 additions & 0 deletions broadcast/broadcast-client/arc_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package broadcast_client

type ArcClientConfig struct {
APIUrl string
Token string
}

func (c *ArcClientConfig) GetApiUrl() string {
return c.APIUrl
}

func (c *ArcClientConfig) GetToken() string {
return c.Token
}
36 changes: 36 additions & 0 deletions broadcast/broadcast-client/client_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package broadcast_client

import (
broadcast_api "github.com/bitcoin-sv/go-broadcast-client/broadcast"
"github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/arc"
"github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/composite"
"github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/httpclient"
)

type builder struct {
factories []composite.BroadcastFactory
client httpclient.HTTPInterface
}

func Builder() *builder {
return &builder{}
}

func (cb *builder) WithHttpClient(client httpclient.HTTPInterface) *builder {
cb.client = client
return cb
}

func (cb *builder) WithArc(config ArcClientConfig) *builder {
cb.factories = append(cb.factories, func() broadcast_api.Client {
return arc.NewArcClient(&config, cb.client)
})
return cb
}

func (cb *builder) Build() broadcast_api.Client {
if len(cb.factories) == 1 {
return cb.factories[0]()
}
return composite.NewBroadcasterWithDefaultStrategy(cb.factories...)
}
43 changes: 0 additions & 43 deletions broadcast/broadcast.go

This file was deleted.

17 changes: 17 additions & 0 deletions broadcast/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package broadcast

import (
"errors"
)

var ErrClientUndefined = errors.New("client is undefined")

var ErrAllBroadcastersFailed = errors.New("all broadcasters failed")

var ErrURLEmpty = errors.New("url is empty")

var ErrBroadcasterFailed = errors.New("broadcaster failed")

var ErrUnableToDecodeResponse = errors.New("unable to decode response")

var ErrMissingStatus = errors.New("missing tx status")
64 changes: 31 additions & 33 deletions broadcast/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,34 @@ import (
)

type BestQuoter interface {
// BestQuote(ctx context.Context, feeCategory, feeType string) (*FeeQuoteResponse, error)
BestQuote(ctx context.Context, feeCategory, feeType string) error
}

// type FastestQuoter interface {
// // FastestQuote(ctx context.Context, timeout time.Duration) (*FeeQuoteResponse, error)
// FastestQuote(ctx context.Context, timeout time.Duration) error
// }

// type FeeQuoter interface {
// // FeeQuote(ctx context.Context, miner *Miner) (*FeeQuoteResponse, error)
// FeeQuote(ctx context.Context) error
// }

// type PolicyQuoter interface {
// // PolicyQuote(ctx context.Context, miner *Miner) (*PolicyQuoteResponse, error)
// PolicyQuote(ctx context.Context) error
// }

// type TransactionQuerier interface {
// // // QueryTransaction(ctx context.Context, miner *Miner, txID string, opts ...QueryTransactionOptFunc) (*QueryTransactionResponse, error)
// QueryTransaction(ctx context.Context, txID string) error
// }

// type TransactionSubmitter interface {
// // SubmitTransaction(ctx context.Context, miner *Miner, tx *Transaction) (*SubmitTransactionResponse, error)
// SubmitTransaction(ctx context.Context) error
// }

// type TransactionsSubmitter interface {
// // SubmitTransactions(ctx context.Context, miner *Miner, txs []Transaction) (*SubmitTransactionsResponse, error)
// SubmitTransactions(ctx context.Context) error
// }
}

type FastestQuoter interface {
}

type FeeQuoter interface {
}

type PolicyQuoter interface {
}

type TransactionQuerier interface {
QueryTransaction(ctx context.Context, txID string) (*QueryTxResponse, error)
}

type TransactionSubmitter interface {
SubmitTransaction(ctx context.Context, tx *Transaction) (*SubmitTxResponse, error)
}

type TransactionsSubmitter interface {
}

type Client interface {
BestQuoter
FastestQuoter
FeeQuoter
PolicyQuoter
TransactionQuerier
TransactionSubmitter
TransactionsSubmitter
}
115 changes: 115 additions & 0 deletions broadcast/internal/acceptance_tests/arc_query_tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package acceptancetests

import (
"context"
"errors"
"io"
"net/http"
"strings"
"testing"

"github.com/bitcoin-sv/go-broadcast-client/broadcast"
broadcast_client "github.com/bitcoin-sv/go-broadcast-client/broadcast/broadcast-client"
"github.com/bitcoin-sv/go-broadcast-client/broadcast/internal/arc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

var firstSuccessfulResponse = `
{
"blockHash": "hash",
"txStatus": "MINED",
"txid": "abc123"
}
`

var secondSuccessfulResponse = `
{
"blockHash": "hash",
"txStatus": "CONFIRMED"
"txid": "abc123"
}
`

func TestQueryTransaction(t *testing.T) {
t.Run("Should successfully query from multiple ArcClients", func(t *testing.T) {
// given
httpClientMock := &arc.MockHttpClient{}
broadcaster := broadcast_client.Builder().
WithHttpClient(httpClientMock).
WithArc(broadcast_client.ArcClientConfig{APIUrl: "http://arc1-api-url", Token: "arc1-token"}).
WithArc(broadcast_client.ArcClientConfig{APIUrl: "http://arc2-api-url", Token: "arc2-token"}).
Build()

httpResponse1 := &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader(firstSuccessfulResponse))}
httpResponse2 := &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader(secondSuccessfulResponse))}
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse1, nil).Once()
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse2, nil).Once()

// when
result, err := broadcaster.QueryTransaction(context.Background(), "txID")

// then
assert.NoError(t, err)
assert.NotNil(t, result)
})

t.Run("Should return error if all ArcClients return errors", func(t *testing.T) {
// given
httpClientMock := &arc.MockHttpClient{}
broadcaster := broadcast_client.Builder().
WithHttpClient(httpClientMock).
WithArc(broadcast_client.ArcClientConfig{APIUrl: "http://arc1-api-url", Token: "arc1-token"}).
WithArc(broadcast_client.ArcClientConfig{APIUrl: "http://arc2-api-url", Token: "arc2-token"}).
Build()

httpResponse := &http.Response{}
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse, errors.New("http error")).Twice()

// when
result, err := broadcaster.QueryTransaction(context.Background(), "txID")

// then
assert.Error(t, err)
assert.Nil(t, result)
assert.EqualError(t, err, broadcast.ErrAllBroadcastersFailed.Error())
})

t.Run("Should successfully query from single ArcClient", func(t *testing.T) {
// given
httpClientMock := &arc.MockHttpClient{}
broadcaster := broadcast_client.Builder().
WithHttpClient(httpClientMock).
WithArc(broadcast_client.ArcClientConfig{APIUrl: "http://arc1-api-url", Token: "arc1-token"}).
Build()

httpResponse1 := &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader(firstSuccessfulResponse))}
httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse1, nil).Once()

// when
result, err := broadcaster.QueryTransaction(context.Background(), "txID")

// then
assert.NoError(t, err)
assert.NotNil(t, result)
})

t.Run("Should return error if single ArcClient returns error", func(t *testing.T) {
// given
httpClientMock := &arc.MockHttpClient{}
httpResponse := &http.Response{}
broadcaster := broadcast_client.Builder().
WithHttpClient(httpClientMock).
WithArc(broadcast_client.ArcClientConfig{APIUrl: "http://arc1-api-url", Token: "arc1-token"}).
Build()

httpClientMock.On("DoRequest", mock.Anything, mock.Anything).Return(httpResponse, errors.New("http error")).Once()

// when
result, err := broadcaster.QueryTransaction(context.Background(), "txID")

// then
assert.Error(t, err)
assert.Nil(t, result)
})
}
Loading

0 comments on commit ebb8aea

Please sign in to comment.