github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/resource/state/resource_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "bytes" 8 "io" 9 "io/ioutil" 10 "strings" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 charmresource "gopkg.in/juju/charm.v6-unstable/resource" 18 "gopkg.in/juju/names.v2" 19 "gopkg.in/mgo.v2/txn" 20 21 "github.com/juju/juju/resource" 22 "github.com/juju/juju/resource/resourcetesting" 23 "github.com/juju/utils/clock" 24 ) 25 26 var _ = gc.Suite(&ResourceSuite{}) 27 28 type ResourceSuite struct { 29 testing.IsolationSuite 30 31 stub *testing.Stub 32 raw *stubRawState 33 persist *stubPersistence 34 storage *stubStorage 35 timestamp time.Time 36 pendingID string 37 } 38 39 func (s *ResourceSuite) SetUpTest(c *gc.C) { 40 s.IsolationSuite.SetUpTest(c) 41 42 s.stub = &testing.Stub{} 43 s.raw = &stubRawState{stub: s.stub} 44 s.persist = &stubPersistence{stub: s.stub} 45 s.persist.ReturnStageResource = &stubStagedResource{stub: s.stub} 46 s.storage = &stubStorage{stub: s.stub} 47 s.raw.ReturnPersistence = s.persist 48 s.raw.ReturnStorage = s.storage 49 s.timestamp = time.Now().UTC() 50 s.pendingID = "" 51 } 52 53 func (s *ResourceSuite) now() time.Time { 54 s.stub.AddCall("currentTimestamp") 55 s.stub.NextErr() // Pop one off. 56 57 return s.timestamp 58 } 59 60 func (s *ResourceSuite) newPendingID() (string, error) { 61 s.stub.AddCall("newPendingID") 62 if err := s.stub.NextErr(); err != nil { 63 return "", errors.Trace(err) 64 } 65 66 return s.pendingID, nil 67 } 68 69 func (s *ResourceSuite) TestListResourcesOkay(c *gc.C) { 70 expected := newUploadResources(c, "spam", "eggs") 71 s.persist.ReturnListResources = resource.ServiceResources{Resources: expected} 72 tag := names.NewUnitTag("a-application/0") 73 s.raw.ReturnUnits = []names.UnitTag{tag} 74 st := NewState(s.raw) 75 s.stub.ResetCalls() 76 77 resources, err := st.ListResources("a-application") 78 c.Assert(err, jc.ErrorIsNil) 79 80 c.Check(resources.Resources, jc.DeepEquals, expected) 81 c.Check(resources.UnitResources, jc.DeepEquals, []resource.UnitResources{{ 82 Tag: tag, 83 }}) 84 s.stub.CheckCallNames(c, "ListResources", "Units") 85 s.stub.CheckCall(c, 0, "ListResources", "a-application") 86 } 87 88 func (s *ResourceSuite) TestListResourcesNoUnits(c *gc.C) { 89 expected := newUploadResources(c, "spam", "eggs") 90 s.persist.ReturnListResources = resource.ServiceResources{Resources: expected} 91 st := NewState(s.raw) 92 s.stub.ResetCalls() 93 94 resources, err := st.ListResources("a-application") 95 c.Assert(err, jc.ErrorIsNil) 96 97 c.Check(resources.Resources, jc.DeepEquals, expected) 98 c.Check(resources.UnitResources, gc.HasLen, 0) 99 s.stub.CheckCallNames(c, "ListResources", "Units") 100 s.stub.CheckCall(c, 0, "ListResources", "a-application") 101 } 102 103 func (s *ResourceSuite) TestListResourcesEmpty(c *gc.C) { 104 s.raw.ReturnUnits = []names.UnitTag{ 105 names.NewUnitTag("a-application/0"), 106 } 107 st := NewState(s.raw) 108 s.stub.ResetCalls() 109 110 resources, err := st.ListResources("a-application") 111 c.Assert(err, jc.ErrorIsNil) 112 113 c.Check(resources.Resources, gc.HasLen, 0) 114 c.Check(resources.UnitResources, gc.HasLen, 1) 115 s.stub.CheckCallNames(c, "ListResources", "Units") 116 } 117 118 func (s *ResourceSuite) TestListResourcesError(c *gc.C) { 119 expected := newUploadResources(c, "spam", "eggs") 120 s.persist.ReturnListResources = resource.ServiceResources{Resources: expected} 121 st := NewState(s.raw) 122 s.stub.ResetCalls() 123 failure := errors.New("<failure>") 124 s.stub.SetErrors(failure) 125 126 _, err := st.ListResources("a-application") 127 128 c.Check(errors.Cause(err), gc.Equals, failure) 129 s.stub.CheckCallNames(c, "ListResources", "VerifyService") 130 } 131 132 func (s *ResourceSuite) TestGetPendingResource(c *gc.C) { 133 resources := newUploadResources(c, "spam", "eggs") 134 resources[0].PendingID = "some-unique-id" 135 resources[1].PendingID = "other-unique-id" 136 s.persist.ReturnListPendingResources = resources 137 st := NewState(s.raw) 138 s.stub.ResetCalls() 139 140 res, err := st.GetPendingResource("a-application", "eggs", "other-unique-id") 141 c.Assert(err, jc.ErrorIsNil) 142 143 s.stub.CheckCallNames(c, "ListPendingResources") 144 s.stub.CheckCall(c, 0, "ListPendingResources", "a-application") 145 c.Check(res, jc.DeepEquals, resources[1]) 146 } 147 148 func (s *ResourceSuite) TestSetResourceOkay(c *gc.C) { 149 expected := newUploadResource(c, "spam", "spamspamspam") 150 expected.Timestamp = s.timestamp 151 chRes := expected.Resource 152 hash := chRes.Fingerprint.String() 153 path := "application-a-application/resources/spam" 154 file := &stubReader{stub: s.stub} 155 st := NewState(s.raw) 156 st.currentTimestamp = s.now 157 s.stub.ResetCalls() 158 159 res, err := st.SetResource("a-application", "a-user", chRes, file) 160 c.Assert(err, jc.ErrorIsNil) 161 162 s.stub.CheckCallNames(c, 163 "currentTimestamp", 164 "StageResource", 165 "PutAndCheckHash", 166 "Activate", 167 ) 168 s.stub.CheckCall(c, 1, "StageResource", expected, path) 169 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, res.Size, hash) 170 c.Check(res, jc.DeepEquals, resource.Resource{ 171 Resource: chRes, 172 ID: "a-application/" + res.Name, 173 ApplicationID: "a-application", 174 Username: "a-user", 175 Timestamp: s.timestamp, 176 }) 177 } 178 179 func (s *ResourceSuite) TestSetResourceInfoOnly(c *gc.C) { 180 expected := newUploadResource(c, "spam", "spamspamspam") 181 expected.Timestamp = time.Time{} 182 expected.Username = "" 183 chRes := expected.Resource 184 st := NewState(s.raw) 185 st.currentTimestamp = s.now 186 s.stub.ResetCalls() 187 188 res, err := st.SetResource("a-application", "a-user", chRes, nil) 189 c.Assert(err, jc.ErrorIsNil) 190 191 s.stub.CheckCallNames(c, 192 "SetResource", 193 ) 194 s.stub.CheckCall(c, 0, "SetResource", expected) 195 c.Check(res, jc.DeepEquals, resource.Resource{ 196 Resource: chRes, 197 ID: "a-application/" + res.Name, 198 ApplicationID: "a-application", 199 }) 200 } 201 202 func (s *ResourceSuite) TestSetResourceBadResource(c *gc.C) { 203 res := newUploadResource(c, "spam", "spamspamspam") 204 res.Fingerprint = charmresource.Fingerprint{} 205 file := &stubReader{stub: s.stub} 206 st := NewState(s.raw) 207 st.currentTimestamp = s.now 208 s.stub.ResetCalls() 209 210 _, err := st.SetResource("a-application", "a-user", res.Resource, file) 211 212 c.Check(err, jc.Satisfies, errors.IsNotValid) 213 c.Check(err, gc.ErrorMatches, `bad resource metadata.*`) 214 s.stub.CheckCallNames(c, "currentTimestamp") 215 } 216 217 func (s *ResourceSuite) TestSetResourceStagingFailure(c *gc.C) { 218 expected := newUploadResource(c, "spam", "spamspamspam") 219 expected.Timestamp = s.timestamp 220 path := "application-a-application/resources/spam" 221 file := &stubReader{stub: s.stub} 222 st := NewState(s.raw) 223 st.currentTimestamp = s.now 224 s.stub.ResetCalls() 225 failure := errors.New("<failure>") 226 ignoredErr := errors.New("<never reached>") 227 s.stub.SetErrors(nil, failure, ignoredErr) 228 229 _, err := st.SetResource("a-application", "a-user", expected.Resource, file) 230 231 c.Check(errors.Cause(err), gc.Equals, failure) 232 s.stub.CheckCallNames(c, "currentTimestamp", "StageResource") 233 s.stub.CheckCall(c, 1, "StageResource", expected, path) 234 } 235 236 func (s *ResourceSuite) TestSetResourcePutFailureBasic(c *gc.C) { 237 expected := newUploadResource(c, "spam", "spamspamspam") 238 expected.Timestamp = s.timestamp 239 hash := expected.Fingerprint.String() 240 path := "application-a-application/resources/spam" 241 file := &stubReader{stub: s.stub} 242 st := NewState(s.raw) 243 st.currentTimestamp = s.now 244 s.stub.ResetCalls() 245 failure := errors.New("<failure>") 246 ignoredErr := errors.New("<never reached>") 247 s.stub.SetErrors(nil, nil, failure, nil, ignoredErr) 248 249 _, err := st.SetResource("a-application", "a-user", expected.Resource, file) 250 251 c.Check(errors.Cause(err), gc.Equals, failure) 252 s.stub.CheckCallNames(c, 253 "currentTimestamp", 254 "StageResource", 255 "PutAndCheckHash", 256 "Unstage", 257 ) 258 s.stub.CheckCall(c, 1, "StageResource", expected, path) 259 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 260 } 261 262 func (s *ResourceSuite) TestSetResourcePutFailureExtra(c *gc.C) { 263 expected := newUploadResource(c, "spam", "spamspamspam") 264 expected.Timestamp = s.timestamp 265 hash := expected.Fingerprint.String() 266 path := "application-a-application/resources/spam" 267 file := &stubReader{stub: s.stub} 268 st := NewState(s.raw) 269 st.currentTimestamp = s.now 270 s.stub.ResetCalls() 271 failure := errors.New("<failure>") 272 extraErr := errors.New("<just not your day>") 273 ignoredErr := errors.New("<never reached>") 274 s.stub.SetErrors(nil, nil, failure, extraErr, ignoredErr) 275 276 _, err := st.SetResource("a-application", "a-user", expected.Resource, file) 277 278 c.Check(errors.Cause(err), gc.Equals, failure) 279 s.stub.CheckCallNames(c, 280 "currentTimestamp", 281 "StageResource", 282 "PutAndCheckHash", 283 "Unstage", 284 ) 285 s.stub.CheckCall(c, 1, "StageResource", expected, path) 286 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 287 } 288 289 func (s *ResourceSuite) TestSetResourceSetFailureBasic(c *gc.C) { 290 expected := newUploadResource(c, "spam", "spamspamspam") 291 expected.Timestamp = s.timestamp 292 hash := expected.Fingerprint.String() 293 path := "application-a-application/resources/spam" 294 file := &stubReader{stub: s.stub} 295 st := NewState(s.raw) 296 st.currentTimestamp = s.now 297 s.stub.ResetCalls() 298 failure := errors.New("<failure>") 299 ignoredErr := errors.New("<never reached>") 300 s.stub.SetErrors(nil, nil, nil, failure, nil, nil, ignoredErr) 301 302 _, err := st.SetResource("a-application", "a-user", expected.Resource, file) 303 304 c.Check(errors.Cause(err), gc.Equals, failure) 305 s.stub.CheckCallNames(c, 306 "currentTimestamp", 307 "StageResource", 308 "PutAndCheckHash", 309 "Activate", 310 "Remove", 311 "Unstage", 312 ) 313 s.stub.CheckCall(c, 1, "StageResource", expected, path) 314 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 315 s.stub.CheckCall(c, 4, "Remove", path) 316 } 317 318 func (s *ResourceSuite) TestSetResourceSetFailureExtra(c *gc.C) { 319 expected := newUploadResource(c, "spam", "spamspamspam") 320 expected.Timestamp = s.timestamp 321 hash := expected.Fingerprint.String() 322 path := "application-a-application/resources/spam" 323 file := &stubReader{stub: s.stub} 324 st := NewState(s.raw) 325 st.currentTimestamp = s.now 326 s.stub.ResetCalls() 327 failure := errors.New("<failure>") 328 extraErr1 := errors.New("<just not your day>") 329 extraErr2 := errors.New("<wow...just wow>") 330 ignoredErr := errors.New("<never reached>") 331 s.stub.SetErrors(nil, nil, nil, failure, extraErr1, extraErr2, ignoredErr) 332 333 _, err := st.SetResource("a-application", "a-user", expected.Resource, file) 334 335 c.Check(errors.Cause(err), gc.Equals, failure) 336 s.stub.CheckCallNames(c, 337 "currentTimestamp", 338 "StageResource", 339 "PutAndCheckHash", 340 "Activate", 341 "Remove", 342 "Unstage", 343 ) 344 s.stub.CheckCall(c, 1, "StageResource", expected, path) 345 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 346 s.stub.CheckCall(c, 4, "Remove", path) 347 } 348 349 func (s *ResourceSuite) TestUpdatePendingResourceOkay(c *gc.C) { 350 expected := newUploadResource(c, "spam", "spamspamspam") 351 expected.PendingID = "some-unique-id" 352 expected.Timestamp = s.timestamp 353 chRes := expected.Resource 354 hash := chRes.Fingerprint.String() 355 path := "application-a-application/resources/spam-some-unique-id" 356 file := &stubReader{stub: s.stub} 357 st := NewState(s.raw) 358 st.currentTimestamp = s.now 359 s.stub.ResetCalls() 360 361 res, err := st.UpdatePendingResource("a-application", "some-unique-id", "a-user", chRes, file) 362 c.Assert(err, jc.ErrorIsNil) 363 364 s.stub.CheckCallNames(c, 365 "currentTimestamp", 366 "StageResource", 367 "PutAndCheckHash", 368 "Activate", 369 ) 370 s.stub.CheckCall(c, 1, "StageResource", expected, path) 371 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, res.Size, hash) 372 c.Check(res, jc.DeepEquals, resource.Resource{ 373 Resource: chRes, 374 ID: "a-application/" + res.Name, 375 ApplicationID: "a-application", 376 PendingID: "some-unique-id", 377 Username: "a-user", 378 Timestamp: s.timestamp, 379 }) 380 } 381 382 func (s *ResourceSuite) TestAddPendingResourceOkay(c *gc.C) { 383 s.pendingID = "some-unique-ID-001" 384 expected := newUploadResource(c, "spam", "spamspamspam") 385 expected.PendingID = s.pendingID 386 expected.Timestamp = s.timestamp 387 chRes := expected.Resource 388 hash := chRes.Fingerprint.String() 389 path := "application-a-application/resources/spam-some-unique-ID-001" 390 file := &stubReader{stub: s.stub} 391 st := NewState(s.raw) 392 st.currentTimestamp = s.now 393 st.newPendingID = s.newPendingID 394 s.stub.ResetCalls() 395 396 pendingID, err := st.AddPendingResource("a-application", "a-user", chRes, file) 397 c.Assert(err, jc.ErrorIsNil) 398 399 s.stub.CheckCallNames(c, 400 "newPendingID", 401 "currentTimestamp", 402 "StageResource", 403 "PutAndCheckHash", 404 "Activate", 405 ) 406 s.stub.CheckCall(c, 2, "StageResource", expected, path) 407 s.stub.CheckCall(c, 3, "PutAndCheckHash", path, file, expected.Size, hash) 408 c.Check(pendingID, gc.Equals, s.pendingID) 409 } 410 411 func (s *ResourceSuite) TestOpenResourceOkay(c *gc.C) { 412 data := "some data" 413 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-application", data) 414 s.persist.ReturnGetResource = opened.Resource 415 s.persist.ReturnGetResourcePath = "application-a-application/resources/spam" 416 s.storage.ReturnGet = opened.Content() 417 st := NewState(s.raw) 418 s.stub.ResetCalls() 419 420 info, reader, err := st.OpenResource("a-application", "spam") 421 c.Assert(err, jc.ErrorIsNil) 422 423 s.stub.CheckCallNames(c, "GetResource", "Get") 424 s.stub.CheckCall(c, 1, "Get", "application-a-application/resources/spam") 425 c.Check(info, jc.DeepEquals, opened.Resource) 426 c.Check(reader, gc.Equals, opened.ReadCloser) 427 } 428 429 func (s *ResourceSuite) TestOpenResourceNotFound(c *gc.C) { 430 st := NewState(s.raw) 431 s.stub.ResetCalls() 432 errNotFound := errors.NotFoundf("resource") 433 s.stub.SetErrors(errNotFound) 434 435 _, _, err := st.OpenResource("a-application", "spam") 436 437 s.stub.CheckCallNames(c, "GetResource", "VerifyService") 438 c.Check(err, jc.Satisfies, errors.IsNotFound) 439 } 440 441 func (s *ResourceSuite) TestOpenResourcePlaceholder(c *gc.C) { 442 res := resourcetesting.NewPlaceholderResource(c, "spam", "a-application") 443 s.persist.ReturnGetResource = res 444 s.persist.ReturnGetResourcePath = "application-a-application/resources/spam" 445 st := NewState(s.raw) 446 s.stub.ResetCalls() 447 448 _, _, err := st.OpenResource("a-application", "spam") 449 450 s.stub.CheckCallNames(c, "GetResource") 451 c.Check(err, jc.Satisfies, errors.IsNotFound) 452 } 453 454 func (s *ResourceSuite) TestOpenResourceSizeMismatch(c *gc.C) { 455 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-application", "some data") 456 s.persist.ReturnGetResource = opened.Resource 457 s.persist.ReturnGetResourcePath = "application-a-application/resources/spam" 458 content := opened.Content() 459 content.Size += 1 460 s.storage.ReturnGet = content 461 st := NewState(s.raw) 462 s.stub.ResetCalls() 463 464 _, _, err := st.OpenResource("a-application", "spam") 465 466 s.stub.CheckCallNames(c, "GetResource", "Get") 467 c.Check(err, gc.ErrorMatches, `storage returned a size \(10\) which doesn't match resource metadata \(9\)`) 468 } 469 470 func (s *ResourceSuite) TestOpenResourceForUniterOkay(c *gc.C) { 471 data := "some data" 472 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-application", data) 473 s.persist.ReturnGetResource = opened.Resource 474 s.persist.ReturnGetResourcePath = "application-a-application/resources/spam" 475 s.storage.ReturnGet = opened.Content() 476 unit := newUnit(s.stub, "a-application/0") 477 st := NewState(s.raw) 478 s.stub.ResetCalls() 479 480 info, reader, err := st.OpenResourceForUniter(unit, "spam") 481 c.Assert(err, jc.ErrorIsNil) 482 483 s.stub.CheckCallNames(c, "ApplicationName", "GetResource", "Get", "Name", "SetUnitResourceProgress") 484 s.stub.CheckCall(c, 2, "Get", "application-a-application/resources/spam") 485 c.Check(info, jc.DeepEquals, opened.Resource) 486 487 b, err := ioutil.ReadAll(reader) 488 // note ioutil.ReadAll converts EOF to nil 489 c.Check(err, jc.ErrorIsNil) 490 c.Check(b, gc.DeepEquals, []byte(data)) 491 } 492 493 func (s *ResourceSuite) TestOpenResourceForUniterNotFound(c *gc.C) { 494 unit := newUnit(s.stub, "a-application/0") 495 st := NewState(s.raw) 496 s.stub.ResetCalls() 497 errNotFound := errors.NotFoundf("resource") 498 s.stub.SetErrors(nil, errNotFound) 499 500 _, _, err := st.OpenResourceForUniter(unit, "spam") 501 502 s.stub.CheckCallNames(c, "ApplicationName", "GetResource", "VerifyService") 503 c.Check(err, jc.Satisfies, errors.IsNotFound) 504 } 505 506 func (s *ResourceSuite) TestOpenResourceForUniterPlaceholder(c *gc.C) { 507 res := resourcetesting.NewPlaceholderResource(c, "spam", "a-application") 508 s.persist.ReturnGetResource = res 509 s.persist.ReturnGetResourcePath = "application-a-application/resources/spam" 510 unit := newUnit(s.stub, "a-application/0") 511 st := NewState(s.raw) 512 s.stub.ResetCalls() 513 514 _, _, err := st.OpenResourceForUniter(unit, "spam") 515 516 s.stub.CheckCallNames(c, "ApplicationName", "GetResource") 517 c.Check(err, jc.Satisfies, errors.IsNotFound) 518 } 519 520 func (s *ResourceSuite) TestOpenResourceForUniterSizeMismatch(c *gc.C) { 521 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-application", "some data") 522 s.persist.ReturnGetResource = opened.Resource 523 s.persist.ReturnGetResourcePath = "application-a-application/resources/spam" 524 content := opened.Content() 525 content.Size += 1 526 s.storage.ReturnGet = content 527 unit := newUnit(s.stub, "a-application/0") 528 st := NewState(s.raw) 529 s.stub.ResetCalls() 530 531 _, _, err := st.OpenResourceForUniter(unit, "spam") 532 533 s.stub.CheckCallNames(c, "ApplicationName", "GetResource", "Get") 534 c.Check(err, gc.ErrorMatches, `storage returned a size \(10\) which doesn't match resource metadata \(9\)`) 535 } 536 537 func (s *ResourceSuite) TestSetCharmStoreResources(c *gc.C) { 538 lastPolled := time.Now().UTC() 539 resources := newStoreResources(c, "spam", "eggs") 540 var info []charmresource.Resource 541 for _, res := range resources { 542 chRes := res.Resource 543 info = append(info, chRes) 544 } 545 st := NewState(s.raw) 546 s.stub.ResetCalls() 547 548 err := st.SetCharmStoreResources("a-application", info, lastPolled) 549 c.Assert(err, jc.ErrorIsNil) 550 551 s.stub.CheckCallNames(c, 552 "SetCharmStoreResource", 553 "SetCharmStoreResource", 554 ) 555 s.stub.CheckCall(c, 0, "SetCharmStoreResource", "a-application/spam", "a-application", info[0], lastPolled) 556 s.stub.CheckCall(c, 1, "SetCharmStoreResource", "a-application/eggs", "a-application", info[1], lastPolled) 557 } 558 559 func (s *ResourceSuite) TestNewResourcePendingResourcesOps(c *gc.C) { 560 doc1 := map[string]string{"a": "1"} 561 doc2 := map[string]string{"b": "2"} 562 expected := []txn.Op{{ 563 C: "resources", 564 Id: "resource#a-application/spam#pending-some-unique-ID-001", 565 Assert: txn.DocExists, 566 Remove: true, 567 }, { 568 C: "resources", 569 Id: "resource#a-application/spam", 570 Assert: txn.DocMissing, 571 Insert: &doc1, 572 }, { 573 C: "resources", 574 Id: "resource#a-application/spam#pending-some-unique-ID-001", 575 Assert: txn.DocExists, 576 Remove: true, 577 }, { 578 C: "resources", 579 Id: "resource#a-application/spam", 580 Assert: txn.DocMissing, 581 Insert: &doc2, 582 }} 583 s.persist.ReturnNewResolvePendingResourceOps = [][]txn.Op{ 584 expected[:2], 585 expected[2:], 586 } 587 applicationID := "a-application" 588 st := NewState(s.raw) 589 s.stub.ResetCalls() 590 pendingIDs := map[string]string{ 591 "spam": "some-unique-id", 592 "eggs": "other-unique-id", 593 } 594 595 ops, err := st.NewResolvePendingResourcesOps(applicationID, pendingIDs) 596 c.Assert(err, jc.ErrorIsNil) 597 598 s.stub.CheckCallNames(c, 599 "NewResolvePendingResourceOps", 600 "NewResolvePendingResourceOps", 601 ) 602 c.Check(s.persist.CallsForNewResolvePendingResourceOps, jc.DeepEquals, map[string]string{ 603 "a-application/spam": "some-unique-id", 604 "a-application/eggs": "other-unique-id", 605 }) 606 c.Check(ops, jc.DeepEquals, expected) 607 } 608 609 func (s *ResourceSuite) TestUnitSetterEOF(c *gc.C) { 610 r := unitSetter{ 611 ReadCloser: ioutil.NopCloser(&bytes.Buffer{}), 612 persist: &stubPersistence{stub: s.stub}, 613 unit: newUnit(s.stub, "some-application/0"), 614 resource: newUploadResource(c, "res", "res"), 615 clock: clock.WallClock, 616 } 617 // have to try to read non-zero data, or bytes.buffer will happily return 618 // nil. 619 p := make([]byte, 5) 620 n, err := r.Read(p) 621 c.Assert(n, gc.Equals, 0) 622 c.Assert(err, gc.Equals, io.EOF) 623 624 s.stub.CheckCallNames(c, "Name", "SetUnitResource") 625 s.stub.CheckCall(c, 1, "SetUnitResource", "some-application/0", r.resource) 626 } 627 628 func (s *ResourceSuite) TestUnitSetterNoEOF(c *gc.C) { 629 r := unitSetter{ 630 ReadCloser: ioutil.NopCloser(bytes.NewBufferString("foobar")), 631 persist: &stubPersistence{stub: s.stub}, 632 unit: newUnit(s.stub, "some-application/0"), 633 resource: newUploadResource(c, "res", "res"), 634 clock: clock.WallClock, 635 } 636 // read less than the full buffer 637 p := make([]byte, 3) 638 n, err := r.Read(p) 639 c.Assert(n, gc.Equals, 3) 640 c.Assert(err, gc.Equals, nil) 641 642 // Assert that we don't call SetUnitResource if we read but don't reach the 643 // end of the buffer. 644 s.stub.CheckCallNames(c, "Name", "SetUnitResourceProgress") 645 } 646 647 func (s *ResourceSuite) TestUnitSetterSetUnitErr(c *gc.C) { 648 r := unitSetter{ 649 ReadCloser: ioutil.NopCloser(&bytes.Buffer{}), 650 persist: &stubPersistence{stub: s.stub}, 651 unit: newUnit(s.stub, "some-application/0"), 652 resource: newUploadResource(c, "res", "res"), 653 clock: clock.WallClock, 654 } 655 656 s.stub.SetErrors(errors.Errorf("oops!")) 657 // have to try to read non-zero data, or bytes.buffer will happily return 658 // nil. 659 p := make([]byte, 5) 660 n, err := r.Read(p) 661 c.Assert(n, gc.Equals, 0) 662 663 // ensure that we return the EOF from bytes.buffer and not the error from SetUnitResource. 664 c.Assert(err, gc.Equals, io.EOF) 665 666 s.stub.CheckCallNames(c, "Name", "SetUnitResource") 667 s.stub.CheckCall(c, 1, "SetUnitResource", "some-application/0", r.resource) 668 } 669 670 func (s *ResourceSuite) TestUnitSetterErr(c *gc.C) { 671 r := unitSetter{ 672 ReadCloser: ioutil.NopCloser(&stubReader{stub: s.stub}), 673 persist: &stubPersistence{stub: s.stub}, 674 unit: newUnit(s.stub, "some-application/0"), 675 resource: newUploadResource(c, "res", "res"), 676 clock: clock.WallClock, 677 } 678 expected := errors.Errorf("some-err") 679 s.stub.SetErrors(expected) 680 // have to try to read non-zero data, or bytes.buffer will happily return 681 // nil. 682 p := make([]byte, 5) 683 n, err := r.Read(p) 684 c.Assert(n, gc.Equals, 0) 685 c.Assert(err, gc.Equals, expected) 686 687 s.stub.CheckCall(c, 0, "Read", p) 688 } 689 690 func newUploadResources(c *gc.C, names ...string) []resource.Resource { 691 var resources []resource.Resource 692 for _, name := range names { 693 res := newUploadResource(c, name, name) 694 resources = append(resources, res) 695 } 696 return resources 697 } 698 699 func newUploadResource(c *gc.C, name, data string) resource.Resource { 700 opened := resourcetesting.NewResource(c, nil, name, "a-application", data) 701 return opened.Resource 702 } 703 704 func newStoreResources(c *gc.C, names ...string) []resource.Resource { 705 var resources []resource.Resource 706 for _, name := range names { 707 res := newStoreResource(c, name, name) 708 resources = append(resources, res) 709 } 710 return resources 711 } 712 713 func newStoreResource(c *gc.C, name, data string) resource.Resource { 714 opened := resourcetesting.NewResource(c, nil, name, "a-application", data) 715 res := opened.Resource 716 res.Origin = charmresource.OriginStore 717 res.Revision = 1 718 res.Username = "" 719 res.Timestamp = time.Time{} 720 return res 721 } 722 723 func newCharmResource(c *gc.C, name, data string, rev int) charmresource.Resource { 724 opened := resourcetesting.NewResource(c, nil, name, "a-application", data) 725 chRes := opened.Resource.Resource 726 chRes.Origin = charmresource.OriginStore 727 chRes.Revision = rev 728 return chRes 729 } 730 731 func newUnit(stub *testing.Stub, name string) *resourcetesting.StubUnit { 732 return &resourcetesting.StubUnit{ 733 Stub: stub, 734 ReturnName: name, 735 ReturnApplicationName: strings.Split(name, "/")[0], 736 } 737 }