Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(507)

Side by Side Diff: appengine/logdog/coordinator/logView/view_test.go

Issue 1672833003: LogDog: Add log rendering view. Base URL: https://github.com/luci/luci-go@master
Patch Set: Clean up, add tests, little reorg. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/logdog/coordinator/logView/view.go ('k') | appengine/logdog/coordinator/service.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package logView
6
7 import (
8 "errors"
9 "fmt"
10 "io/ioutil"
11 "net/http"
12 "net/http/httptest"
13 "net/url"
14 "strings"
15 "testing"
16 "time"
17
18 "github.com/golang/protobuf/proto"
19 "github.com/julienschmidt/httprouter"
20 "github.com/luci/gae/impl/memory"
21 ds "github.com/luci/gae/service/datastore"
22 "github.com/luci/luci-go/appengine/logdog/coordinator"
23 ct "github.com/luci/luci-go/appengine/logdog/coordinator/coordinatorTest "
24 "github.com/luci/luci-go/common/clock"
25 "github.com/luci/luci-go/common/clock/testclock"
26 "github.com/luci/luci-go/common/logdog/types"
27 "github.com/luci/luci-go/common/logging"
28 "github.com/luci/luci-go/common/logging/gologger"
29 "github.com/luci/luci-go/common/proto/logdog/logpb"
30 "github.com/luci/luci-go/common/proto/logdog/svcconfig"
31 "github.com/luci/luci-go/server/auth"
32 "github.com/luci/luci-go/server/auth/authtest"
33 "github.com/luci/luci-go/server/logdog/storage"
34 "github.com/luci/luci-go/server/middleware"
35 "golang.org/x/net/context"
36
37 . "github.com/smartystreets/goconvey/convey"
38 )
39
40 type simulatedStorageInsn struct {
41 idx int64
42 d [][]byte
43 err error
44 }
45
46 type simulatedStorage struct {
47 storage.Storage
48
49 insnC chan *simulatedStorageInsn
50 gotC chan struct{}
51 closedC chan struct{}
52 }
53
54 func (s *simulatedStorage) Get(req *storage.GetRequest, cb storage.GetCallback) error {
55 insn := <-s.insnC
56 if insn.err != nil {
57 return insn.err
58 }
59
60 defer func() {
61 if s.gotC != nil {
62 s.gotC <- struct{}{}
63 }
64 }()
65
66 for i, d := range insn.d {
67 if !cb(types.MessageIndex(insn.idx+int64(i)), d) {
68 return nil
69 }
70 }
71 return nil
72 }
73
74 func (s *simulatedStorage) Close() {
75 close(s.insnC)
76 close(s.closedC)
77 }
78
79 func (s *simulatedStorage) err(err error) {
80 s.insnC <- &simulatedStorageInsn{err: err}
81 }
82
83 func (s *simulatedStorage) text(startIdx int64, lines ...string) {
84 d := make([][]byte, len(lines))
85 for i, v := range lines {
86 line := logpb.Text_Line{
87 Value: v,
88 }
89 if strings.HasSuffix(line.Value, "\n") {
90 line.Value = strings.TrimSuffix(line.Value, "\n")
91 line.Delimiter = "\n"
92 }
93
94 pb := logpb.LogEntry{
95 StreamIndex: uint64(startIdx + int64(i)),
96 Content: &logpb.LogEntry_Text{
97 Text: &logpb.Text{
98 Lines: []*logpb.Text_Line{
99 &line,
100 },
101 },
102 },
103 }
104
105 data, err := proto.Marshal(&pb)
106 require(err)
107 d[i] = data
108 }
109 s.data(startIdx, d...)
110 }
111
112 func (s *simulatedStorage) data(startIdx int64, d ...[]byte) {
113 s.insnC <- &simulatedStorageInsn{idx: startIdx, d: d}
114 }
115
116 // testBase is a middleware.Base which uses its current Context as the base
117 // context.
118 type testBase struct {
119 context.Context
120 }
121
122 func (t *testBase) base(h middleware.Handler) httprouter.Handle {
123 return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
124 h(t.Context, w, r, p)
125 }
126 }
127
128 func TestLogView(t *testing.T) {
129 Convey(`With a testing configuration, a LogView handler`, t, func() {
130 c, tc := testclock.UseTime(context.Background(), testclock.TestT imeLocal)
131 c = memory.Use(c)
132 c = gologger.Use(c) // XXX: Remove me.
133 c = logging.SetLevel(c, logging.Debug)
134
135 fs := authtest.FakeState{}
136 c = auth.WithState(c, &fs)
137
138 c = ct.UseConfig(c, &svcconfig.Coordinator{
139 AdminAuthGroup: "test-administrators",
140 })
141
142 st := simulatedStorage{
143 insnC: make(chan *simulatedStorageInsn, 1),
144 closedC: make(chan struct{}),
145 }
146
147 desc := ct.TestLogStreamDescriptor(c, "foo/bar")
148 ls, err := ct.TestLogStream(c, desc)
149 require(err)
150
151 err = ls.Put(ds.Get(c))
152 require(err)
153
154 h := Handler{
155 Service: coordinator.Service{
156 IntermediateStorageFunc: func(c context.Context) (storage.Storage, error) {
157 return &st, nil
158 },
159 },
160 }
161
162 r := httprouter.New()
163 h.InstallHandlers(r, func(h middleware.Handler) httprouter.Handl e {
164 return func(w http.ResponseWriter, r *http.Request, p ht tprouter.Params) {
165 h(c, w, r, p)
166 }
167 })
168
169 srv := httptest.NewServer(r)
170 defer srv.Close()
171
172 // Executes a view request for the named stream/hash against our server.
173 view := func(v string) (string, int) {
174 u, err := url.Parse(srv.URL)
175 require(err)
176
177 u.Path = fmt.Sprintf("/logs/view/%s", v)
178 resp, err := http.Get(u.String())
179 require(err)
180 defer resp.Body.Close()
181
182 b, err := ioutil.ReadAll(resp.Body)
183 require(err)
184 return string(b), resp.StatusCode
185 }
186
187 Convey(`Will return Bad Request for an invalid log stream path/h ash.`, func() {
188 _, err := view("!!!invalid!!!")
189 So(err, ShouldEqual, http.StatusBadRequest)
190 })
191
192 Convey(`Will return Not Found for a non-existent log stream.`, f unc() {
193 _, err := view("does/not/+/exist")
194 So(err, ShouldEqual, http.StatusNotFound)
195 })
196
197 Convey(`When Storage has a finished log with "Line0\nLine1\nLine 2\n"`, func() {
198 st.text(0, "Line0\n", "Line1\n", "Line2\n")
199 ls.TerminalIndex = 2
200 require(ls.Put(ds.Get(c)))
201
202 Convey(`Can read the log stream.`, func() {
203 txt, err := view(string(ls.Path()))
204 So(err, ShouldEqual, http.StatusOK)
205 So(txt, ShouldEqual, "Line0\nLine1\nLine2\n")
206 })
207
208 Convey(`When a log stream has been purged`, func() {
209 ls.Purged = true
210 require(ls.Put(ds.Get(c)))
211
212 Convey(`Will return Not Found if the user is not admin.`, func() {
213 _, err := view(string(ls.Path()))
214 So(err, ShouldEqual, http.StatusNotFound )
215 })
216
217 Convey(`Will return the log stream if the user i s admin.`, func() {
218 fs.IdentityGroups = []string{"test-admin istrators"}
219
220 txt, err := view(string(ls.Path()))
221 So(err, ShouldEqual, http.StatusOK)
222 So(txt, ShouldEqual, "Line0\nLine1\nLine 2\n")
223 })
224 })
225 })
226
227 Convey(`Can stream a non-terminal log.`, func() {
228 st.gotC = make(chan struct{})
229
230 go func() {
231 // Put initial log record.
232 st.text(0, "Line0\n")
233 <-st.gotC
234
235 // Skip a log record. This should be ignored sin ce it's non-contiguous.
236 st.text(2, "Line2\n")
237
238 // When we sleep pending a new log, add log line #1. This will happen
239 // AFTER the following "gotC" block. We do this to ensure that the
240 // callback is in place when the timer needs it.
241 tc.SetTimerCallback(func(d time.Duration, t cloc k.Timer) {
242 st.text(1, "Line1\n")
243 tc.SetTimerCallback(nil)
244 tc.Add(d)
245 })
246
247 <-st.gotC
248
249 // The log stream should sleep pending new logs. When it does, our timer
250 // callback will add the missing log (once).
251 <-st.gotC
252
253 // Mark the log stream as terminal @3.
254 ls.TerminalIndex = 3
255 require(ls.Put(ds.Get(c)))
256
257 // Add the missing logs.
258 st.text(2, "Line2\n", "Line3\n")
259 <-st.gotC
260 }()
261
262 txt, err := view(string(ls.Path()))
263 So(err, ShouldEqual, http.StatusOK)
264 So(txt, ShouldEqual, "Line0\nLine1\nLine2\nLine3\n")
265 })
266
267 Convey(`Will return InternalServerError if our storage returns a n error.`, func() {
268 st.err(errors.New("test error"))
269 _, err := view(string(ls.Path()))
270 So(err, ShouldEqual, http.StatusInternalServerError)
271 })
272
273 Convey(`Will return InternalServerError if our storage returns j unk log data.`, func() {
274 st.data(0, []byte{0x00})
275 _, err := view(string(ls.Path()))
276 So(err, ShouldEqual, http.StatusInternalServerError)
277 })
278 })
279 }
280
281 func require(err error) {
282 if err != nil {
283 panic(err)
284 }
285 }
OLDNEW
« no previous file with comments | « appengine/logdog/coordinator/logView/view.go ('k') | appengine/logdog/coordinator/service.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698