15 "google.golang.org/grpc"
16 "google.golang.org/grpc/codes"
17 "google.golang.org/grpc/status"
19 "github.com/Debian/dcs/grpcutil"
20 "github.com/Debian/dcs/internal/api"
21 "github.com/Debian/dcs/internal/localdcs"
22 "github.com/Debian/dcs/internal/proto/dcspb"
23 "github.com/google/go-cmp/cmp"
24 "google.golang.org/protobuf/encoding/prototext"
25 "google.golang.org/protobuf/proto"
28 func TestEndToEnd(t
*testing
.T
) {
29 temp
, err
:= ioutil
.TempDir("", "dcs-endtoend")
33 // TODO: refactor localdcs.Start to take options
34 flag
.Set("localdcs_path", temp
)
35 flag
.Set("shard_path", filepath
.Join(temp
, "shard"))
36 flag
.Set("shard_path", filepath
.Join(temp
, "shard"))
38 instance
, err
:= localdcs
.Start(
39 "-securecookie_hash_key=3270b4d09abccbf3fe59b957b1d429c8c58ac5def079ea4b245f66ade65168c2",
40 "-securecookie_block_key=cdba47f8f82be74175a75ec864aca56d8dcdc5610c88af446005766c6f9e6fd5",
46 flag
.Set("stop", "true")
50 // Created using dcs apikey-create; subject is set to “unittest!”
51 const apikey
= "MTYxNDAxMzI4OXwyb2pFeXdGd0Q0VmdhTkZtMkRoeDdsa1JUa3ZwOTQtM3M2MG1ybGFWRkhacUZwZ1dmMmFlMG5lbkM3UWQ1SV96LXc9PXxdMAS04xLgPDL02_RXt7IftcfPZ4x839RuMhy0_WSX0g=="
53 t
.Run("GRPC", func(t
*testing
.T
) {
55 conn
, err
:= grpcutil
.DialTLS(instance
.Addr
,
56 filepath
.Join(temp
, "cert.pem"),
57 filepath
.Join(temp
, "key.pem"),
60 t
.Fatalf("could not connect to %q: %v", instance
.Addr
, err
)
63 dcs
:= dcspb
.NewDCSClient(conn
)
66 // Verify API key are required:
67 stream
, err
:= dcs
.Search(context
.Background(), &dcspb
.SearchRequest
{
70 if err
!= nil && status
.Code(err
) != codes
.Unauthenticated
{
71 t
.Fatalf("Search(without API key) = %v, want Unauthenticated", err
)
73 if _
, err
:= stream
.Recv(); err
== nil || status
.Code(err
) != codes
.Unauthenticated
{
74 t
.Fatalf("Search(without API key) = %v, want Unauthenticated", err
)
78 stream
, err
:= dcs
.Search(context
.Background(), &dcspb
.SearchRequest
{
85 var events
[]*dcspb
.Event
87 event
, err
:= stream
.Recv()
94 if prog
, ok
:= event
.Data
.(*dcspb
.Event_Progress
); !ok
{
95 continue // TODO: compare the rest, too
97 if prog
.Progress
.FilesProcessed
> 0 &&
98 prog
.Progress
.FilesProcessed
< prog
.Progress
.FilesTotal
{
99 continue // TODO: compare intermediate progress updates, too
102 events
= append(events
, event
)
104 t
.Logf("%d events", len(events
))
105 for idx
, ev
:= range events
{
106 t
.Logf("event %d: %+v", idx
, ev
)
109 last
:= events
[len(events
)-1]
110 if p
, ok
:= last
.Data
.(*dcspb
.Event_Progress
); ok
{
111 queryId
= p
.Progress
.QueryId
114 want
:= []*dcspb
.Event
{
116 Data
: &dcspb
.Event_Progress
{
117 Progress
: &dcspb
.Progress
{
125 Data
: &dcspb
.Event_Progress
{
126 Progress
: &dcspb
.Progress
{
136 t
.Logf("printing %d events:", len(events
))
137 for _
, ev
:= range events
{
138 t
.Logf("event: %s", prototext
.Format(ev
))
139 // TODO: figure out why results is sometimes 17, sometimes less?!
140 // might be related to positional index
141 ev
.Data
.(*dcspb
.Event_Progress
).Progress
.Results
= 0
144 diff1
:= cmp
.Diff(want
, events
, cmp
.Comparer(proto
.Equal
))
145 // The second event (progress update) obsoletes the first one. Depending
146 // on timing, only the second one may be received.
147 diff2
:= cmp
.Diff(want
[1:], events
, cmp
.Comparer(proto
.Equal
))
148 if diff1
!= "" && diff2
!= "" {
149 t
.Fatalf("Search: events differ (-want +got)\n%s", diff1
)
153 t
.Run("OpenAPI", func(t
*testing
.T
) {
154 urlPrefix
:= "https://" + instance
.Addr
+ "/api"
156 t
.Run("OPTIONS", func(t
*testing
.T
) {
157 req
, err
:= http
.NewRequest("OPTIONS", urlPrefix
+"/v1/search", nil)
161 resp
, err
:= instance
.HTTPClient
.Do(req
)
165 if got
, want
:= resp
.StatusCode
, http
.StatusNoContent
; got
!= want
{
166 b
, _
:= ioutil
.ReadAll(resp
.Body
)
167 t
.Fatalf("unexpected HTTP status code: got %v (%s), want %v",
169 strings
.TrimSpace(string(b
)),
172 // TODO: verify CORS headers are present
175 t
.Run("WithoutKey", func(t
*testing
.T
) {
176 req
, err
:= http
.NewRequest("GET", urlPrefix
+"/v1/search", nil)
180 resp
, err
:= instance
.HTTPClient
.Do(req
)
184 if got
, want
:= resp
.StatusCode
, http
.StatusForbidden
; got
!= want
{
185 b
, _
:= ioutil
.ReadAll(resp
.Body
)
186 t
.Fatalf("unexpected HTTP status code: got %v (%s), want %v",
188 strings
.TrimSpace(string(b
)),
193 t
.Run("GET", func(t
*testing
.T
) {
194 req
, err
:= http
.NewRequest("GET", urlPrefix
+"/v1/search?query=i3Font", nil)
198 req
.Header
.Set("x-dcs-apikey", apikey
)
199 resp
, err
:= instance
.HTTPClient
.Do(req
)
203 if got
, want
:= resp
.StatusCode
, http
.StatusOK
; got
!= want
{
204 b
, _
:= ioutil
.ReadAll(resp
.Body
)
205 t
.Fatalf("unexpected HTTP status code: got %v (%s), want %v",
207 strings
.TrimSpace(string(b
)),
211 var results
[]api
.SearchResult
212 b
, err
:= ioutil
.ReadAll(resp
.Body
)
216 if err
:= json
.Unmarshal(b
, &results
); err
!= nil {
219 want
:= api
.SearchResult
{
220 Package
: "i3-wm_4.5.1-2",
221 Path
: "i3-wm_4.5.1-2/libi3/font.c",
223 Context
: "i3Font load_font(const char *pattern, const bool fallback) {",
224 ContextBefore
: []string{
228 ContextAfter
: []string{
230 " font.type = FONT_TYPE_NONE;",
233 for _
, got
:= range results
{
234 if reflect
.DeepEqual(got
, want
) {
235 return // test passed
238 t
.Fatalf("search result %+v not found in results %+v", want
, results
)
241 t
.Run("PerPackage", func(t
*testing
.T
) {
242 req
, err
:= http
.NewRequest("GET", urlPrefix
+"/v1/searchperpackage?query=i3Font", nil)
246 req
.Header
.Set("x-dcs-apikey", apikey
)
247 resp
, err
:= instance
.HTTPClient
.Do(req
)
251 if got
, want
:= resp
.StatusCode
, http
.StatusOK
; got
!= want
{
252 b
, _
:= ioutil
.ReadAll(resp
.Body
)
253 t
.Fatalf("unexpected HTTP status code: got %v (%s), want %v",
255 strings
.TrimSpace(string(b
)),
259 var results
[]api
.PerPackageResult
260 b
, err
:= ioutil
.ReadAll(resp
.Body
)
264 if err
:= json
.Unmarshal(b
, &results
); err
!= nil {
267 if got
, want
:= len(results
), 1; got
!= want
{
268 t
.Fatalf("len(results) = %d, want %d", got
, want
)
270 for _
, got
:= range results
[0].Results
{
271 if got
.Path
== "i3-wm_4.5.1-2/libi3/font.c" &&
272 strings
.Contains(got
.Context
, "i3Font") {
273 return // test passed
276 t
.Fatalf("search result i3-wm_4.5.1-2/libi3/font.c not found in results %+v", results
)