status-go/jail/cell_test.go

256 lines
6.0 KiB
Go

package jail
import (
"fmt"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/robertkrimen/otto"
"github.com/stretchr/testify/suite"
)
func TestCellTestSuite(t *testing.T) {
suite.Run(t, new(CellTestSuite))
}
type CellTestSuite struct {
suite.Suite
cell *Cell
}
func (s *CellTestSuite) SetupTest() {
cell, err := NewCell("testCell1")
s.NoError(err)
s.NotNil(cell)
s.cell = cell
}
func (s *CellTestSuite) TearDownTest() {
err := s.cell.Stop()
s.NoError(err)
}
func (s *CellTestSuite) TestCellRegisteredHandlers() {
_, err := s.cell.Run(`setTimeout(function(){}, 100)`)
s.NoError(err)
_, err = s.cell.Run(`fetch`)
s.NoError(err)
}
// TestJailLoopRace tests multiple setTimeout callbacks,
// supposed to be run with '-race' flag.
func (s *CellTestSuite) TestCellLoopRace() {
cell := s.cell
items := make(chan struct{})
err := cell.Set("__captureResponse", func() otto.Value {
items <- struct{}{}
return otto.UndefinedValue()
})
s.NoError(err)
_, err = cell.Run(`
function callRunner(){
return setTimeout(function(){
__captureResponse();
}, 200);
}
`)
s.NoError(err)
for i := 0; i < 100; i++ {
_, err = cell.Call("callRunner", nil)
s.NoError(err)
}
for i := 0; i < 100; i++ {
select {
case <-items:
case <-time.After(400 * time.Millisecond):
s.Fail("test timed out")
}
}
}
// TestJailFetchRace tests multiple sending multiple HTTP requests simultaneously in one cell, using `fetch`.
// Supposed to be run with '-race' flag.
func (s *CellTestSuite) TestCellFetchRace() {
// How many request should the test perform ?
const requestCount = 5
// Create a test server that simply outputs the "i" parameter passed.
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
w.Write([]byte(r.URL.Query()["i"][0])) //nolint: errcheck
}))
defer server.Close()
cell := s.cell
dataCh := make(chan otto.Value, requestCount)
errCh := make(chan otto.Value, requestCount)
err := cell.Set("__captureSuccess", func(res otto.Value) { dataCh <- res })
s.NoError(err)
err = cell.Set("__captureError", func(res otto.Value) { errCh <- res })
s.NoError(err)
fetchCode := `fetch('%s?i=%d').then(function(r) {
return r.text()
}).then(function(data) {
__captureSuccess(data)
}).catch(function (e) {
__captureError(e)
})`
for i := 0; i < requestCount; i++ {
_, err = cell.Run(fmt.Sprintf(fetchCode, server.URL, i))
s.NoError(err)
}
expected := map[string]bool{} // It'll help us verify if every request was successfully completed.
for i := 0; i < requestCount; i++ {
select {
case data := <-dataCh:
// Mark the request as successful.
expected[data.String()] = true
case <-errCh:
s.Fail("fetch failed to complete the request")
case <-time.After(5 * time.Second):
s.Fail("test timed out")
return
}
}
// Make sure every request was completed successfully.
for i := 0; i < requestCount; i++ {
s.Equal(expected[fmt.Sprintf("%d", i)], true)
}
// There might be some tasks about to call `ready`,
// add a little delay before `TearDownTest` closes the loop.
time.Sleep(100 * time.Millisecond)
}
func (s *CellTestSuite) TestCellFetchErrorRace() {
cell := s.cell
dataCh := make(chan otto.Value, 1)
errCh := make(chan otto.Value, 1)
err := cell.Set("__captureSuccess", func(res otto.Value) { dataCh <- res })
s.NoError(err)
err = cell.Set("__captureError", func(res otto.Value) { errCh <- res })
s.NoError(err)
// Find a free port in localhost
freeportListener, err := net.Listen("tcp", "127.0.0.1:0")
s.Require().NoError(err)
defer func() {
err := freeportListener.Close()
s.NoError(err)
}()
// Send an HTTP request to the free port that we found above
_, err = cell.Run(fmt.Sprintf(`fetch('%s').then(function(r) {
return r.text()
}).then(function(data) {
__captureSuccess(data)
}).catch(function (e) {
__captureError(e)
})`, freeportListener.Addr().String()))
s.NoError(err)
select {
case <-dataCh:
s.Fail("fetch didn't return error for nonexistent url")
case e := <-errCh:
name, err := cell.GetObjectValue(e, "name")
s.NoError(err)
s.Equal("Error", name.Value().String())
_, err = cell.GetObjectValue(e, "message")
s.NoError(err)
case <-time.After(5 * time.Second):
s.Fail("test timed out")
return
}
}
// TestCellLoopCancel tests that cell.Stop() really cancels event
// loop and pending tasks.
func (s *CellTestSuite) TestCellLoopCancel() {
cell := s.cell
var err error
var count int
err = cell.Set("__captureResponse", func(call otto.FunctionCall) otto.Value {
count++
return otto.UndefinedValue()
})
s.NoError(err)
_, err = cell.Run(`
function callRunner(delay){
return setTimeout(function(){
__captureResponse();
}, delay);
}
`)
s.NoError(err)
// Run 5 timeout tasks to be executed in: 1, 2, 3, 4 and 5 secs
for i := 1; i <= 5; i++ {
_, err = cell.Call("callRunner", nil, i*1000)
s.NoError(err)
}
// Wait 1.5 second (so only one task executed) so far
// and stop the cell (event loop should die)
time.Sleep(1500 * time.Millisecond)
err = cell.Stop()
s.NoError(err)
// check that only 1 task has increased counter
s.Equal(1, count)
// wait 2 seconds more (so at least two more tasks would
// have been executed if event loop is still running)
<-time.After(2 * time.Second)
// check that counter hasn't increased
s.Equal(1, count)
}
func (s *CellTestSuite) TestCellCallAsync() {
// Don't use buffered channel as it's supposed to be an async call.
datac := make(chan string)
err := s.cell.Set("testCallAsync", func(call otto.FunctionCall) otto.Value {
datac <- call.Argument(0).String()
return otto.UndefinedValue()
})
s.NoError(err)
fn, err := s.cell.Get("testCallAsync")
s.NoError(err)
err = s.cell.CallAsync(fn.Value(), "success")
s.NoError(err)
s.Equal("success", <-datac)
}
func (s *CellTestSuite) TestCellCallStopMultipleTimes() {
s.NotPanics(func() {
err := s.cell.Stop()
s.NoError(err)
err = s.cell.Stop()
s.NoError(err)
})
}