itest: refactor WS tests into functions
This commit is contained in:
parent
5df69cf93f
commit
066472fd3e
@ -47,6 +47,9 @@ var (
|
|||||||
TLSClientConfig: insecureTransport.TLSClientConfig,
|
TLSClientConfig: insecureTransport.TLSClientConfig,
|
||||||
}
|
}
|
||||||
resultPattern = regexp.MustCompile("{\"result\":(.*)}")
|
resultPattern = regexp.MustCompile("{\"result\":(.*)}")
|
||||||
|
closeMsg = websocket.FormatCloseMessage(
|
||||||
|
websocket.CloseNormalClosure, "done",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// testRestAPI tests that the most important features of the REST API work
|
// testRestAPI tests that the most important features of the REST API work
|
||||||
@ -185,204 +188,16 @@ func testRestAPI(net *lntest.NetworkHarness, ht *harnessTest) {
|
|||||||
)
|
)
|
||||||
assert.Equal(t, 0, len(body))
|
assert.Equal(t, 0, len(body))
|
||||||
},
|
},
|
||||||
}, {
|
}}
|
||||||
|
wsTestCases := []struct {
|
||||||
|
name string
|
||||||
|
run func(ht *harnessTest, net *lntest.NetworkHarness)
|
||||||
|
}{{
|
||||||
name: "websocket subscription",
|
name: "websocket subscription",
|
||||||
run: func(t *testing.T, a, b *lntest.HarnessNode) {
|
run: wsTestCaseSubscription,
|
||||||
// Find out the current best block so we can subscribe
|
|
||||||
// to the next one.
|
|
||||||
hash, height, err := net.Miner.Client.GetBestBlock()
|
|
||||||
require.Nil(t, err, "get best block")
|
|
||||||
|
|
||||||
// Create a new subscription to get block epoch events.
|
|
||||||
req := &chainrpc.BlockEpoch{
|
|
||||||
Hash: hash.CloneBytes(),
|
|
||||||
Height: uint32(height),
|
|
||||||
}
|
|
||||||
url := "/v2/chainnotifier/register/blocks"
|
|
||||||
c, err := openWebSocket(a, url, "POST", req, nil)
|
|
||||||
require.Nil(t, err, "websocket")
|
|
||||||
defer func() {
|
|
||||||
_ = c.WriteMessage(
|
|
||||||
websocket.CloseMessage,
|
|
||||||
websocket.FormatCloseMessage(
|
|
||||||
websocket.CloseNormalClosure,
|
|
||||||
"done",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_ = c.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
msgChan := make(chan *chainrpc.BlockEpoch)
|
|
||||||
errChan := make(chan error)
|
|
||||||
timeout := time.After(defaultTimeout)
|
|
||||||
|
|
||||||
// We want to read exactly one message.
|
|
||||||
go func() {
|
|
||||||
defer close(msgChan)
|
|
||||||
|
|
||||||
_, msg, err := c.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The chunked/streamed responses come wrapped
|
|
||||||
// in either a {"result":{}} or {"error":{}}
|
|
||||||
// wrapper which we'll get rid of here.
|
|
||||||
msgStr := string(msg)
|
|
||||||
if !strings.Contains(msgStr, "\"result\":") {
|
|
||||||
errChan <- fmt.Errorf("invalid msg: %s",
|
|
||||||
msgStr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msgStr = resultPattern.ReplaceAllString(
|
|
||||||
msgStr, "${1}",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Make sure we can parse the unwrapped message
|
|
||||||
// into the expected proto message.
|
|
||||||
protoMsg := &chainrpc.BlockEpoch{}
|
|
||||||
err = jsonpb.UnmarshalString(
|
|
||||||
msgStr, protoMsg,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case msgChan <- protoMsg:
|
|
||||||
case <-timeout:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Mine a block and make sure we get a message for it.
|
|
||||||
blockHashes, err := net.Miner.Client.Generate(1)
|
|
||||||
require.Nil(t, err, "generate blocks")
|
|
||||||
assert.Equal(t, 1, len(blockHashes), "num blocks")
|
|
||||||
select {
|
|
||||||
case msg := <-msgChan:
|
|
||||||
assert.Equal(
|
|
||||||
t, blockHashes[0].CloneBytes(),
|
|
||||||
msg.Hash, "block hash",
|
|
||||||
)
|
|
||||||
|
|
||||||
case err := <-errChan:
|
|
||||||
t.Fatalf("Received error from WS: %v", err)
|
|
||||||
|
|
||||||
case <-timeout:
|
|
||||||
t.Fatalf("Timeout before message was received")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
name: "websocket subscription with macaroon in protocol",
|
name: "websocket subscription with macaroon in protocol",
|
||||||
run: func(t *testing.T, a, b *lntest.HarnessNode) {
|
run: wsTestCaseSubscriptionMacaroon,
|
||||||
// Find out the current best block so we can subscribe
|
|
||||||
// to the next one.
|
|
||||||
hash, height, err := net.Miner.Client.GetBestBlock()
|
|
||||||
require.Nil(t, err, "get best block")
|
|
||||||
|
|
||||||
// Create a new subscription to get block epoch events.
|
|
||||||
req := &chainrpc.BlockEpoch{
|
|
||||||
Hash: hash.CloneBytes(),
|
|
||||||
Height: uint32(height),
|
|
||||||
}
|
|
||||||
url := "/v2/chainnotifier/register/blocks"
|
|
||||||
|
|
||||||
// This time we send the macaroon in the special header
|
|
||||||
// Sec-Websocket-Protocol which is the only header field
|
|
||||||
// available to browsers when opening a WebSocket.
|
|
||||||
mac, err := a.ReadMacaroon(
|
|
||||||
a.AdminMacPath(), defaultTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(t, err, "read admin mac")
|
|
||||||
macBytes, err := mac.MarshalBinary()
|
|
||||||
require.NoError(t, err, "marshal admin mac")
|
|
||||||
|
|
||||||
customHeader := make(http.Header)
|
|
||||||
customHeader.Set(
|
|
||||||
lnrpc.HeaderWebSocketProtocol, fmt.Sprintf(
|
|
||||||
"Grpc-Metadata-Macaroon+%s",
|
|
||||||
hex.EncodeToString(macBytes),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
c, err := openWebSocket(
|
|
||||||
a, url, "POST", req, customHeader,
|
|
||||||
)
|
|
||||||
require.Nil(t, err, "websocket")
|
|
||||||
defer func() {
|
|
||||||
_ = c.WriteMessage(
|
|
||||||
websocket.CloseMessage,
|
|
||||||
websocket.FormatCloseMessage(
|
|
||||||
websocket.CloseNormalClosure,
|
|
||||||
"done",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_ = c.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
msgChan := make(chan *chainrpc.BlockEpoch)
|
|
||||||
errChan := make(chan error)
|
|
||||||
timeout := time.After(defaultTimeout)
|
|
||||||
|
|
||||||
// We want to read exactly one message.
|
|
||||||
go func() {
|
|
||||||
defer close(msgChan)
|
|
||||||
|
|
||||||
_, msg, err := c.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The chunked/streamed responses come wrapped
|
|
||||||
// in either a {"result":{}} or {"error":{}}
|
|
||||||
// wrapper which we'll get rid of here.
|
|
||||||
msgStr := string(msg)
|
|
||||||
if !strings.Contains(msgStr, "\"result\":") {
|
|
||||||
errChan <- fmt.Errorf("invalid msg: %s",
|
|
||||||
msgStr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msgStr = resultPattern.ReplaceAllString(
|
|
||||||
msgStr, "${1}",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Make sure we can parse the unwrapped message
|
|
||||||
// into the expected proto message.
|
|
||||||
protoMsg := &chainrpc.BlockEpoch{}
|
|
||||||
err = jsonpb.UnmarshalString(
|
|
||||||
msgStr, protoMsg,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
errChan <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case msgChan <- protoMsg:
|
|
||||||
case <-timeout:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Mine a block and make sure we get a message for it.
|
|
||||||
blockHashes, err := net.Miner.Client.Generate(1)
|
|
||||||
require.Nil(t, err, "generate blocks")
|
|
||||||
assert.Equal(t, 1, len(blockHashes), "num blocks")
|
|
||||||
select {
|
|
||||||
case msg := <-msgChan:
|
|
||||||
assert.Equal(
|
|
||||||
t, blockHashes[0].CloneBytes(),
|
|
||||||
msg.Hash, "block hash",
|
|
||||||
)
|
|
||||||
|
|
||||||
case err := <-errChan:
|
|
||||||
t.Fatalf("Received error from WS: %v", err)
|
|
||||||
|
|
||||||
case <-timeout:
|
|
||||||
t.Fatalf("Timeout before message was received")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// Make sure Alice allows all CORS origins. Bob will keep the default.
|
// Make sure Alice allows all CORS origins. Bob will keep the default.
|
||||||
@ -401,6 +216,187 @@ func testRestAPI(net *lntest.NetworkHarness, ht *harnessTest) {
|
|||||||
tc.run(t, net.Alice, net.Bob)
|
tc.run(t, net.Alice, net.Bob)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, tc := range wsTestCases {
|
||||||
|
tc := tc
|
||||||
|
ht.t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ht := &harnessTest{
|
||||||
|
t: t, testCase: ht.testCase, lndHarness: net,
|
||||||
|
}
|
||||||
|
tc.run(ht, net)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsTestCaseSubscription(ht *harnessTest, net *lntest.NetworkHarness) {
|
||||||
|
// Find out the current best block so we can subscribe to the next one.
|
||||||
|
hash, height, err := net.Miner.Client.GetBestBlock()
|
||||||
|
require.Nil(ht.t, err, "get best block")
|
||||||
|
|
||||||
|
// Create a new subscription to get block epoch events.
|
||||||
|
req := &chainrpc.BlockEpoch{
|
||||||
|
Hash: hash.CloneBytes(),
|
||||||
|
Height: uint32(height),
|
||||||
|
}
|
||||||
|
url := "/v2/chainnotifier/register/blocks"
|
||||||
|
c, err := openWebSocket(net.Alice, url, "POST", req, nil)
|
||||||
|
require.Nil(ht.t, err, "websocket")
|
||||||
|
defer func() {
|
||||||
|
err := c.WriteMessage(websocket.CloseMessage, closeMsg)
|
||||||
|
require.NoError(ht.t, err)
|
||||||
|
_ = c.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
msgChan := make(chan *chainrpc.BlockEpoch)
|
||||||
|
errChan := make(chan error)
|
||||||
|
timeout := time.After(defaultTimeout)
|
||||||
|
|
||||||
|
// We want to read exactly one message.
|
||||||
|
go func() {
|
||||||
|
defer close(msgChan)
|
||||||
|
|
||||||
|
_, msg, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The chunked/streamed responses come wrapped in either a
|
||||||
|
// {"result":{}} or {"error":{}} wrapper which we'll get rid of
|
||||||
|
// here.
|
||||||
|
msgStr := string(msg)
|
||||||
|
if !strings.Contains(msgStr, "\"result\":") {
|
||||||
|
errChan <- fmt.Errorf("invalid msg: %s", msgStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msgStr = resultPattern.ReplaceAllString(msgStr, "${1}")
|
||||||
|
|
||||||
|
// Make sure we can parse the unwrapped message into the
|
||||||
|
// expected proto message.
|
||||||
|
protoMsg := &chainrpc.BlockEpoch{}
|
||||||
|
err = jsonpb.UnmarshalString(msgStr, protoMsg)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msgChan <- protoMsg:
|
||||||
|
case <-timeout:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Mine a block and make sure we get a message for it.
|
||||||
|
blockHashes, err := net.Miner.Client.Generate(1)
|
||||||
|
require.Nil(ht.t, err, "generate blocks")
|
||||||
|
assert.Equal(ht.t, 1, len(blockHashes), "num blocks")
|
||||||
|
select {
|
||||||
|
case msg := <-msgChan:
|
||||||
|
assert.Equal(
|
||||||
|
ht.t, blockHashes[0].CloneBytes(), msg.Hash,
|
||||||
|
"block hash",
|
||||||
|
)
|
||||||
|
|
||||||
|
case err := <-errChan:
|
||||||
|
ht.t.Fatalf("Received error from WS: %v", err)
|
||||||
|
|
||||||
|
case <-timeout:
|
||||||
|
ht.t.Fatalf("Timeout before message was received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsTestCaseSubscriptionMacaroon(ht *harnessTest,
|
||||||
|
net *lntest.NetworkHarness) {
|
||||||
|
|
||||||
|
// Find out the current best block so we can subscribe to the next one.
|
||||||
|
hash, height, err := net.Miner.Client.GetBestBlock()
|
||||||
|
require.Nil(ht.t, err, "get best block")
|
||||||
|
|
||||||
|
// Create a new subscription to get block epoch events.
|
||||||
|
req := &chainrpc.BlockEpoch{
|
||||||
|
Hash: hash.CloneBytes(),
|
||||||
|
Height: uint32(height),
|
||||||
|
}
|
||||||
|
url := "/v2/chainnotifier/register/blocks"
|
||||||
|
|
||||||
|
// This time we send the macaroon in the special header
|
||||||
|
// Sec-Websocket-Protocol which is the only header field available to
|
||||||
|
// browsers when opening a WebSocket.
|
||||||
|
mac, err := net.Alice.ReadMacaroon(
|
||||||
|
net.Alice.AdminMacPath(), defaultTimeout,
|
||||||
|
)
|
||||||
|
require.NoError(ht.t, err, "read admin mac")
|
||||||
|
macBytes, err := mac.MarshalBinary()
|
||||||
|
require.NoError(ht.t, err, "marshal admin mac")
|
||||||
|
|
||||||
|
customHeader := make(http.Header)
|
||||||
|
customHeader.Set(lnrpc.HeaderWebSocketProtocol, fmt.Sprintf(
|
||||||
|
"Grpc-Metadata-Macaroon+%s", hex.EncodeToString(macBytes),
|
||||||
|
))
|
||||||
|
c, err := openWebSocket(net.Alice, url, "POST", req, customHeader)
|
||||||
|
require.Nil(ht.t, err, "websocket")
|
||||||
|
defer func() {
|
||||||
|
err := c.WriteMessage(websocket.CloseMessage, closeMsg)
|
||||||
|
require.NoError(ht.t, err)
|
||||||
|
_ = c.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
msgChan := make(chan *chainrpc.BlockEpoch)
|
||||||
|
errChan := make(chan error)
|
||||||
|
timeout := time.After(defaultTimeout)
|
||||||
|
|
||||||
|
// We want to read exactly one message.
|
||||||
|
go func() {
|
||||||
|
defer close(msgChan)
|
||||||
|
|
||||||
|
_, msg, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The chunked/streamed responses come wrapped in either a
|
||||||
|
// {"result":{}} or {"error":{}} wrapper which we'll get rid of
|
||||||
|
// here.
|
||||||
|
msgStr := string(msg)
|
||||||
|
if !strings.Contains(msgStr, "\"result\":") {
|
||||||
|
errChan <- fmt.Errorf("invalid msg: %s", msgStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msgStr = resultPattern.ReplaceAllString(msgStr, "${1}")
|
||||||
|
|
||||||
|
// Make sure we can parse the unwrapped message into the
|
||||||
|
// expected proto message.
|
||||||
|
protoMsg := &chainrpc.BlockEpoch{}
|
||||||
|
err = jsonpb.UnmarshalString(msgStr, protoMsg)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msgChan <- protoMsg:
|
||||||
|
case <-timeout:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Mine a block and make sure we get a message for it.
|
||||||
|
blockHashes, err := net.Miner.Client.Generate(1)
|
||||||
|
require.Nil(ht.t, err, "generate blocks")
|
||||||
|
assert.Equal(ht.t, 1, len(blockHashes), "num blocks")
|
||||||
|
select {
|
||||||
|
case msg := <-msgChan:
|
||||||
|
assert.Equal(
|
||||||
|
ht.t, blockHashes[0].CloneBytes(), msg.Hash,
|
||||||
|
"block hash",
|
||||||
|
)
|
||||||
|
|
||||||
|
case err := <-errChan:
|
||||||
|
ht.t.Fatalf("Received error from WS: %v", err)
|
||||||
|
|
||||||
|
case <-timeout:
|
||||||
|
ht.t.Fatalf("Timeout before message was received")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// invokeGET calls the given URL with the GET method and appropriate macaroon
|
// invokeGET calls the given URL with the GET method and appropriate macaroon
|
||||||
|
Loading…
Reference in New Issue
Block a user