Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/common-go/baseserver/testing.go
2498 views
1
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package baseserver
6
7
import (
8
"context"
9
"fmt"
10
"net/http"
11
"strings"
12
"testing"
13
"time"
14
15
"github.com/stretchr/testify/require"
16
)
17
18
// NewForTests constructs a *baseserver.Server which is automatically closed after the test finishes.
19
func NewForTests(t *testing.T, opts ...Option) *Server {
20
t.Helper()
21
22
defaultTestOpts := []Option{
23
WithUnderTest(),
24
WithCloseTimeout(1 * time.Second),
25
}
26
27
// specified opts override our defaults
28
srv, err := New("test_server", append(defaultTestOpts, opts...)...)
29
require.NoError(t, err)
30
31
t.Cleanup(func() {
32
require.NoError(t, srv.Close())
33
})
34
35
return srv
36
}
37
38
func MustUseRandomLocalAddress(t *testing.T) *ServerConfiguration {
39
t.Helper()
40
41
return &ServerConfiguration{
42
Address: "localhost:0",
43
}
44
}
45
46
// StartServerForTests starts the server for test purposes.
47
// This is a helper which also ensures the server is reachable before returning.
48
func StartServerForTests(t *testing.T, srv *Server) {
49
t.Helper()
50
51
go func() {
52
retry := 0
53
for ; retry <= 3; retry++ {
54
err := srv.ListenAndServe()
55
56
// TODO(gpl) This is a bandaid, because we are experiencing build reliability issues.
57
// ":0" should trigger the kernel you choose a free port for us, but somehow this fails. Google points to
58
// potential recent kernel bug or tcp4 vs. tcp6 (network stack config) problems.
59
// To not waste more energy debugging our build setup her and now, this bandaid to re-try.
60
// NOTE: If you ask for a specific port (not ":0"), the test still fails
61
if strings.Contains(err.Error(), ":0: bind: address already in use") {
62
time.Sleep(time.Millisecond * 200)
63
continue
64
}
65
66
require.NoError(t, err)
67
return
68
}
69
t.Errorf("Cannot bind to %s after %d retries", srv.options.config.Services.HTTP.Address, retry)
70
}()
71
72
waitForServerToBeReachable(t, srv, 3*time.Second)
73
}
74
75
func waitForServerToBeReachable(t *testing.T, srv *Server, timeout time.Duration) {
76
ctx, cancel := context.WithTimeout(context.Background(), timeout)
77
defer cancel()
78
79
tick := 100 * time.Millisecond
80
ticker := time.NewTicker(tick)
81
defer ticker.Stop()
82
83
client := &http.Client{
84
Timeout: tick,
85
}
86
87
for {
88
healthURL := fmt.Sprintf("%s/ready", srv.HealthAddr())
89
90
select {
91
case <-ctx.Done():
92
t.Fatalf("server did not become reachable in %s on %s", timeout.String(), healthURL)
93
case <-ticker.C:
94
// We retrieve the URL on each tick, because the HTTPAddress is only available once the server is listening.
95
_, err := client.Get(healthURL)
96
if err != nil {
97
continue
98
}
99
100
// any response means we've managed to reach the server
101
return
102
}
103
}
104
}
105
106