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