Skip to content

Commit ab8923f

Browse files
committed
loopdb: add sqlite test files
1 parent 9278c18 commit ab8923f

File tree

1 file changed

+389
-0
lines changed

1 file changed

+389
-0
lines changed

loopdb/sql_test.go

Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
package loopdb
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"errors"
7+
"math/rand"
8+
"reflect"
9+
"testing"
10+
"time"
11+
12+
"github.com/btcsuite/btcd/chaincfg/chainhash"
13+
"github.com/lightninglabs/loop/loopdb/sqlc"
14+
"github.com/lightninglabs/loop/test"
15+
"github.com/lightningnetwork/lnd/keychain"
16+
"github.com/lightningnetwork/lnd/routing/route"
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
var (
21+
testTime1 = time.Date(2018, time.January, 9, 14, 54, 32, 3, time.UTC)
22+
testTime2 = time.Date(2018, time.January, 9, 15, 02, 03, 5, time.UTC)
23+
)
24+
25+
// TestSqliteLoopOutStore tests all the basic functionality of the current
26+
// sqlite swap store.
27+
func TestSqliteLoopOutStore(t *testing.T) {
28+
destAddr := test.GetDestAddr(t, 0)
29+
initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
30+
31+
// Next, we'll make a new pending swap that we'll insert into the
32+
// database shortly.
33+
unrestrictedSwap := LoopOutContract{
34+
SwapContract: SwapContract{
35+
AmountRequested: 100,
36+
Preimage: testPreimage,
37+
CltvExpiry: 144,
38+
HtlcKeys: HtlcKeys{
39+
SenderScriptKey: senderKey,
40+
ReceiverScriptKey: receiverKey,
41+
SenderInternalPubKey: senderInternalKey,
42+
ReceiverInternalPubKey: receiverInternalKey,
43+
ClientScriptKeyLocator: keychain.KeyLocator{
44+
Family: 1,
45+
Index: 2,
46+
},
47+
},
48+
MaxMinerFee: 10,
49+
MaxSwapFee: 20,
50+
51+
InitiationHeight: 99,
52+
53+
InitiationTime: initiationTime,
54+
ProtocolVersion: ProtocolVersionMuSig2,
55+
},
56+
MaxPrepayRoutingFee: 40,
57+
PrepayInvoice: "prepayinvoice",
58+
DestAddr: destAddr,
59+
SwapInvoice: "swapinvoice",
60+
MaxSwapRoutingFee: 30,
61+
SweepConfTarget: 2,
62+
HtlcConfirmations: 2,
63+
SwapPublicationDeadline: initiationTime,
64+
}
65+
66+
t.Run("no outgoing set", func(t *testing.T) {
67+
testSqliteLoopOutStore(t, &unrestrictedSwap)
68+
})
69+
70+
restrictedSwap := unrestrictedSwap
71+
restrictedSwap.OutgoingChanSet = ChannelSet{1, 2}
72+
73+
t.Run("two channel outgoing set", func(t *testing.T) {
74+
testSqliteLoopOutStore(t, &restrictedSwap)
75+
})
76+
77+
labelledSwap := unrestrictedSwap
78+
labelledSwap.Label = "test label"
79+
t.Run("labelled swap", func(t *testing.T) {
80+
testSqliteLoopOutStore(t, &labelledSwap)
81+
})
82+
}
83+
84+
// testSqliteLoopOutStore tests the basic functionality of the current sqlite
85+
// swap store for specific swap parameters.
86+
func testSqliteLoopOutStore(t *testing.T, pendingSwap *LoopOutContract) {
87+
store := NewTestDB(t)
88+
89+
ctxb := context.Background()
90+
91+
// First, verify that an empty database has no active swaps.
92+
swaps, err := store.FetchLoopOutSwaps(ctxb)
93+
94+
require.NoError(t, err)
95+
require.Empty(t, swaps)
96+
97+
hash := pendingSwap.Preimage.Hash()
98+
99+
// checkSwap is a test helper function that'll assert the state of a
100+
// swap.
101+
checkSwap := func(expectedState SwapState) {
102+
t.Helper()
103+
104+
swaps, err := store.FetchLoopOutSwaps(ctxb)
105+
require.NoError(t, err)
106+
107+
require.Len(t, swaps, 1)
108+
109+
swap, err := store.FetchLoopOutSwap(ctxb, hash)
110+
require.NoError(t, err)
111+
112+
require.Equal(t, hash, swap.Hash)
113+
require.Equal(t, hash, swaps[0].Hash)
114+
115+
swapContract := swap.Contract
116+
117+
require.Equal(t, swapContract, pendingSwap)
118+
119+
require.Equal(t, expectedState, swap.State().State)
120+
121+
if expectedState == StatePreimageRevealed {
122+
require.NotNil(t, swap.State().HtlcTxHash)
123+
}
124+
}
125+
126+
// If we create a new swap, then it should show up as being initialized
127+
// right after.
128+
err = store.CreateLoopOut(ctxb, hash, pendingSwap)
129+
require.NoError(t, err)
130+
131+
checkSwap(StateInitiated)
132+
133+
// Trying to make the same swap again should result in an error.
134+
err = store.CreateLoopOut(ctxb, hash, pendingSwap)
135+
require.Error(t, err)
136+
checkSwap(StateInitiated)
137+
138+
// Next, we'll update to the next state of the pre-image being
139+
// revealed. The state should be reflected here again.
140+
err = store.UpdateLoopOut(
141+
ctxb, hash, testTime,
142+
SwapStateData{
143+
State: StatePreimageRevealed,
144+
HtlcTxHash: &chainhash.Hash{1, 6, 2},
145+
},
146+
)
147+
require.NoError(t, err)
148+
149+
checkSwap(StatePreimageRevealed)
150+
151+
// Next, we'll update to the final state to ensure that the state is
152+
// properly updated.
153+
err = store.UpdateLoopOut(
154+
ctxb, hash, testTime,
155+
SwapStateData{
156+
State: StateFailInsufficientValue,
157+
},
158+
)
159+
require.NoError(t, err)
160+
checkSwap(StateFailInsufficientValue)
161+
162+
err = store.Close()
163+
require.NoError(t, err)
164+
}
165+
166+
// TestSQLliteLoopInStore tests all the basic functionality of the current
167+
// sqlite swap store.
168+
func TestSQLliteLoopInStore(t *testing.T) {
169+
initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
170+
171+
// Next, we'll make a new pending swap that we'll insert into the
172+
// database shortly.
173+
lastHop := route.Vertex{1, 2, 3}
174+
175+
pendingSwap := LoopInContract{
176+
SwapContract: SwapContract{
177+
AmountRequested: 100,
178+
Preimage: testPreimage,
179+
CltvExpiry: 144,
180+
HtlcKeys: HtlcKeys{
181+
SenderScriptKey: senderKey,
182+
ReceiverScriptKey: receiverKey,
183+
SenderInternalPubKey: senderInternalKey,
184+
ReceiverInternalPubKey: receiverInternalKey,
185+
ClientScriptKeyLocator: keychain.KeyLocator{
186+
Family: 1,
187+
Index: 2,
188+
},
189+
},
190+
MaxMinerFee: 10,
191+
MaxSwapFee: 20,
192+
InitiationHeight: 99,
193+
194+
// Convert to/from unix to remove timezone, so that it
195+
// doesn't interfere with DeepEqual.
196+
InitiationTime: initiationTime,
197+
ProtocolVersion: ProtocolVersionMuSig2,
198+
},
199+
HtlcConfTarget: 2,
200+
LastHop: &lastHop,
201+
ExternalHtlc: true,
202+
}
203+
204+
t.Run("loop in", func(t *testing.T) {
205+
testSqliteLoopInStore(t, pendingSwap)
206+
})
207+
208+
labelledSwap := pendingSwap
209+
labelledSwap.Label = "test label"
210+
t.Run("loop in with label", func(t *testing.T) {
211+
testSqliteLoopInStore(t, labelledSwap)
212+
})
213+
}
214+
215+
func testSqliteLoopInStore(t *testing.T, pendingSwap LoopInContract) {
216+
store := NewTestDB(t)
217+
218+
ctxb := context.Background()
219+
220+
// First, verify that an empty database has no active swaps.
221+
swaps, err := store.FetchLoopInSwaps(ctxb)
222+
require.NoError(t, err)
223+
require.Empty(t, swaps)
224+
225+
hash := sha256.Sum256(testPreimage[:])
226+
227+
// checkSwap is a test helper function that'll assert the state of a
228+
// swap.
229+
checkSwap := func(expectedState SwapState) {
230+
t.Helper()
231+
232+
swaps, err := store.FetchLoopInSwaps(ctxb)
233+
require.NoError(t, err)
234+
require.Len(t, swaps, 1)
235+
236+
swap := swaps[0].Contract
237+
238+
require.Equal(t, swap, &pendingSwap)
239+
240+
require.Equal(t, swaps[0].State().State, expectedState)
241+
}
242+
243+
// If we create a new swap, then it should show up as being initialized
244+
// right after.
245+
err = store.CreateLoopIn(ctxb, hash, &pendingSwap)
246+
require.NoError(t, err)
247+
248+
checkSwap(StateInitiated)
249+
250+
// Trying to make the same swap again should result in an error.
251+
err = store.CreateLoopIn(ctxb, hash, &pendingSwap)
252+
require.Error(t, err)
253+
254+
checkSwap(StateInitiated)
255+
256+
// Next, we'll update to the next state of the pre-image being
257+
// revealed. The state should be reflected here again.
258+
err = store.UpdateLoopIn(
259+
ctxb, hash, testTime,
260+
SwapStateData{
261+
State: StatePreimageRevealed,
262+
},
263+
)
264+
require.NoError(t, err)
265+
266+
checkSwap(StatePreimageRevealed)
267+
268+
// Next, we'll update to the final state to ensure that the state is
269+
// properly updated.
270+
err = store.UpdateLoopIn(
271+
ctxb, hash, testTime,
272+
SwapStateData{
273+
State: StateFailInsufficientValue,
274+
},
275+
)
276+
require.NoError(t, err)
277+
checkSwap(StateFailInsufficientValue)
278+
279+
err = store.Close()
280+
require.NoError(t, err)
281+
}
282+
283+
// TestLiquidityParams checks that reading and writing to liquidty bucket are
284+
// as expected.
285+
func TestSqliteLiquidityParams(t *testing.T) {
286+
ctxb := context.Background()
287+
288+
store := NewTestDB(t)
289+
290+
// Test when there's no params saved before, an empty bytes is
291+
// returned.
292+
params, err := store.FetchLiquidityParams(ctxb)
293+
require.NoError(t, err, "failed to fetch params")
294+
require.Empty(t, params, "expect empty bytes")
295+
require.Nil(t, params, "expected nil byte array")
296+
297+
params = []byte("test")
298+
299+
// Test we can save the params.
300+
err = store.PutLiquidityParams(ctxb, params)
301+
require.NoError(t, err, "failed to put params")
302+
303+
// Now fetch the db again should return the above saved bytes.
304+
paramsRead, err := store.FetchLiquidityParams(ctxb)
305+
require.NoError(t, err, "failed to fetch params")
306+
require.Equal(t, params, paramsRead, "unexpected return value")
307+
}
308+
309+
// TestSqliteTypeConversion is a small test that checks that we can safely
310+
// convert between the :one and :many types from sqlc.
311+
func TestSqliteTypeConversion(t *testing.T) {
312+
loopOutSwapRow := sqlc.GetLoopOutSwapRow{}
313+
randomStruct(&loopOutSwapRow)
314+
require.NotNil(t, loopOutSwapRow.DestAddress)
315+
316+
loopOutSwapsRow := sqlc.GetLoopOutSwapsRow(loopOutSwapRow)
317+
require.EqualValues(t, loopOutSwapRow, loopOutSwapsRow)
318+
319+
}
320+
321+
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
322+
323+
func randomString(length int) string {
324+
b := make([]byte, length)
325+
for i := range b {
326+
b[i] = charset[rand.Intn(len(charset))]
327+
}
328+
return string(b)
329+
}
330+
331+
func randomBytes(length int) []byte {
332+
b := make([]byte, length)
333+
for i := range b {
334+
b[i] = byte(rand.Intn(256))
335+
}
336+
return b
337+
}
338+
339+
func randomStruct(v interface{}) error {
340+
val := reflect.ValueOf(v)
341+
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
342+
return errors.New("Input should be a pointer to a struct type")
343+
}
344+
345+
val = val.Elem()
346+
for i := 0; i < val.NumField(); i++ {
347+
field := val.Field(i)
348+
349+
switch field.Kind() {
350+
case reflect.Int64:
351+
if field.CanSet() {
352+
field.SetInt(rand.Int63())
353+
}
354+
355+
case reflect.String:
356+
if field.CanSet() {
357+
field.SetString(randomString(10))
358+
}
359+
360+
case reflect.Slice:
361+
if field.Type().Elem().Kind() == reflect.Uint8 {
362+
if field.CanSet() {
363+
field.SetBytes(randomBytes(32))
364+
}
365+
}
366+
367+
case reflect.Struct:
368+
if field.Type() == reflect.TypeOf(time.Time{}) {
369+
if field.CanSet() {
370+
field.Set(reflect.ValueOf(time.Now()))
371+
}
372+
}
373+
if field.Type() == reflect.TypeOf(route.Vertex{}) {
374+
if field.CanSet() {
375+
vertex, err := route.NewVertexFromBytes(
376+
randomBytes(route.VertexSize),
377+
)
378+
if err != nil {
379+
return err
380+
}
381+
382+
field.Set(reflect.ValueOf(vertex))
383+
}
384+
}
385+
}
386+
}
387+
388+
return nil
389+
}

0 commit comments

Comments
 (0)