OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 memory |
| 6 |
| 7 import ( |
| 8 "fmt" |
| 9 "infra/gae/libs/wrapper" |
| 10 "math/rand" |
| 11 "net/http" |
| 12 "testing" |
| 13 "time" |
| 14 |
| 15 . "github.com/smartystreets/goconvey/convey" |
| 16 "golang.org/x/net/context" |
| 17 |
| 18 "appengine/taskqueue" |
| 19 ) |
| 20 |
| 21 func TestTaskQueue(t *testing.T) { |
| 22 t.Parallel() |
| 23 |
| 24 Convey("TaskQueue", t, func() { |
| 25 now := time.Date(2000, time.January, 1, 1, 1, 1, 1, time.UTC) |
| 26 timeNow := func(context.Context) time.Time { |
| 27 ret := now |
| 28 now = now.Add(time.Second) |
| 29 return ret |
| 30 } |
| 31 c := wrapper.SetTimeNowFactory(context.Background(), timeNow) |
| 32 c = wrapper.SetMathRand(c, rand.New(rand.NewSource(wrapper.GetTi
meNow(c).UnixNano()))) |
| 33 c = Use(Enable(c)) |
| 34 |
| 35 tq := wrapper.GetTQ(c).(interface { |
| 36 wrapper.TQMultiReadWriter |
| 37 wrapper.TQTestable |
| 38 }) |
| 39 |
| 40 So(tq, ShouldNotBeNil) |
| 41 |
| 42 Convey("implements TQMultiReadWriter", func() { |
| 43 Convey("Add", func() { |
| 44 t := &taskqueue.Task{Path: "/hello/world"} |
| 45 |
| 46 Convey("works", func() { |
| 47 curTime := now |
| 48 t.Delay = 4 * time.Second |
| 49 t.Header = http.Header{} |
| 50 t.Header.Add("Cat", "tabby") |
| 51 t.Payload = []byte("watwatwat") |
| 52 t.RetryOptions = &taskqueue.RetryOptions
{AgeLimit: 7 * time.Second} |
| 53 _, err := tq.Add(t, "") |
| 54 So(err, ShouldBeNil) |
| 55 name := "Z_UjshxM9ecyMQfGbZmUGOEcgxWU0_5
CGLl_-RntudwAw2DqQ5-58bzJiWQN4OKzeuUb9O4JrPkUw2rOvk2Ax46THojnQ6avBQgZdrKcJmrwQ6o
4qKfJdiyUbGXvy691yRfzLeQhs6cBhWrgf3wH-VPMcA4SC-zlbJ2U8An7I0zJQA5nBFnMNoMgT-2peGo
ay3rCSbj4z9VFFm9kS_i6JCaQH518ujLDSNCYdjTq6B6lcWrZAh0U_q3a1S2nXEwrKiw_t9MTNQFgAQZ
WyGBbvZQPmeRYtu8SPaWzTfd25v_YWgBuVL2rRSPSMvlDwE04nNdtvVzE8vNNiA1zRimmdzKeqATQF9_
ReUvj4D7U8dcS703DZWfKMBLgBffY9jqCassOOOw77V72Oq5EVauUw3Qw0L6bBsfM9FtahTKUdabzRZj
XUoze3EK4KXPt3-wdidau-8JrVf2XFocjjZbwHoxcGvbtT3b4nGLDlgwdC00bwaFBZWff" |
| 56 So(*tq.GetScheduledTasks()["default"][na
me], ShouldResemble, taskqueue.Task{ |
| 57 ETA: curTime.Add(4 * ti
me.Second), |
| 58 Header: http.Header{"Cat":
[]string{"tabby"}}, |
| 59 Method: "POST", |
| 60 Name: name, |
| 61 Path: "/hello/world", |
| 62 Payload: []byte("watwatwat"
), |
| 63 RetryOptions: &taskqueue.RetryOp
tions{AgeLimit: 7 * time.Second}, |
| 64 }) |
| 65 }) |
| 66 |
| 67 Convey("cannot add to bad queues", func() { |
| 68 _, err := tq.Add(nil, "waaat") |
| 69 So(err.Error(), ShouldContainSubstring,
"UNKNOWN_QUEUE") |
| 70 |
| 71 Convey("but you can add Queues when test
ing", func() { |
| 72 tq.CreateQueue("waaat") |
| 73 _, err := tq.Add(t, "waaat") |
| 74 So(err, ShouldBeNil) |
| 75 |
| 76 Convey("you just can't add them
twice", func() { |
| 77 So(func() { tq.CreateQue
ue("waaat") }, ShouldPanic) |
| 78 }) |
| 79 }) |
| 80 }) |
| 81 |
| 82 Convey("requires a URL", func() { |
| 83 t.Path = "" |
| 84 tr, err := tq.Add(t, "") |
| 85 So(err.Error(), ShouldContainSubstring,
"INVALID_URL") |
| 86 So(tr, ShouldBeNil) |
| 87 }) |
| 88 |
| 89 Convey("cannot add twice", func() { |
| 90 t.Name = "bob" |
| 91 _, err := tq.Add(t, "") |
| 92 So(err, ShouldBeNil) |
| 93 |
| 94 // can't add the same one twice! |
| 95 _, err = tq.Add(t, "") |
| 96 So(err, ShouldEqual, taskqueue.ErrTaskAl
readyAdded) |
| 97 }) |
| 98 |
| 99 Convey("cannot add deleted task", func() { |
| 100 t.Name = "bob" |
| 101 _, err := tq.Add(t, "") |
| 102 So(err, ShouldBeNil) |
| 103 |
| 104 err = tq.Delete(t, "") |
| 105 So(err, ShouldBeNil) |
| 106 |
| 107 // can't add a deleted task! |
| 108 _, err = tq.Add(t, "") |
| 109 So(err, ShouldEqual, taskqueue.ErrTaskAl
readyAdded) |
| 110 }) |
| 111 |
| 112 Convey("cannot set ETA+Delay", func() { |
| 113 t.ETA = wrapper.GetTimeNow(c).Add(time.H
our) |
| 114 t.Delay = time.Hour |
| 115 So(func() { tq.Add(t, "") }, ShouldPanic
) |
| 116 }) |
| 117 |
| 118 Convey("must use a reasonable method", func() { |
| 119 t.Method = "Crystal" |
| 120 _, err := tq.Add(t, "") |
| 121 So(err.Error(), ShouldContainSubstring,
"bad method") |
| 122 }) |
| 123 |
| 124 Convey("payload gets dumped for non POST/PUT met
hods", func() { |
| 125 t.Method = "HEAD" |
| 126 t.Payload = []byte("coool") |
| 127 tq, err := tq.Add(t, "") |
| 128 So(err, ShouldBeNil) |
| 129 So(tq.Payload, ShouldBeNil) |
| 130 |
| 131 // check that it didn't modify our origi
nal |
| 132 So(t.Payload, ShouldResemble, []byte("co
ool")) |
| 133 }) |
| 134 |
| 135 Convey("invalid names are rejected", func() { |
| 136 t.Name = "happy times" |
| 137 _, err := tq.Add(t, "") |
| 138 So(err.Error(), ShouldContainSubstring,
"INVALID_TASK_NAME") |
| 139 }) |
| 140 |
| 141 Convey("can be broken", func() { |
| 142 tq.BreakFeatures(nil, "Add") |
| 143 _, err := tq.Add(t, "") |
| 144 So(err.Error(), ShouldContainSubstring,
"TRANSIENT_ERROR") |
| 145 }) |
| 146 |
| 147 Convey("AddMulti also works", func() { |
| 148 curTime := now |
| 149 |
| 150 t2 := dupTask(t) |
| 151 t2.Path = "/hi/city" |
| 152 |
| 153 expect := []*taskqueue.Task{t, t2} |
| 154 |
| 155 tasks, err := tq.AddMulti(expect, "defau
lt") |
| 156 So(err, ShouldBeNil) |
| 157 So(len(tasks), ShouldEqual, 2) |
| 158 So(len(tq.GetScheduledTasks()["default"]
), ShouldEqual, 2) |
| 159 |
| 160 for i := range expect { |
| 161 Convey(fmt.Sprintf("task %d: %s"
, i, expect[i].Path), func() { |
| 162 expect[i].Method = "POST
" |
| 163 expect[i].ETA = curTime.
Add(time.Duration(i) * time.Second) |
| 164 So(expect[i].Name, Shoul
dEqual, "") |
| 165 So(len(tasks[i].Name), S
houldEqual, 500) |
| 166 tasks[i].Name = "" |
| 167 So(tasks[i], ShouldResem
ble, expect[i]) |
| 168 }) |
| 169 } |
| 170 |
| 171 Convey("can be broken", func() { |
| 172 tq.BreakFeatures(nil, "AddMulti"
) |
| 173 _, err := tq.AddMulti([]*taskque
ue.Task{t}, "") |
| 174 So(err.Error(), ShouldContainSub
string, "TRANSIENT_ERROR") |
| 175 }) |
| 176 |
| 177 Convey("is not broken by Add", func() { |
| 178 tq.BreakFeatures(nil, "Add") |
| 179 _, err := tq.AddMulti([]*taskque
ue.Task{t}, "") |
| 180 So(err, ShouldBeNil) |
| 181 }) |
| 182 }) |
| 183 }) |
| 184 |
| 185 Convey("Delete", func() { |
| 186 t := &taskqueue.Task{Path: "/hello/world"} |
| 187 tEnQ, err := tq.Add(t, "") |
| 188 So(err, ShouldBeNil) |
| 189 |
| 190 Convey("works", func() { |
| 191 t.Name = tEnQ.Name |
| 192 err := tq.Delete(t, "") |
| 193 So(err, ShouldBeNil) |
| 194 So(len(tq.GetScheduledTasks()["default"]
), ShouldEqual, 0) |
| 195 So(len(tq.GetTombstonedTasks()["default"
]), ShouldEqual, 1) |
| 196 So(tq.GetTombstonedTasks()["default"][tE
nQ.Name], ShouldResemble, tEnQ) |
| 197 }) |
| 198 |
| 199 Convey("cannot delete a task twice", func() { |
| 200 err := tq.Delete(tEnQ, "") |
| 201 So(err, ShouldBeNil) |
| 202 |
| 203 err = tq.Delete(tEnQ, "") |
| 204 So(err.Error(), ShouldContainSubstring,
"TOMBSTONED_TASK") |
| 205 |
| 206 Convey("but you can if you do a reset",
func() { |
| 207 tq.ResetTasks() |
| 208 |
| 209 tEnQ, err := tq.Add(t, "") |
| 210 So(err, ShouldBeNil) |
| 211 err = tq.Delete(tEnQ, "") |
| 212 So(err, ShouldBeNil) |
| 213 }) |
| 214 }) |
| 215 |
| 216 Convey("cannot delete from bogus queues", func()
{ |
| 217 err := tq.Delete(t, "wat") |
| 218 So(err.Error(), ShouldContainSubstring,
"UNKNOWN_QUEUE") |
| 219 }) |
| 220 |
| 221 Convey("cannot delete a missing task", func() { |
| 222 t.Name = "tarntioarenstyw" |
| 223 err := tq.Delete(t, "") |
| 224 So(err.Error(), ShouldContainSubstring,
"UNKNOWN_TASK") |
| 225 }) |
| 226 |
| 227 Convey("can be broken", func() { |
| 228 tq.BreakFeatures(nil, "Delete") |
| 229 err := tq.Delete(t, "") |
| 230 So(err.Error(), ShouldContainSubstring,
"TRANSIENT_ERROR") |
| 231 }) |
| 232 |
| 233 Convey("DeleteMulti also works", func() { |
| 234 t2 := dupTask(t) |
| 235 t2.Path = "/hi/city" |
| 236 tEnQ2, err := tq.Add(t2, "") |
| 237 So(err, ShouldBeNil) |
| 238 |
| 239 Convey("usually works", func() { |
| 240 err = tq.DeleteMulti([]*taskqueu
e.Task{tEnQ, tEnQ2}, "") |
| 241 So(err, ShouldBeNil) |
| 242 So(len(tq.GetScheduledTasks()["d
efault"]), ShouldEqual, 0) |
| 243 So(len(tq.GetTombstonedTasks()["
default"]), ShouldEqual, 2) |
| 244 }) |
| 245 |
| 246 Convey("can be broken", func() { |
| 247 tq.BreakFeatures(nil, "DeleteMul
ti") |
| 248 err = tq.DeleteMulti([]*taskqueu
e.Task{tEnQ, tEnQ2}, "") |
| 249 So(err.Error(), ShouldContainSub
string, "TRANSIENT_ERROR") |
| 250 }) |
| 251 |
| 252 Convey("is not broken by Delete", func()
{ |
| 253 tq.BreakFeatures(nil, "Delete") |
| 254 err = tq.DeleteMulti([]*taskqueu
e.Task{tEnQ, tEnQ2}, "") |
| 255 So(err, ShouldBeNil) |
| 256 }) |
| 257 }) |
| 258 }) |
| 259 }) |
| 260 |
| 261 Convey("works with transactions", func() { |
| 262 t := &taskqueue.Task{Path: "/hello/world"} |
| 263 tEnQ, err := tq.Add(t, "") |
| 264 So(err, ShouldBeNil) |
| 265 |
| 266 t2 := &taskqueue.Task{Path: "/hi/city"} |
| 267 tEnQ2, err := tq.Add(t2, "") |
| 268 So(err, ShouldBeNil) |
| 269 |
| 270 err = tq.Delete(tEnQ2, "") |
| 271 So(err, ShouldBeNil) |
| 272 |
| 273 Convey("can view regular tasks", func() { |
| 274 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 275 tq := wrapper.GetTQ(c).(interface { |
| 276 wrapper.TQTestable |
| 277 wrapper.TaskQueue |
| 278 }) |
| 279 |
| 280 So(tq.GetScheduledTasks()["default"][tEn
Q.Name], ShouldResemble, tEnQ) |
| 281 So(tq.GetTombstonedTasks()["default"][tE
nQ2.Name], ShouldResemble, tEnQ2) |
| 282 So(tq.GetTransactionTasks()["default"],
ShouldBeNil) |
| 283 return nil |
| 284 }, nil) |
| 285 }) |
| 286 |
| 287 Convey("can add a new task", func() { |
| 288 tEnQ3 := (*taskqueue.Task)(nil) |
| 289 |
| 290 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 291 tq := wrapper.GetTQ(c).(interface { |
| 292 wrapper.TQTestable |
| 293 wrapper.TaskQueue |
| 294 }) |
| 295 |
| 296 t3 := &taskqueue.Task{Path: "/sandwitch/
victory"} |
| 297 tEnQ3, err = tq.Add(t3, "") |
| 298 So(err, ShouldBeNil) |
| 299 |
| 300 So(tq.GetScheduledTasks()["default"][tEn
Q.Name], ShouldResemble, tEnQ) |
| 301 So(tq.GetTombstonedTasks()["default"][tE
nQ2.Name], ShouldResemble, tEnQ2) |
| 302 So(tq.GetTransactionTasks()["default"][0
], ShouldResemble, tEnQ3) |
| 303 return nil |
| 304 }, nil) |
| 305 |
| 306 // name gets generated at transaction-commit-tim
e |
| 307 for name := range tq.GetScheduledTasks()["defaul
t"] { |
| 308 if name == tEnQ.Name { |
| 309 continue |
| 310 } |
| 311 tEnQ3.Name = name |
| 312 break |
| 313 } |
| 314 |
| 315 So(tq.GetScheduledTasks()["default"][tEnQ.Name],
ShouldResemble, tEnQ) |
| 316 So(tq.GetScheduledTasks()["default"][tEnQ3.Name]
, ShouldResemble, tEnQ3) |
| 317 So(tq.GetTombstonedTasks()["default"][tEnQ2.Name
], ShouldResemble, tEnQ2) |
| 318 So(tq.GetTransactionTasks()["default"], ShouldBe
Nil) |
| 319 }) |
| 320 |
| 321 Convey("can a new task (but reset the state in a test)",
func() { |
| 322 tEnQ3 := (*taskqueue.Task)(nil) |
| 323 |
| 324 ttq := interface { |
| 325 wrapper.TQTestable |
| 326 wrapper.TaskQueue |
| 327 }(nil) |
| 328 |
| 329 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 330 ttq = wrapper.GetTQ(c).(interface { |
| 331 wrapper.TQTestable |
| 332 wrapper.TaskQueue |
| 333 }) |
| 334 |
| 335 t3 := &taskqueue.Task{Path: "/sandwitch/
victory"} |
| 336 tEnQ3, err = ttq.Add(t3, "") |
| 337 So(err, ShouldBeNil) |
| 338 |
| 339 So(ttq.GetScheduledTasks()["default"][tE
nQ.Name], ShouldResemble, tEnQ) |
| 340 So(ttq.GetTombstonedTasks()["default"][t
EnQ2.Name], ShouldResemble, tEnQ2) |
| 341 So(ttq.GetTransactionTasks()["default"][
0], ShouldResemble, tEnQ3) |
| 342 |
| 343 ttq.ResetTasks() |
| 344 |
| 345 So(len(ttq.GetScheduledTasks()["default"
]), ShouldEqual, 0) |
| 346 So(len(ttq.GetTombstonedTasks()["default
"]), ShouldEqual, 0) |
| 347 So(len(ttq.GetTransactionTasks()["defaul
t"]), ShouldEqual, 0) |
| 348 |
| 349 return nil |
| 350 }, nil) |
| 351 |
| 352 So(len(tq.GetScheduledTasks()["default"]), Shoul
dEqual, 0) |
| 353 So(len(tq.GetTombstonedTasks()["default"]), Shou
ldEqual, 0) |
| 354 So(len(tq.GetTransactionTasks()["default"]), Sho
uldEqual, 0) |
| 355 |
| 356 Convey("and reusing a closed context is bad time
s", func() { |
| 357 _, err := ttq.Add(nil, "") |
| 358 So(err.Error(), ShouldContainSubstring,
"expired") |
| 359 }) |
| 360 }) |
| 361 |
| 362 Convey("you can AddMulti as well", func() { |
| 363 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 364 tq := wrapper.GetTQ(c).(interface { |
| 365 wrapper.TQTestable |
| 366 wrapper.TaskQueue |
| 367 }) |
| 368 _, err := tq.AddMulti([]*taskqueue.Task{
t, t, t}, "") |
| 369 So(err, ShouldBeNil) |
| 370 So(len(tq.GetScheduledTasks()["default"]
), ShouldEqual, 1) |
| 371 So(len(tq.GetTransactionTasks()["default
"]), ShouldEqual, 3) |
| 372 return nil |
| 373 }, nil) |
| 374 So(len(tq.GetScheduledTasks()["default"]), Shoul
dEqual, 4) |
| 375 So(len(tq.GetTransactionTasks()["default"]), Sho
uldEqual, 0) |
| 376 }) |
| 377 |
| 378 Convey("unless you add too many things", func() { |
| 379 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 380 for i := 0; i < 5; i++ { |
| 381 _, err = wrapper.GetTQ(c).Add(t,
"") |
| 382 So(err, ShouldBeNil) |
| 383 } |
| 384 _, err = wrapper.GetTQ(c).Add(t, "") |
| 385 So(err.Error(), ShouldContainSubstring,
"BAD_REQUEST") |
| 386 return nil |
| 387 }, nil) |
| 388 }) |
| 389 |
| 390 Convey("unless you Add to a bad queue", func() { |
| 391 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 392 _, err = wrapper.GetTQ(c).Add(t, "meat") |
| 393 So(err.Error(), ShouldContainSubstring,
"UNKNOWN_QUEUE") |
| 394 |
| 395 Convey("unless you add it!", func() { |
| 396 wrapper.GetTQ(c).(wrapper.TQTest
able).CreateQueue("meat") |
| 397 _, err = wrapper.GetTQ(c).Add(t,
"meat") |
| 398 So(err, ShouldBeNil) |
| 399 }) |
| 400 |
| 401 return nil |
| 402 }, nil) |
| 403 }) |
| 404 |
| 405 Convey("unless Add is broken", func() { |
| 406 tq.BreakFeatures(nil, "Add") |
| 407 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 408 _, err = wrapper.GetTQ(c).Add(t, "") |
| 409 So(err.Error(), ShouldContainSubstring,
"TRANSIENT_ERROR") |
| 410 return nil |
| 411 }, nil) |
| 412 }) |
| 413 |
| 414 Convey("unless AddMulti is broken", func() { |
| 415 tq.BreakFeatures(nil, "AddMulti") |
| 416 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 417 _, err = wrapper.GetTQ(c).AddMulti(nil,
"") |
| 418 So(err.Error(), ShouldContainSubstring,
"TRANSIENT_ERROR") |
| 419 return nil |
| 420 }, nil) |
| 421 }) |
| 422 |
| 423 Convey("No other features are available, however", func(
) { |
| 424 err := error(nil) |
| 425 func() { |
| 426 defer func() { err = recover().(error) }
() |
| 427 wrapper.GetDS(c).RunInTransaction(func(c
context.Context) error { |
| 428 wrapper.GetTQ(c).Delete(t, "") |
| 429 return nil |
| 430 }, nil) |
| 431 }() |
| 432 So(err.Error(), ShouldContainSubstring, "TaskQue
ue.Delete") |
| 433 }) |
| 434 |
| 435 Convey("adding a new task only happens if we don't errou
t", func() { |
| 436 wrapper.GetDS(c).RunInTransaction(func(c context
.Context) error { |
| 437 t3 := &taskqueue.Task{Path: "/sandwitch/
victory"} |
| 438 _, err = wrapper.GetTQ(c).Add(t3, "") |
| 439 So(err, ShouldBeNil) |
| 440 return fmt.Errorf("nooooo") |
| 441 }, nil) |
| 442 |
| 443 So(tq.GetScheduledTasks()["default"][tEnQ.Name],
ShouldResemble, tEnQ) |
| 444 So(tq.GetTombstonedTasks()["default"][tEnQ2.Name
], ShouldResemble, tEnQ2) |
| 445 So(tq.GetTransactionTasks()["default"], ShouldBe
Nil) |
| 446 }) |
| 447 |
| 448 Convey("likewise, a panic doesn't schedule anything", fu
nc() { |
| 449 func() { |
| 450 defer func() { recover() }() |
| 451 wrapper.GetDS(c).RunInTransaction(func(c
context.Context) error { |
| 452 tq := wrapper.GetTQ(c).(interfac
e { |
| 453 wrapper.TQTestable |
| 454 wrapper.TaskQueue |
| 455 }) |
| 456 |
| 457 t3 := &taskqueue.Task{Path: "/sa
ndwitch/victory"} |
| 458 _, err = tq.Add(t3, "") |
| 459 So(err, ShouldBeNil) |
| 460 |
| 461 panic(fmt.Errorf("nooooo")) |
| 462 }, nil) |
| 463 }() |
| 464 |
| 465 So(tq.GetScheduledTasks()["default"][tEnQ.Name],
ShouldResemble, tEnQ) |
| 466 So(tq.GetTombstonedTasks()["default"][tEnQ2.Name
], ShouldResemble, tEnQ2) |
| 467 So(tq.GetTransactionTasks()["default"], ShouldBe
Nil) |
| 468 }) |
| 469 |
| 470 }) |
| 471 }) |
| 472 } |
OLD | NEW |