weaviate
197 строк · 6.8 Кб
1// _ _
2// __ _____ __ ___ ___ __ _| |_ ___
3// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
4// \ V V / __/ (_| |\ V /| | (_| | || __/
5// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
6//
7// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
8//
9// CONTACT: hello@weaviate.io
10//
11
12package clients
13
14import (
15"context"
16"net/http"
17"net/http/httptest"
18"regexp"
19"strings"
20"testing"
21"time"
22
23"github.com/sirupsen/logrus"
24"github.com/sirupsen/logrus/hooks/test"
25"github.com/stretchr/testify/assert"
26"github.com/stretchr/testify/require"
27)
28
29func TestWaitForStartup(t *testing.T) {
30t.Run("when common server is immediately ready", func(t *testing.T) {
31server := httptest.NewServer(&testReadyHandler{t: t})
32defer server.Close()
33v := New(server.URL, server.URL, 0, nullLogger())
34err := v.WaitForStartup(context.Background(), 150*time.Millisecond)
35
36assert.Nil(t, err)
37})
38
39t.Run("when passage and query servers are immediately ready", func(t *testing.T) {
40serverPassage := httptest.NewServer(&testReadyHandler{t: t})
41serverQuery := httptest.NewServer(&testReadyHandler{t: t})
42defer serverPassage.Close()
43defer serverQuery.Close()
44v := New(serverPassage.URL, serverQuery.URL, 0, nullLogger())
45err := v.WaitForStartup(context.Background(), 150*time.Millisecond)
46
47assert.Nil(t, err)
48})
49
50t.Run("when common server is down", func(t *testing.T) {
51url := "http://nothing-running-at-this-url"
52v := New(url, url, 0, nullLogger())
53ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
54defer cancel()
55err := v.WaitForStartup(ctx, 50*time.Millisecond)
56
57require.NotNil(t, err, nullLogger())
58assert.Contains(t, err.Error(), "init context expired before remote was ready: send check ready request")
59assertContainsEither(t, err.Error(), "dial tcp", "context deadline exceeded")
60assert.NotContains(t, err.Error(), "[passage]")
61assert.NotContains(t, err.Error(), "[query]")
62})
63
64t.Run("when passage and query servers are down", func(t *testing.T) {
65urlPassage := "http://nothing-running-at-this-url"
66urlQuery := "http://nothing-running-at-this-url-either"
67v := New(urlPassage, urlQuery, 0, nullLogger())
68ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
69defer cancel()
70err := v.WaitForStartup(ctx, 50*time.Millisecond)
71
72require.NotNil(t, err, nullLogger())
73assert.Contains(t, err.Error(), "[passage] init context expired before remote was ready: send check ready request")
74assert.Contains(t, err.Error(), "[query] init context expired before remote was ready: send check ready request")
75assertContainsEither(t, err.Error(), "dial tcp", "context deadline exceeded")
76})
77
78t.Run("when common server is alive, but not ready", func(t *testing.T) {
79server := httptest.NewServer(&testReadyHandler{
80t: t,
81readyTime: time.Now().Add(time.Hour),
82})
83defer server.Close()
84v := New(server.URL, server.URL, 0, nullLogger())
85ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
86defer cancel()
87err := v.WaitForStartup(ctx, 50*time.Millisecond)
88
89require.NotNil(t, err)
90assert.Contains(t, err.Error(), "init context expired before remote was ready")
91assertContainsEither(t, err.Error(), "not ready: status 503", "context deadline exceeded")
92assert.NotContains(t, err.Error(), "[passage]")
93assert.NotContains(t, err.Error(), "[query]")
94})
95
96t.Run("when passage and query servers are alive, but not ready", func(t *testing.T) {
97rt := time.Now().Add(time.Hour)
98serverPassage := httptest.NewServer(&testReadyHandler{
99t: t,
100readyTime: rt,
101})
102serverQuery := httptest.NewServer(&testReadyHandler{
103t: t,
104readyTime: rt,
105})
106defer serverPassage.Close()
107defer serverQuery.Close()
108v := New(serverPassage.URL, serverQuery.URL, 0, nullLogger())
109ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
110defer cancel()
111err := v.WaitForStartup(ctx, 50*time.Millisecond)
112
113require.NotNil(t, err)
114assert.Contains(t, err.Error(), "[passage] init context expired before remote was ready")
115assert.Contains(t, err.Error(), "[query] init context expired before remote was ready")
116assertContainsEither(t, err.Error(), "not ready: status 503", "context deadline exceeded")
117})
118
119t.Run("when passage and query servers are alive, but query one is not ready", func(t *testing.T) {
120serverPassage := httptest.NewServer(&testReadyHandler{t: t})
121serverQuery := httptest.NewServer(&testReadyHandler{
122t: t,
123readyTime: time.Now().Add(1 * time.Minute),
124})
125defer serverPassage.Close()
126defer serverQuery.Close()
127v := New(serverPassage.URL, serverQuery.URL, 0, nullLogger())
128ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
129defer cancel()
130err := v.WaitForStartup(ctx, 50*time.Millisecond)
131
132require.NotNil(t, err)
133assert.Contains(t, err.Error(), "[query] init context expired before remote was ready")
134assertContainsEither(t, err.Error(), "not ready: status 503", "context deadline exceeded")
135assert.NotContains(t, err.Error(), "[passage]")
136})
137
138t.Run("when common server is initially not ready, but then becomes ready", func(t *testing.T) {
139server := httptest.NewServer(&testReadyHandler{
140t: t,
141readyTime: time.Now().Add(100 * time.Millisecond),
142})
143v := New(server.URL, server.URL, 0, nullLogger())
144defer server.Close()
145ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
146defer cancel()
147err := v.WaitForStartup(ctx, 50*time.Millisecond)
148
149require.Nil(t, err)
150})
151
152t.Run("when passage and query servers are initially not ready, but then become ready", func(t *testing.T) {
153serverPassage := httptest.NewServer(&testReadyHandler{
154t: t,
155readyTime: time.Now().Add(100 * time.Millisecond),
156})
157serverQuery := httptest.NewServer(&testReadyHandler{
158t: t,
159readyTime: time.Now().Add(150 * time.Millisecond),
160})
161defer serverPassage.Close()
162defer serverQuery.Close()
163v := New(serverPassage.URL, serverQuery.URL, 0, nullLogger())
164ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
165defer cancel()
166err := v.WaitForStartup(ctx, 50*time.Millisecond)
167
168require.Nil(t, err)
169})
170}
171
172type testReadyHandler struct {
173t *testing.T
174// the test handler will report as not ready before the time has passed
175readyTime time.Time
176}
177
178func (f *testReadyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
179assert.Equal(f.t, "/.well-known/ready", r.URL.String())
180assert.Equal(f.t, http.MethodGet, r.Method)
181
182if time.Since(f.readyTime) < 0 {
183w.WriteHeader(http.StatusServiceUnavailable)
184} else {
185w.WriteHeader(http.StatusNoContent)
186}
187}
188
189func nullLogger() logrus.FieldLogger {
190l, _ := test.NewNullLogger()
191return l
192}
193
194func assertContainsEither(t *testing.T, str string, contains ...string) {
195reg := regexp.MustCompile(strings.Join(contains, "|"))
196assert.Regexp(t, reg, str)
197}
198