From 753bce7a2aa03a960614a3a9536fb11b7b63f3dc Mon Sep 17 00:00:00 2001 From: fortytw2 Date: Wed, 24 Oct 2018 13:57:25 -0500 Subject: [PATCH 1/3] sketching e2e test pools on the airplane --- e2e/e2e_test.go | 68 +++++++++++++++++++++++++++++++++++++++++ e2e/server_pool.go | 60 ++++++++++++++++++++++++++++++++++++ e2e/setup_e2e_server.go | 39 +++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 e2e/e2e_test.go create mode 100644 e2e/server_pool.go create mode 100644 e2e/setup_e2e_server.go diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go new file mode 100644 index 0000000..b576ead --- /dev/null +++ b/e2e/e2e_test.go @@ -0,0 +1,68 @@ +//+build integration + +package e2e + +import ( + "fmt" + "net/http" + "os" + "strconv" + "sync" + "testing" + + "github.com/fortytw2/hydrocarbon" + "github.com/fortytw2/hydrocarbon/pg" +) + +var cases = []struct { + Name string + Execute func(*chromedp.Chrome, addr string) error + Verify func(*chromedp.Chrome, addr string, db *pg.DB, mm *hydrocarbon.MockMailer) error +}{ + { + // TODO: test cases go here + }, +} + +func TestEndToEnd(t *testing.T) { + + instances := 1 + if val, ok := os.LookupEnv("HYDROCARBON_E2E_PARALLELISM"); ok { + instances, err = strconv.Atoi(val) + if err != nil { + t.Fatal(err) + } + } + + pool := newTestServerPool(instances) + chromePool := chromedp.NewPool(instances) + + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + t.Parallel() + + s, release := pool.getServer() + defer release() + + chrome, chromeRelease := chromePool.getServer() + defer chromeRelease() + + if c.Execute != nil { + err := c.Execute(chrome, s.addr) + if err != nil { + t.Fatal(err) + } + } + + if c.Verify != nil { + + err := c.Verify(chrome, s.addr, s.db, s.mm) + if err != nil { + t.Fatal(err) + } + } + + s.db.TruncateTables() + }) + } +} diff --git a/e2e/server_pool.go b/e2e/server_pool.go new file mode 100644 index 0000000..25bb5b6 --- /dev/null +++ b/e2e/server_pool.go @@ -0,0 +1,60 @@ +package e2e + +import ( + "fmt" + "net/http" + + "github.com/fortytw2/hydrocarbon" + "github.com/fortytw2/hydrocarbon/pg" +) + +type testServer struct { + addr string + s http.Server + db *pg.DB + mm *hydrocarbon.MockMailer +} + +type testServerPool struct { + servers chan *testServer +} + +func newTestServerPool(instances int) *testServerPool { + var addrs []string + for index := 0; index < instances; index++ { + addrs = append(addrs, fmt.Sprintf(":690%d", index)) + } + + var servers []testServer + for _, addr := range addrs { + fullAddr := fmt.Sprintf("http://localhost%s", addr) + + server, db, mockMailer, cancel := SetupE2EServer(t, fullAddr) + defer cancel() + + servers = append(servers, testServer{ + addr: fullAddr, + s: server, + db: db, + mm: mockMailer, + }) + + go http.ListenAndServe(addr, server) + } + + serverChan := make(chan *testServer, len(servers)) + for _, s := range servers { + serverChan <- s + } + + return &testServerPool{ + servers: serverChan, + } +} + +func (t *testServerPool) getServer() (*testServer, func()) { + s := <-t.servers + return s, func() { + t.servers <- s + } +} diff --git a/e2e/setup_e2e_server.go b/e2e/setup_e2e_server.go new file mode 100644 index 0000000..66048cd --- /dev/null +++ b/e2e/setup_e2e_server.go @@ -0,0 +1,39 @@ +//+build integration + +package e2e + +import ( + "net/http" + "testing" + + "github.com/fortytw2/hydrocarbon" + "github.com/fortytw2/hydrocarbon/discollect" +) + +func SetupE2EServer(t *testing.T, address string) (http.Server, *pg.DB, *hydrocarbon.MockMailer, func()) { + t.Helper() + + db, cancel := pg.SetupTestDB() + + dc, _ := discollect.New(discollect.WithPlugins(&discollect.Plugin{ + Name: "ycombinators", + Entrypoints: []string{".*"}, + ConfigCreator: func(url string, ho *discollect.HandlerOpts) (string, *discollect.Config, error) { + return "gotem", &discollect.Config{ + Type: discollect.FullScrape, + Entrypoints: []string{"gotem"}, + }, nil + }, + })) + + mm := &hydrocarbon.MockMailer{} + ks := hydrocarbon.NewKeySigner("test") + h := hydrocarbon.NewRouter( + hydrocarbon.NewUserAPI(db, ks, mm, "", "", false), + hydrocarbon.NewFeedAPI(db, dc, ks), + hydrocarbon.NewReadStatusAPI(db, ks), + address, + ) + + return h, db, mm, cancel +} From d26a225de357cab2cb21f860787cfefd9f7b3998 Mon Sep 17 00:00:00 2001 From: fortytw2 Date: Wed, 24 Oct 2018 14:01:11 -0500 Subject: [PATCH 2/3] build tag e2e and clean up the interface --- e2e/e2e_test.go | 18 +++++------------- e2e/server_pool.go | 2 ++ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index b576ead..1c0f041 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -16,8 +16,7 @@ import ( var cases = []struct { Name string - Execute func(*chromedp.Chrome, addr string) error - Verify func(*chromedp.Chrome, addr string, db *pg.DB, mm *hydrocarbon.MockMailer) error + Run func(*chromedp.Chrome, addr string, db *pg.DB, mm *hydrocarbon.MockMailer) error }{ { // TODO: test cases go here @@ -25,7 +24,6 @@ var cases = []struct { } func TestEndToEnd(t *testing.T) { - instances := 1 if val, ok := os.LookupEnv("HYDROCARBON_E2E_PARALLELISM"); ok { instances, err = strconv.Atoi(val) @@ -39,24 +37,18 @@ func TestEndToEnd(t *testing.T) { for _, c := range cases { t.Run(c.Name, func(t *testing.T) { + // TODO(fortytw2): does this work right? t.Parallel() + // this will block if there are no free instances s, release := pool.getServer() defer release() chrome, chromeRelease := chromePool.getServer() defer chromeRelease() - if c.Execute != nil { - err := c.Execute(chrome, s.addr) - if err != nil { - t.Fatal(err) - } - } - - if c.Verify != nil { - - err := c.Verify(chrome, s.addr, s.db, s.mm) + if c.Run != nil { + err := c.Run(chrome, s.addr, s.db, s.mm) if err != nil { t.Fatal(err) } diff --git a/e2e/server_pool.go b/e2e/server_pool.go index 25bb5b6..dac3d57 100644 --- a/e2e/server_pool.go +++ b/e2e/server_pool.go @@ -1,3 +1,5 @@ +//+build integration + package e2e import ( From b4dff384a57efc6bd8cfb7a1eaabb58358d655f1 Mon Sep 17 00:00:00 2001 From: fortytw2 Date: Wed, 24 Oct 2018 14:05:24 -0500 Subject: [PATCH 3/3] committing until I have internet to fetch deps --- e2e/e2e_test.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 1c0f041..6b4664b 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -3,33 +3,36 @@ package e2e import ( - "fmt" - "net/http" "os" + "runtime" "strconv" - "sync" "testing" "github.com/fortytw2/hydrocarbon" "github.com/fortytw2/hydrocarbon/pg" ) -var cases = []struct { - Name string - Run func(*chromedp.Chrome, addr string, db *pg.DB, mm *hydrocarbon.MockMailer) error -}{ +type E2ETestCase struct { + Name string + Run func(browser *chromedp.Chrome, addr string, db *pg.DB, mm *hydrocarbon.MockMailer) error +} + +var cases = []E2ETestCase{ { // TODO: test cases go here }, } func TestEndToEnd(t *testing.T) { - instances := 1 - if val, ok := os.LookupEnv("HYDROCARBON_E2E_PARALLELISM"); ok { + var instances int + if val, ok := os.LookupEnv("E2E_PARALLELISM"); ok { instances, err = strconv.Atoi(val) if err != nil { t.Fatal(err) } + } else { + // this should be a sensible default + instances = runtime.NumCPU() / 2 } pool := newTestServerPool(instances) @@ -37,14 +40,14 @@ func TestEndToEnd(t *testing.T) { for _, c := range cases { t.Run(c.Name, func(t *testing.T) { - // TODO(fortytw2): does this work right? + // TODO(fortytw2): does this work right with subtests t.Parallel() // this will block if there are no free instances s, release := pool.getServer() defer release() - chrome, chromeRelease := chromePool.getServer() + chrome, chromeRelease := chromePool.GetInstance() defer chromeRelease() if c.Run != nil {