6
6
"encoding/hex"
7
7
"fmt"
8
8
"net"
9
+ "slices"
9
10
"testing"
10
11
"time"
11
12
@@ -16,6 +17,7 @@ import (
16
17
"github.com/lightninglabs/taproot-assets/rpcutils"
17
18
oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
18
19
"github.com/lightningnetwork/lnd/cert"
20
+ "github.com/stretchr/testify/mock"
19
21
"github.com/stretchr/testify/require"
20
22
"google.golang.org/grpc"
21
23
"google.golang.org/grpc/credentials"
@@ -30,35 +32,43 @@ type oracleHarness struct {
30
32
grpcListener net.Listener
31
33
grpcServer * grpc.Server
32
34
33
- // bidPrices is a map used internally by the oracle harness to store bid
35
+ // Mock is a mock object that can optionally be used to track calls to
36
+ // the oracle harness. If no call expectations are set, the prices from
37
+ // the maps below will be used.
38
+ // NOTE: When setting up the call expectations, we need to use the
39
+ // actual fields of the `QueryAssetRatesRequest` message, since
40
+ // otherwise it is much harder to match the calls nicely.
41
+ mock.Mock
42
+
43
+ // buyPrices is a map used internally by the oracle harness to store buy
34
44
// prices for certain assets. We use the asset specifier string as a
35
45
// unique identifier, since it will either contain an asset ID or a
36
46
// group key.
37
- bidPrices map [string ]rfqmath.BigIntFixedPoint
47
+ buyPrices map [string ]rfqmath.BigIntFixedPoint
38
48
39
- // askPrices is a map used internally by the oracle harness to store ask
40
- // prices for certain assets. We use the asset specifier string as a
41
- // unique identifier, since it will either contain an asset ID or a
49
+ // sellPrices is a map used internally by the oracle harness to store
50
+ // sell prices for certain assets. We use the asset specifier string as
51
+ // a unique identifier, since it will either contain an asset ID or a
42
52
// group key.
43
- askPrices map [string ]rfqmath.BigIntFixedPoint
53
+ sellPrices map [string ]rfqmath.BigIntFixedPoint
44
54
}
45
55
46
56
// newOracleHarness returns a new oracle harness instance that is set to listen
47
57
// on the provided address.
48
58
func newOracleHarness (listenAddr string ) * oracleHarness {
49
59
return & oracleHarness {
50
60
listenAddr : listenAddr ,
51
- bidPrices : make (map [string ]rfqmath.BigIntFixedPoint ),
52
- askPrices : make (map [string ]rfqmath.BigIntFixedPoint ),
61
+ buyPrices : make (map [string ]rfqmath.BigIntFixedPoint ),
62
+ sellPrices : make (map [string ]rfqmath.BigIntFixedPoint ),
53
63
}
54
64
}
55
65
56
- // setPrice sets the target bid and ask price for the provided specifier.
57
- func (o * oracleHarness ) setPrice (specifier asset.Specifier , bidPrice ,
58
- askPrice rfqmath.BigIntFixedPoint ) {
66
+ // setPrice sets the target buy and sell price for the provided specifier.
67
+ func (o * oracleHarness ) setPrice (specifier asset.Specifier , buyPrice ,
68
+ sellPrice rfqmath.BigIntFixedPoint ) {
59
69
60
- o .bidPrices [specifier .String ()] = bidPrice
61
- o .askPrices [specifier .String ()] = askPrice
70
+ o .buyPrices [specifier .String ()] = buyPrice
71
+ o .sellPrices [specifier .String ()] = sellPrice
62
72
}
63
73
64
74
// start runs the oracle harness.
@@ -113,14 +123,14 @@ func (o *oracleHarness) getAssetRates(specifier asset.Specifier,
113
123
// Determine the rate based on the transaction type.
114
124
var subjectAssetRate rfqmath.BigIntFixedPoint
115
125
if transactionType == oraclerpc .TransactionType_PURCHASE {
116
- rate , ok := o .bidPrices [specifier .String ()]
126
+ rate , ok := o .buyPrices [specifier .String ()]
117
127
if ! ok {
118
128
return oraclerpc.AssetRates {}, fmt .Errorf ("purchase " +
119
129
"price not found for %s" , specifier .String ())
120
130
}
121
131
subjectAssetRate = rate
122
132
} else {
123
- rate , ok := o .askPrices [specifier .String ()]
133
+ rate , ok := o .sellPrices [specifier .String ()]
124
134
if ! ok {
125
135
return oraclerpc.AssetRates {}, fmt .Errorf ("sale " +
126
136
"price not found for %s" , specifier .String ())
@@ -181,6 +191,18 @@ func (o *oracleHarness) QueryAssetRates(_ context.Context,
181
191
req * oraclerpc.QueryAssetRatesRequest ) (
182
192
* oraclerpc.QueryAssetRatesResponse , error ) {
183
193
194
+ // Return early with the mocked value if call expectations are set up.
195
+ if hasExpectedCall (o .ExpectedCalls , "QueryAssetRates" ) {
196
+ args := o .Called (
197
+ req .TransactionType , req .SubjectAsset ,
198
+ req .SubjectAssetMaxAmount , req .PaymentAsset ,
199
+ req .PaymentAssetMaxAmount , req .AssetRatesHint ,
200
+ req .Intent , req .CounterpartyId , req .Metadata ,
201
+ )
202
+ resp , _ := args .Get (0 ).(* oraclerpc.QueryAssetRatesResponse )
203
+ return resp , args .Error (1 )
204
+ }
205
+
184
206
// Ensure that the payment asset is BTC. We only support BTC as the
185
207
// payment asset in this example.
186
208
if ! rpcutils .IsAssetBtc (req .PaymentAsset ) {
@@ -203,8 +225,8 @@ func (o *oracleHarness) QueryAssetRates(_ context.Context,
203
225
return nil , fmt .Errorf ("error parsing subject asset: %w" , err )
204
226
}
205
227
206
- _ , hasPurchase := o .bidPrices [specifier .String ()]
207
- _ , hasSale := o .askPrices [specifier .String ()]
228
+ _ , hasPurchase := o .buyPrices [specifier .String ()]
229
+ _ , hasSale := o .sellPrices [specifier .String ()]
208
230
209
231
log .Infof ("Have for %s, purchase=%v, sale=%v" , specifier .String (),
210
232
hasPurchase , hasSale )
@@ -324,3 +346,11 @@ func generateSelfSignedCert() (tls.Certificate, error) {
324
346
325
347
return tlsCert , nil
326
348
}
349
+
350
+ // hasExpectedCall checks if the method call has been registered as an expected
351
+ // call with the mock object.
352
+ func hasExpectedCall (expectedCalls []* mock.Call , method string ) bool {
353
+ return slices .ContainsFunc (expectedCalls , func (call * mock.Call ) bool {
354
+ return call .Method == method
355
+ })
356
+ }
0 commit comments