Path: blob/main/components/common-go/baseserver/testing.go
2498 views
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package baseserver56import (7"context"8"fmt"9"net/http"10"strings"11"testing"12"time"1314"github.com/stretchr/testify/require"15)1617// NewForTests constructs a *baseserver.Server which is automatically closed after the test finishes.18func NewForTests(t *testing.T, opts ...Option) *Server {19t.Helper()2021defaultTestOpts := []Option{22WithUnderTest(),23WithCloseTimeout(1 * time.Second),24}2526// specified opts override our defaults27srv, err := New("test_server", append(defaultTestOpts, opts...)...)28require.NoError(t, err)2930t.Cleanup(func() {31require.NoError(t, srv.Close())32})3334return srv35}3637func MustUseRandomLocalAddress(t *testing.T) *ServerConfiguration {38t.Helper()3940return &ServerConfiguration{41Address: "localhost:0",42}43}4445// StartServerForTests starts the server for test purposes.46// This is a helper which also ensures the server is reachable before returning.47func StartServerForTests(t *testing.T, srv *Server) {48t.Helper()4950go func() {51retry := 052for ; retry <= 3; retry++ {53err := srv.ListenAndServe()5455// TODO(gpl) This is a bandaid, because we are experiencing build reliability issues.56// ":0" should trigger the kernel you choose a free port for us, but somehow this fails. Google points to57// potential recent kernel bug or tcp4 vs. tcp6 (network stack config) problems.58// To not waste more energy debugging our build setup her and now, this bandaid to re-try.59// NOTE: If you ask for a specific port (not ":0"), the test still fails60if strings.Contains(err.Error(), ":0: bind: address already in use") {61time.Sleep(time.Millisecond * 200)62continue63}6465require.NoError(t, err)66return67}68t.Errorf("Cannot bind to %s after %d retries", srv.options.config.Services.HTTP.Address, retry)69}()7071waitForServerToBeReachable(t, srv, 3*time.Second)72}7374func waitForServerToBeReachable(t *testing.T, srv *Server, timeout time.Duration) {75ctx, cancel := context.WithTimeout(context.Background(), timeout)76defer cancel()7778tick := 100 * time.Millisecond79ticker := time.NewTicker(tick)80defer ticker.Stop()8182client := &http.Client{83Timeout: tick,84}8586for {87healthURL := fmt.Sprintf("%s/ready", srv.HealthAddr())8889select {90case <-ctx.Done():91t.Fatalf("server did not become reachable in %s on %s", timeout.String(), healthURL)92case <-ticker.C:93// We retrieve the URL on each tick, because the HTTPAddress is only available once the server is listening.94_, err := client.Get(healthURL)95if err != nil {96continue97}9899// any response means we've managed to reach the server100return101}102}103}104105106