OLD | NEW |
1 // Copyright 2017 The LUCI Authors. All rights reserved. | 1 // Copyright 2017 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package apiservers | 5 package apiservers |
6 | 6 |
7 import ( | 7 import ( |
| 8 "fmt" |
8 "testing" | 9 "testing" |
| 10 "time" |
| 11 |
| 12 "google.golang.org/grpc/codes" |
| 13 "google.golang.org/grpc/status" |
9 | 14 |
10 "golang.org/x/net/context" | 15 "golang.org/x/net/context" |
11 | 16 |
12 "github.com/golang/protobuf/proto" | 17 "github.com/golang/protobuf/proto" |
13 "github.com/luci/luci-go/appengine/gaetesting" | 18 "github.com/luci/luci-go/appengine/gaetesting" |
14 "github.com/luci/luci-go/server/auth/identity" | 19 "github.com/luci/luci-go/server/auth/identity" |
15 | 20 |
16 scheduler "github.com/luci/luci-go/scheduler/api/scheduler/v1" | 21 scheduler "github.com/luci/luci-go/scheduler/api/scheduler/v1" |
17 "github.com/luci/luci-go/scheduler/appengine/catalog" | 22 "github.com/luci/luci-go/scheduler/appengine/catalog" |
18 "github.com/luci/luci-go/scheduler/appengine/engine" | 23 "github.com/luci/luci-go/scheduler/appengine/engine" |
19 "github.com/luci/luci-go/scheduler/appengine/messages" | 24 "github.com/luci/luci-go/scheduler/appengine/messages" |
| 25 "github.com/luci/luci-go/scheduler/appengine/task" |
20 "github.com/luci/luci-go/scheduler/appengine/task/urlfetch" | 26 "github.com/luci/luci-go/scheduler/appengine/task/urlfetch" |
21 | 27 |
22 . "github.com/smartystreets/goconvey/convey" | 28 . "github.com/smartystreets/goconvey/convey" |
23 ) | 29 ) |
24 | 30 |
25 func TestGetJobsApi(t *testing.T) { | 31 func TestGetJobsApi(t *testing.T) { |
26 t.Parallel() | 32 t.Parallel() |
27 | 33 |
28 Convey("Scheduler GetJobs API works", t, func() { | 34 Convey("Scheduler GetJobs API works", t, func() { |
29 ctx := gaetesting.TestingContext() | 35 ctx := gaetesting.TestingContext() |
30 fakeEng, catalog := newTestEngine() | 36 fakeEng, catalog := newTestEngine() |
31 » » So(catalog.RegisterTaskManager(&urlfetch.TaskManager{}), ShouldB
eNil) | 37 » » fakeTaskBlob, err := registerUrlFetcher(catalog) |
32 | 38 » » So(err, ShouldBeNil) |
33 ss := SchedulerServer{fakeEng, catalog} | 39 ss := SchedulerServer{fakeEng, catalog} |
34 taskBlob, err := proto.Marshal(&messages.TaskDefWrapper{ | |
35 UrlFetch: &messages.UrlFetchTask{Url: "http://example.co
m/path"}, | |
36 }) | |
37 So(err, ShouldBeNil) | |
38 | 40 |
39 Convey("Empty", func() { | 41 Convey("Empty", func() { |
40 fakeEng.getAllJobs = func() ([]*engine.Job, error) { ret
urn []*engine.Job{}, nil } | 42 fakeEng.getAllJobs = func() ([]*engine.Job, error) { ret
urn []*engine.Job{}, nil } |
41 reply, err := ss.GetJobs(ctx, nil) | 43 reply, err := ss.GetJobs(ctx, nil) |
42 So(err, ShouldBeNil) | 44 So(err, ShouldBeNil) |
43 So(len(reply.GetJobs()), ShouldEqual, 0) | 45 So(len(reply.GetJobs()), ShouldEqual, 0) |
44 }) | 46 }) |
45 | 47 |
46 Convey("All Projects", func() { | 48 Convey("All Projects", func() { |
47 fakeEng.getAllJobs = func() ([]*engine.Job, error) { | 49 fakeEng.getAllJobs = func() ([]*engine.Job, error) { |
48 return []*engine.Job{ | 50 return []*engine.Job{ |
49 { | 51 { |
50 JobID: "bar/foo", | 52 JobID: "bar/foo", |
51 ProjectID: "bar", | 53 ProjectID: "bar", |
52 Schedule: "0 * * * * * *", | 54 Schedule: "0 * * * * * *", |
53 State: engine.JobState{State
: engine.JobStateRunning}, | 55 State: engine.JobState{State
: engine.JobStateRunning}, |
54 » » » » » » Task: taskBlob, | 56 » » » » » » Task: fakeTaskBlob, |
55 }, | 57 }, |
56 { | 58 { |
57 JobID: "baz/faz", | 59 JobID: "baz/faz", |
58 Paused: true, | 60 Paused: true, |
59 ProjectID: "baz", | 61 ProjectID: "baz", |
60 Schedule: "with 1m interval", | 62 Schedule: "with 1m interval", |
61 State: engine.JobState{State
: engine.JobStateSuspended}, | 63 State: engine.JobState{State
: engine.JobStateSuspended}, |
62 » » » » » » Task: taskBlob, | 64 » » » » » » Task: fakeTaskBlob, |
63 }, | 65 }, |
64 }, nil | 66 }, nil |
65 } | 67 } |
66 reply, err := ss.GetJobs(ctx, nil) | 68 reply, err := ss.GetJobs(ctx, nil) |
67 So(err, ShouldBeNil) | 69 So(err, ShouldBeNil) |
68 So(reply.GetJobs(), ShouldResemble, []*scheduler.Job{ | 70 So(reply.GetJobs(), ShouldResemble, []*scheduler.Job{ |
69 { | 71 { |
70 Name: "foo", | 72 Name: "foo", |
71 Project: "bar", | 73 Project: "bar", |
72 Schedule: "0 * * * * * *", | 74 Schedule: "0 * * * * * *", |
(...skipping 10 matching lines...) Expand all Loading... |
83 | 85 |
84 Convey("One Project", func() { | 86 Convey("One Project", func() { |
85 fakeEng.getProjectJobs = func(projectID string) ([]*engi
ne.Job, error) { | 87 fakeEng.getProjectJobs = func(projectID string) ([]*engi
ne.Job, error) { |
86 So(projectID, ShouldEqual, "bar") | 88 So(projectID, ShouldEqual, "bar") |
87 return []*engine.Job{ | 89 return []*engine.Job{ |
88 { | 90 { |
89 JobID: "bar/foo", | 91 JobID: "bar/foo", |
90 ProjectID: "bar", | 92 ProjectID: "bar", |
91 Schedule: "0 * * * * * *", | 93 Schedule: "0 * * * * * *", |
92 State: engine.JobState{State
: engine.JobStateRunning}, | 94 State: engine.JobState{State
: engine.JobStateRunning}, |
93 » » » » » » Task: taskBlob, | 95 » » » » » » Task: fakeTaskBlob, |
94 }, | 96 }, |
95 }, nil | 97 }, nil |
96 } | 98 } |
97 reply, err := ss.GetJobs(ctx, &scheduler.JobsRequest{Pro
ject: "bar"}) | 99 reply, err := ss.GetJobs(ctx, &scheduler.JobsRequest{Pro
ject: "bar"}) |
98 So(err, ShouldBeNil) | 100 So(err, ShouldBeNil) |
99 So(reply.GetJobs(), ShouldResemble, []*scheduler.Job{ | 101 So(reply.GetJobs(), ShouldResemble, []*scheduler.Job{ |
100 { | 102 { |
101 Name: "foo", | 103 Name: "foo", |
102 Project: "bar", | 104 Project: "bar", |
103 Schedule: "0 * * * * * *", | 105 Schedule: "0 * * * * * *", |
104 State: &scheduler.JobState{UiStatus:
"RUNNING"}, | 106 State: &scheduler.JobState{UiStatus:
"RUNNING"}, |
105 }, | 107 }, |
106 }) | 108 }) |
107 }) | 109 }) |
108 }) | 110 }) |
109 } | 111 } |
110 | 112 |
| 113 func TestGetInvocationsApi(t *testing.T) { |
| 114 t.Parallel() |
| 115 |
| 116 Convey("Scheduler GetInvocations API works", t, func() { |
| 117 ctx := gaetesting.TestingContext() |
| 118 fakeEng, catalog := newTestEngine() |
| 119 _, err := registerUrlFetcher(catalog) |
| 120 So(err, ShouldBeNil) |
| 121 ss := SchedulerServer{fakeEng, catalog} |
| 122 |
| 123 Convey("Job not found", func() { |
| 124 fakeEng.getJob = func(JobID string) (*engine.Job, error)
{ return nil, nil } |
| 125 _, err := ss.GetInvocations(ctx, &scheduler.InvocationsR
equest{Project: "not", Job: "exists"}) |
| 126 s, ok := status.FromError(err) |
| 127 So(ok, ShouldBeTrue) |
| 128 So(s.Code(), ShouldEqual, codes.NotFound) |
| 129 }) |
| 130 |
| 131 Convey("DS error", func() { |
| 132 fakeEng.getJob = func(JobID string) (*engine.Job, error)
{ return nil, fmt.Errorf("ds error") } |
| 133 _, err := ss.GetInvocations(ctx, &scheduler.InvocationsR
equest{Project: "proj", Job: "job"}) |
| 134 s, ok := status.FromError(err) |
| 135 So(ok, ShouldBeTrue) |
| 136 So(s.Code(), ShouldEqual, codes.Internal) |
| 137 }) |
| 138 |
| 139 fakeEng.getJob = func(JobID string) (*engine.Job, error) { |
| 140 return &engine.Job{JobID: "proj/job", ProjectID: "proj"}
, nil |
| 141 } |
| 142 |
| 143 Convey("Emtpy with huge pagesize", func() { |
| 144 fakeEng.listInvocations = func(pageSize int, cursor stri
ng) ([]*engine.Invocation, string, error) { |
| 145 So(pageSize, ShouldEqual, 50) |
| 146 So(cursor, ShouldEqual, "") |
| 147 return nil, "", nil |
| 148 } |
| 149 r, err := ss.GetInvocations(ctx, &scheduler.InvocationsR
equest{Project: "proj", Job: "job", PageSize: 1e9}) |
| 150 So(err, ShouldBeNil) |
| 151 So(r.GetNextCursor(), ShouldEqual, "") |
| 152 So(r.GetInvocations(), ShouldBeEmpty) |
| 153 }) |
| 154 |
| 155 Convey("Some with custom pagesize and cursor", func() { |
| 156 started := time.Unix(123123123, 0).UTC() |
| 157 finished := time.Unix(321321321, 0).UTC() |
| 158 fakeEng.listInvocations = func(pageSize int, cursor stri
ng) ([]*engine.Invocation, string, error) { |
| 159 So(pageSize, ShouldEqual, 5) |
| 160 So(cursor, ShouldEqual, "cursor") |
| 161 return []*engine.Invocation{ |
| 162 {ID: 12, Revision: "deadbeef", Status: t
ask.StatusRunning, Started: started, |
| 163 TriggeredBy: identity.Identity("
user:bot@example.com")}, |
| 164 {ID: 13, Revision: "deadbeef", Status: t
ask.StatusAborted, Started: started, Finished: finished, |
| 165 ViewURL: "https://example.com/13
"}, |
| 166 }, "next", nil |
| 167 } |
| 168 r, err := ss.GetInvocations(ctx, &scheduler.InvocationsR
equest{ |
| 169 Project: "proj", Job: "job", PageSize: 5, Cursor
: "cursor"}) |
| 170 So(err, ShouldBeNil) |
| 171 So(r.GetNextCursor(), ShouldEqual, "next") |
| 172 So(r.GetInvocations(), ShouldResemble, []*scheduler.Invo
cation{ |
| 173 { |
| 174 Project: "proj", Job: "job", ConfigRevis
ion: "deadbeef", |
| 175 Id: 12, Final: false, Status: "RUNNING", |
| 176 StartedTs: started.UnixNano() / 1000, |
| 177 TriggeredBy: "user:bot@example.com", |
| 178 }, |
| 179 { |
| 180 Project: "proj", Job: "job", ConfigRevis
ion: "deadbeef", |
| 181 Id: 13, Final: true, Status: "ABORTED", |
| 182 StartedTs: started.UnixNano() / 1000, Fi
nishedTs: finished.UnixNano() / 1000, |
| 183 ViewUrl: "https://example.com/13", |
| 184 }, |
| 185 }) |
| 186 }) |
| 187 |
| 188 }) |
| 189 } |
| 190 |
111 //// | 191 //// |
112 | 192 |
| 193 func registerUrlFetcher(cat catalog.Catalog) ([]byte, error) { |
| 194 if err := cat.RegisterTaskManager(&urlfetch.TaskManager{}); err != nil { |
| 195 return nil, err |
| 196 } |
| 197 return proto.Marshal(&messages.TaskDefWrapper{ |
| 198 UrlFetch: &messages.UrlFetchTask{Url: "http://example.com/path"}
, |
| 199 }) |
| 200 } |
| 201 |
113 func newTestEngine() (*fakeEngine, catalog.Catalog) { | 202 func newTestEngine() (*fakeEngine, catalog.Catalog) { |
114 cat := catalog.New("scheduler.cfg") | 203 cat := catalog.New("scheduler.cfg") |
115 return &fakeEngine{}, cat | 204 return &fakeEngine{}, cat |
116 } | 205 } |
117 | 206 |
118 type fakeEngine struct { | 207 type fakeEngine struct { |
119 » getAllJobs func() ([]*engine.Job, error) | 208 » getAllJobs func() ([]*engine.Job, error) |
120 » getProjectJobs func(projectID string) ([]*engine.Job, error) | 209 » getProjectJobs func(projectID string) ([]*engine.Job, error) |
| 210 » getJob func(jobID string) (*engine.Job, error) |
| 211 » listInvocations func(pageSize int, cursor string) ([]*engine.Invocation,
string, error) |
121 } | 212 } |
122 | 213 |
123 func (f *fakeEngine) GetAllProjects(c context.Context) ([]string, error) { | 214 func (f *fakeEngine) GetAllProjects(c context.Context) ([]string, error) { |
124 panic("not implemented") | 215 panic("not implemented") |
125 } | 216 } |
126 | 217 |
127 func (f *fakeEngine) GetAllJobs(c context.Context) ([]*engine.Job, error) { | 218 func (f *fakeEngine) GetAllJobs(c context.Context) ([]*engine.Job, error) { |
128 return f.getAllJobs() | 219 return f.getAllJobs() |
129 } | 220 } |
130 | 221 |
131 func (f *fakeEngine) GetProjectJobs(c context.Context, projectID string) ([]*eng
ine.Job, error) { | 222 func (f *fakeEngine) GetProjectJobs(c context.Context, projectID string) ([]*eng
ine.Job, error) { |
132 return f.getProjectJobs(projectID) | 223 return f.getProjectJobs(projectID) |
133 } | 224 } |
134 | 225 |
135 func (f *fakeEngine) GetJob(c context.Context, jobID string) (*engine.Job, error
) { | 226 func (f *fakeEngine) GetJob(c context.Context, jobID string) (*engine.Job, error
) { |
136 » panic("not implemented") | 227 » return f.getJob(jobID) |
137 } | 228 } |
138 | 229 |
139 func (f *fakeEngine) ListInvocations(c context.Context, jobID string, pageSize i
nt, cursor string) ([]*engine.Invocation, string, error) { | 230 func (f *fakeEngine) ListInvocations(c context.Context, jobID string, pageSize i
nt, cursor string) ([]*engine.Invocation, string, error) { |
140 » panic("not implemented") | 231 » return f.listInvocations(pageSize, cursor) |
141 } | 232 } |
142 | 233 |
143 func (f *fakeEngine) GetInvocation(c context.Context, jobID string, invID int64)
(*engine.Invocation, error) { | 234 func (f *fakeEngine) GetInvocation(c context.Context, jobID string, invID int64)
(*engine.Invocation, error) { |
144 panic("not implemented") | 235 panic("not implemented") |
145 } | 236 } |
146 | 237 |
147 func (f *fakeEngine) GetInvocationsByNonce(c context.Context, invNonce int64) ([
]*engine.Invocation, error) { | 238 func (f *fakeEngine) GetInvocationsByNonce(c context.Context, invNonce int64) ([
]*engine.Invocation, error) { |
148 panic("not implemented") | 239 panic("not implemented") |
149 } | 240 } |
150 | 241 |
(...skipping 29 matching lines...) Expand all Loading... |
180 panic("not implemented") | 271 panic("not implemented") |
181 } | 272 } |
182 | 273 |
183 func (f *fakeEngine) AbortInvocation(c context.Context, jobID string, invID int6
4, who identity.Identity) error { | 274 func (f *fakeEngine) AbortInvocation(c context.Context, jobID string, invID int6
4, who identity.Identity) error { |
184 panic("not implemented") | 275 panic("not implemented") |
185 } | 276 } |
186 | 277 |
187 func (f *fakeEngine) AbortJob(c context.Context, jobID string, who identity.Iden
tity) error { | 278 func (f *fakeEngine) AbortJob(c context.Context, jobID string, who identity.Iden
tity) error { |
188 panic("not implemented") | 279 panic("not implemented") |
189 } | 280 } |
OLD | NEW |