k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/cached/cache_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cached_test 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "math/rand" 25 "sort" 26 "strings" 27 "sync" 28 "testing" 29 "time" 30 31 "k8s.io/kube-openapi/pkg/cached" 32 ) 33 34 func TestDataFunc(t *testing.T) { 35 count := 0 36 source := cached.Func(func() ([]byte, string, error) { 37 count += 1 38 return []byte("source"), "source", nil 39 }) 40 if _, _, err := source.Get(); err != nil { 41 t.Fatalf("unexpected error: %v", err) 42 } 43 if _, _, err := source.Get(); err != nil { 44 t.Fatalf("unexpected error: %v", err) 45 } 46 if count != 2 { 47 t.Fatalf("Expected function called twice, called: %v", count) 48 } 49 } 50 51 func TestDataFuncError(t *testing.T) { 52 count := 0 53 source := cached.Func(func() ([]byte, string, error) { 54 count += 1 55 return nil, "", errors.New("source error") 56 }) 57 if _, _, err := source.Get(); err == nil { 58 t.Fatalf("expected error, found none") 59 } 60 if _, _, err := source.Get(); err == nil { 61 t.Fatalf("expected error, found none") 62 } 63 if count != 2 { 64 t.Fatalf("Expected function called twice, called: %v", count) 65 } 66 } 67 68 func TestDataFuncAlternate(t *testing.T) { 69 count := 0 70 source := cached.Func(func() ([]byte, string, error) { 71 count += 1 72 if count%2 == 0 { 73 return nil, "", errors.New("source error") 74 } 75 return []byte("source"), "source", nil 76 }) 77 if _, _, err := source.Get(); err != nil { 78 t.Fatalf("unexpected error: %v", err) 79 } 80 if _, _, err := source.Get(); err == nil { 81 t.Fatalf("expected error, found none") 82 } 83 if _, _, err := source.Get(); err != nil { 84 t.Fatalf("unexpected error: %v", err) 85 } 86 if _, _, err := source.Get(); err == nil { 87 t.Fatalf("expected error, found none") 88 } 89 if count != 4 { 90 t.Fatalf("Expected function called 4x, called: %v", count) 91 } 92 } 93 94 func TestOnce(t *testing.T) { 95 count := 0 96 source := cached.Once(cached.Func(func() ([]byte, string, error) { 97 count += 1 98 return []byte("source"), "source", nil 99 })) 100 if _, _, err := source.Get(); err != nil { 101 t.Fatalf("unexpected error: %v", err) 102 } 103 if _, _, err := source.Get(); err != nil { 104 t.Fatalf("unexpected error: %v", err) 105 } 106 if count != 1 { 107 t.Fatalf("Expected function called once, called: %v", count) 108 } 109 } 110 111 func TestOnceError(t *testing.T) { 112 count := 0 113 source := cached.Once(cached.Func(func() ([]byte, string, error) { 114 count += 1 115 return nil, "", errors.New("source error") 116 })) 117 if _, _, err := source.Get(); err == nil { 118 t.Fatalf("expected error, found none") 119 } 120 if _, _, err := source.Get(); err == nil { 121 t.Fatalf("expected error, found none") 122 } 123 if count != 1 { 124 t.Fatalf("Expected function called once, called: %v", count) 125 } 126 } 127 128 func TestResultGet(t *testing.T) { 129 source := cached.Static([]byte("source"), "etag") 130 value, etag, err := source.Get() 131 if err != nil { 132 t.Fatalf("unexpected error: %v", err) 133 } 134 if want := "source"; string(value) != want { 135 t.Fatalf("expected value %q, got %q", want, string(value)) 136 } 137 if want := "etag"; etag != want { 138 t.Fatalf("expected etag %q, got %q", want, etag) 139 } 140 } 141 142 func TestResultGetError(t *testing.T) { 143 source := cached.Result[[]byte]{Err: errors.New("source error")} 144 value, etag, err := source.Get() 145 if err == nil { 146 t.Fatalf("expected error, found none") 147 } 148 if value != nil { 149 t.Fatalf("expected nil value, got %v", value) 150 } 151 if etag != "" { 152 t.Fatalf("expected empty etag, got %q", etag) 153 } 154 } 155 156 func TestTransform(t *testing.T) { 157 sourceCount := 0 158 source := cached.Func(func() ([]byte, string, error) { 159 sourceCount += 1 160 return []byte("source"), "source", nil 161 }) 162 transformerCount := 0 163 transformer := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 164 transformerCount += 1 165 if err != nil { 166 return nil, "", err 167 } 168 return []byte("transformed " + string(value)), "transformed " + etag, nil 169 }, source) 170 if _, _, err := transformer.Get(); err != nil { 171 t.Fatalf("unexpected error: %v", err) 172 } 173 if _, _, err := transformer.Get(); err != nil { 174 t.Fatalf("unexpected error: %v", err) 175 } 176 if sourceCount != 2 { 177 t.Fatalf("Expected source function called twice, called: %v", sourceCount) 178 } 179 if transformerCount != 1 { 180 t.Fatalf("Expected transformer function called once, called: %v", transformerCount) 181 } 182 } 183 184 func TestTransformChained(t *testing.T) { 185 sourceCount := 0 186 source := cached.Func(func() ([]byte, string, error) { 187 sourceCount += 1 188 return []byte("source"), "source", nil 189 }) 190 transformer1Count := 0 191 transformer1 := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 192 transformer1Count += 1 193 if err != nil { 194 return nil, "", err 195 } 196 return []byte("transformed " + string(value)), etag, nil 197 }, source) 198 transformer2Count := 0 199 transformer2 := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 200 transformer2Count += 1 201 if err != nil { 202 return nil, "", err 203 } 204 return []byte("transformed " + string(value)), etag, nil 205 }, transformer1) 206 transformer3Count := 0 207 transformer3 := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 208 transformer3Count += 1 209 if err != nil { 210 return nil, "", err 211 } 212 return []byte("transformed " + string(value)), etag, nil 213 }, transformer2) 214 if _, _, err := transformer3.Get(); err != nil { 215 t.Fatalf("unexpected error: %v", err) 216 } 217 result, etag, err := transformer3.Get() 218 if err != nil { 219 t.Fatalf("unexpected error: %v", err) 220 } 221 if want := "transformed transformed transformed source"; string(result) != want { 222 t.Fatalf("expected data = %v, got %v", want, string(result)) 223 } 224 if want := "source"; etag != want { 225 t.Fatalf("expected etag = %v, got %v", want, etag) 226 } 227 if sourceCount != 2 { 228 t.Fatalf("Expected source function called twice, called: %v", sourceCount) 229 } 230 if transformer1Count != 1 { 231 t.Fatalf("Expected transformer function called once, called: %v", transformer1Count) 232 } 233 if transformer2Count != 1 { 234 t.Fatalf("Expected transformer function called once, called: %v", transformer2Count) 235 } 236 if transformer3Count != 1 { 237 t.Fatalf("Expected transformer function called once, called: %v", transformer3Count) 238 } 239 } 240 241 func TestTransformError(t *testing.T) { 242 sourceCount := 0 243 source := cached.Func(func() ([]byte, string, error) { 244 sourceCount += 1 245 return []byte("source"), "source", nil 246 }) 247 transformerCount := 0 248 transformer := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 249 transformerCount += 1 250 return nil, "", errors.New("transformer error") 251 }, source) 252 if _, _, err := transformer.Get(); err == nil { 253 t.Fatalf("expected error, none found") 254 } 255 if _, _, err := transformer.Get(); err == nil { 256 t.Fatalf("expected error, none found") 257 } 258 if sourceCount != 2 { 259 t.Fatalf("Expected source function called twice, called: %v", sourceCount) 260 } 261 if transformerCount != 2 { 262 t.Fatalf("Expected transformer function called twice, called: %v", transformerCount) 263 } 264 } 265 266 func TestTransformSourceError(t *testing.T) { 267 sourceCount := 0 268 source := cached.Func(func() ([]byte, string, error) { 269 sourceCount += 1 270 return nil, "", errors.New("source error") 271 }) 272 transformerCount := 0 273 transformer := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 274 transformerCount += 1 275 if err != nil { 276 return nil, "", err 277 } 278 return []byte("transformed " + string(value)), "transformed " + etag, nil 279 }, source) 280 if _, _, err := transformer.Get(); err == nil { 281 t.Fatalf("expected error, none found") 282 } 283 if _, _, err := transformer.Get(); err == nil { 284 t.Fatalf("expected error, none found") 285 } 286 if sourceCount != 2 { 287 t.Fatalf("Expected source function called twice, called: %v", sourceCount) 288 } 289 if transformerCount != 2 { 290 t.Fatalf("Expected transformer function called twice, called: %v", transformerCount) 291 } 292 } 293 294 func TestTransformAlternateSourceError(t *testing.T) { 295 sourceCount := 0 296 source := cached.Func(func() ([]byte, string, error) { 297 sourceCount += 1 298 if sourceCount%2 == 0 { 299 return nil, "", errors.New("source error") 300 } 301 return []byte("source"), "source", nil 302 }) 303 transformerCount := 0 304 transformer := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 305 transformerCount += 1 306 if err != nil { 307 return nil, "", err 308 } 309 return []byte("transformed " + string(value)), "transformed " + etag, err 310 }, source) 311 result, etag, err := transformer.Get() 312 if err != nil { 313 t.Fatalf("unexpected error: %v", err) 314 } 315 if want := "transformed source"; string(result) != want { 316 t.Fatalf("expected data = %v, got %v", want, string(result)) 317 } 318 if want := "transformed source"; etag != want { 319 t.Fatalf("expected etag = %v, got %v", want, etag) 320 } 321 if _, _, err := transformer.Get(); err == nil { 322 t.Fatalf("expected error, none found") 323 } 324 result, etag, err = transformer.Get() 325 if err != nil { 326 t.Fatalf("unexpected error: %v", err) 327 } 328 if want := "transformed source"; string(result) != want { 329 t.Fatalf("expected data = %v, got %v", want, string(result)) 330 } 331 if want := "transformed source"; etag != want { 332 t.Fatalf("expected etag = %v, got %v", want, etag) 333 } 334 if _, _, err := transformer.Get(); err == nil { 335 t.Fatalf("expected error, none found") 336 } 337 if sourceCount != 4 { 338 t.Fatalf("Expected source function called 4x, called: %v", sourceCount) 339 } 340 if transformerCount != 4 { 341 t.Fatalf("Expected transformer function called 4x, called: %v", transformerCount) 342 } 343 344 } 345 346 func TestMerge(t *testing.T) { 347 source1Count := 0 348 source1 := cached.Func(func() ([]byte, string, error) { 349 source1Count += 1 350 return []byte("source1"), "source1", nil 351 }) 352 source2Count := 0 353 source2 := cached.Func(func() ([]byte, string, error) { 354 source2Count += 1 355 return []byte("source2"), "source2", nil 356 }) 357 mergerCount := 0 358 merger := cached.Merge(func(results map[string]cached.Result[[]byte]) ([]byte, string, error) { 359 mergerCount += 1 360 d := []string{} 361 e := []string{} 362 for _, result := range results { 363 if result.Err != nil { 364 return nil, "", result.Err 365 } 366 d = append(d, string(result.Value)) 367 e = append(e, result.Etag) 368 } 369 sort.Strings(d) 370 sort.Strings(e) 371 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 372 }, map[string]cached.Value[[]byte]{ 373 "source1": source1, 374 "source2": source2, 375 }) 376 if _, _, err := merger.Get(); err != nil { 377 t.Fatalf("unexpected error: %v", err) 378 } 379 result, etag, err := merger.Get() 380 if err != nil { 381 t.Fatalf("unexpected error: %v", err) 382 } 383 if want := "merged source1 and source2"; string(result) != want { 384 t.Fatalf("expected data = %v, got %v", want, string(result)) 385 } 386 if want := "merged source1 and source2"; etag != want { 387 t.Fatalf("expected etag = %v, got %v", want, etag) 388 } 389 390 if source1Count != 2 { 391 t.Fatalf("Expected source function called twice, called: %v", source1Count) 392 } 393 if source2Count != 2 { 394 t.Fatalf("Expected source function called twice, called: %v", source2Count) 395 } 396 if mergerCount != 1 { 397 t.Fatalf("Expected merger function called once, called: %v", mergerCount) 398 } 399 } 400 401 func TestMergeError(t *testing.T) { 402 source1Count := 0 403 source1 := cached.Func(func() ([]byte, string, error) { 404 source1Count += 1 405 return []byte("source1"), "source1", nil 406 }) 407 source2Count := 0 408 source2 := cached.Func(func() ([]byte, string, error) { 409 source2Count += 1 410 return []byte("source2"), "source2", nil 411 }) 412 mergerCount := 0 413 merger := cached.Merge(func(results map[string]cached.Result[[]byte]) ([]byte, string, error) { 414 mergerCount += 1 415 return nil, "", errors.New("merger error") 416 }, map[string]cached.Value[[]byte]{ 417 "source1": source1, 418 "source2": source2, 419 }) 420 if _, _, err := merger.Get(); err == nil { 421 t.Fatalf("expected error, none found") 422 } 423 if _, _, err := merger.Get(); err == nil { 424 t.Fatalf("expected error, none found") 425 } 426 if source1Count != 2 { 427 t.Fatalf("Expected source function called twice, called: %v", source1Count) 428 } 429 if source2Count != 2 { 430 t.Fatalf("Expected source function called twice, called: %v", source2Count) 431 } 432 if mergerCount != 2 { 433 t.Fatalf("Expected merger function called twice, called: %v", mergerCount) 434 } 435 } 436 437 func TestMergeSourceError(t *testing.T) { 438 source1Count := 0 439 source1 := cached.Func(func() ([]byte, string, error) { 440 source1Count += 1 441 return nil, "", errors.New("source1 error") 442 }) 443 source2Count := 0 444 source2 := cached.Func(func() ([]byte, string, error) { 445 source2Count += 1 446 return []byte("source2"), "source2", nil 447 }) 448 mergerCount := 0 449 merger := cached.Merge(func(results map[string]cached.Result[[]byte]) ([]byte, string, error) { 450 mergerCount += 1 451 d := []string{} 452 e := []string{} 453 for _, result := range results { 454 if result.Err != nil { 455 return nil, "", result.Err 456 } 457 d = append(d, string(result.Value)) 458 e = append(e, result.Etag) 459 } 460 sort.Strings(d) 461 sort.Strings(e) 462 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 463 }, map[string]cached.Value[[]byte]{ 464 "source1": source1, 465 "source2": source2, 466 }) 467 if _, _, err := merger.Get(); err == nil { 468 t.Fatalf("expected error, none found") 469 } 470 if _, _, err := merger.Get(); err == nil { 471 t.Fatalf("expected error, none found") 472 } 473 if source1Count != 2 { 474 t.Fatalf("Expected source function called twice, called: %v", source1Count) 475 } 476 if source2Count != 2 { 477 t.Fatalf("Expected source function called twice, called: %v", source2Count) 478 } 479 if mergerCount != 2 { 480 t.Fatalf("Expected merger function called twice, called: %v", mergerCount) 481 } 482 } 483 484 func TestMergeAlternateSourceError(t *testing.T) { 485 source1Count := 0 486 source1 := cached.Func(func() ([]byte, string, error) { 487 source1Count += 1 488 if source1Count%2 == 0 { 489 return nil, "", errors.New("source1 error") 490 } else { 491 return []byte("source1"), "source1", nil 492 } 493 }) 494 source2Count := 0 495 source2 := cached.Func(func() ([]byte, string, error) { 496 source2Count += 1 497 return []byte("source2"), "source2", nil 498 }) 499 mergerCount := 0 500 merger := cached.Merge(func(results map[string]cached.Result[[]byte]) ([]byte, string, error) { 501 mergerCount += 1 502 d := []string{} 503 e := []string{} 504 for _, result := range results { 505 if result.Err != nil { 506 return nil, "", result.Err 507 } 508 d = append(d, string(result.Value)) 509 e = append(e, result.Etag) 510 } 511 sort.Strings(d) 512 sort.Strings(e) 513 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 514 }, map[string]cached.Value[[]byte]{ 515 "source1": source1, 516 "source2": source2, 517 }) 518 result, etag, err := merger.Get() 519 if err != nil { 520 t.Fatalf("unexpected error: %v", err) 521 } 522 if want := "merged source1 and source2"; string(result) != want { 523 t.Fatalf("expected data = %v, got %v", want, string(result)) 524 } 525 if want := "merged source1 and source2"; etag != want { 526 t.Fatalf("expected etag = %v, got %v", want, etag) 527 } 528 if _, _, err := merger.Get(); err == nil { 529 t.Fatalf("expected error, none found") 530 } 531 result, etag, err = merger.Get() 532 if err != nil { 533 t.Fatalf("unexpected error: %v", err) 534 } 535 if want := "merged source1 and source2"; string(result) != want { 536 t.Fatalf("expected data = %v, got %v", want, string(result)) 537 } 538 if want := "merged source1 and source2"; etag != want { 539 t.Fatalf("expected etag = %v, got %v", want, etag) 540 } 541 if _, _, err := merger.Get(); err == nil { 542 t.Fatalf("expected error, none found") 543 } 544 if source1Count != 4 { 545 t.Fatalf("Expected source function called 4x, called: %v", source1Count) 546 } 547 if source2Count != 4 { 548 t.Fatalf("Expected source function called 4x, called: %v", source2Count) 549 } 550 if mergerCount != 4 { 551 t.Fatalf("Expected merger function called 4x, called: %v", mergerCount) 552 } 553 } 554 555 func TestAtomic(t *testing.T) { 556 sourceDataCount := 0 557 sourceData := cached.Func(func() ([]byte, string, error) { 558 sourceDataCount += 1 559 return []byte("source"), "source", nil 560 }) 561 sourceData2Count := 0 562 sourceData2 := cached.Func(func() ([]byte, string, error) { 563 sourceData2Count += 1 564 return []byte("source2"), "source2", nil 565 }) 566 sourceErrCount := 0 567 sourceErr := cached.Func(func() ([]byte, string, error) { 568 sourceErrCount += 1 569 return nil, "", errors.New("source error") 570 }) 571 572 replaceable := &cached.Atomic[[]byte]{} 573 replaceable.Store(sourceErr) 574 if _, _, err := replaceable.Get(); err == nil { 575 t.Fatalf("expected error, found none") 576 } 577 578 replaceable.Store(sourceData) 579 result, etag, err := replaceable.Get() 580 if err != nil { 581 t.Fatalf("unexpected error: %v", err) 582 } 583 if want := "source"; string(result) != want { 584 t.Fatalf("expected data = %v, got %v", want, string(result)) 585 } 586 if want := "source"; etag != want { 587 t.Fatalf("expected etag = %v, got %v", want, etag) 588 } 589 590 // replace with the same thing, shouldn't change anything 591 replaceable.Store(sourceData) 592 result, etag, err = replaceable.Get() 593 if err != nil { 594 t.Fatalf("unexpected error: %v", err) 595 } 596 if want := "source"; string(result) != want { 597 t.Fatalf("expected data = %v, got %v", want, string(result)) 598 } 599 if want := "source"; etag != want { 600 t.Fatalf("expected etag = %v, got %v", want, etag) 601 } 602 603 // when replacing with an error source, we see the error again 604 replaceable.Store(sourceErr) 605 result, etag, err = replaceable.Get() 606 if err == nil { 607 t.Fatalf("unexpected success") 608 } 609 610 replaceable.Store(sourceData2) 611 result, etag, err = replaceable.Get() 612 if err != nil { 613 t.Fatalf("unexpected error: %v", err) 614 } 615 if want := "source2"; string(result) != want { 616 t.Fatalf("expected data = %v, got %v", want, string(result)) 617 } 618 if want := "source2"; etag != want { 619 t.Fatalf("expected etag = %v, got %v", want, etag) 620 } 621 if sourceDataCount != 2 { 622 t.Fatalf("Expected sourceData function called twice, called: %v", sourceDataCount) 623 } 624 if sourceData2Count != 1 { 625 t.Fatalf("Expected sourceData2 function called once, called: %v", sourceData2Count) 626 } 627 if sourceErrCount != 2 { 628 t.Fatalf("Expected error source function called once, called: %v", sourceErrCount) 629 } 630 } 631 632 func TestLastSuccess(t *testing.T) { 633 sourceDataCount := 0 634 sourceData := cached.Func(func() ([]byte, string, error) { 635 sourceDataCount += 1 636 return []byte("source"), "source", nil 637 }) 638 sourceData2Count := 0 639 sourceData2 := cached.Func(func() ([]byte, string, error) { 640 sourceData2Count += 1 641 return []byte("source2"), "source2", nil 642 }) 643 644 sourceErrCount := 0 645 sourceErr := cached.Func(func() ([]byte, string, error) { 646 sourceErrCount += 1 647 return nil, "", errors.New("source error") 648 }) 649 lastSuccess := &cached.LastSuccess[[]byte]{} 650 lastSuccess.Store(sourceErr) 651 if _, _, err := lastSuccess.Get(); err == nil { 652 t.Fatalf("expected error, found none") 653 } 654 lastSuccess.Store(sourceData) 655 result, etag, err := lastSuccess.Get() 656 if err != nil { 657 t.Fatalf("unexpected error: %v", err) 658 } 659 if want := "source"; string(result) != want { 660 t.Fatalf("expected data = %v, got %v", want, string(result)) 661 } 662 if want := "source"; etag != want { 663 t.Fatalf("expected etag = %v, got %v", want, etag) 664 } 665 // replace with the same thing, shouldn't change anything 666 lastSuccess.Store(sourceData) 667 result, etag, err = lastSuccess.Get() 668 if err != nil { 669 t.Fatalf("unexpected error: %v", err) 670 } 671 if want := "source"; string(result) != want { 672 t.Fatalf("expected data = %v, got %v", want, string(result)) 673 } 674 if want := "source"; etag != want { 675 t.Fatalf("expected etag = %v, got %v", want, etag) 676 } 677 // Even if we replace with something that fails, we continue to return the success. 678 lastSuccess.Store(sourceErr) 679 result, etag, err = lastSuccess.Get() 680 if err != nil { 681 t.Fatalf("unexpected error: %v", err) 682 } 683 if want := "source"; string(result) != want { 684 t.Fatalf("expected data = %v, got %v", want, string(result)) 685 } 686 if want := "source"; etag != want { 687 t.Fatalf("expected etag = %v, got %v", want, etag) 688 } 689 lastSuccess.Store(sourceData2) 690 result, etag, err = lastSuccess.Get() 691 if err != nil { 692 t.Fatalf("unexpected error: %v", err) 693 } 694 if want := "source2"; string(result) != want { 695 t.Fatalf("expected data = %v, got %v", want, string(result)) 696 } 697 if want := "source2"; etag != want { 698 t.Fatalf("expected etag = %v, got %v", want, etag) 699 } 700 if sourceDataCount != 2 { 701 t.Fatalf("Expected sourceData function called twice, called: %v", sourceDataCount) 702 } 703 if sourceData2Count != 1 { 704 t.Fatalf("Expected sourceData2 function called once, called: %v", sourceData2Count) 705 } 706 if sourceErrCount != 2 { 707 t.Fatalf("Expected error source function called once, called: %v", sourceErrCount) 708 } 709 } 710 711 func TestLastSuccessEtag(t *testing.T) { 712 lastSuccess := &cached.LastSuccess[bool]{} 713 lastSuccess.Store(cached.Func(func() (bool, string, error) { 714 return false, "hash", nil 715 })) 716 lastSuccess.Store(cached.Static(true, "hash2")) 717 result, etag, _ := lastSuccess.Get() 718 if actual := etag; actual != "hash2" { 719 t.Fatalf(`expected "hash2", got %q`, actual) 720 } 721 if result != true { 722 t.Fatal(`expected "true", got "false"`) 723 } 724 } 725 726 func TestLastSuccessAlternateError(t *testing.T) { 727 sourceCount := 0 728 source := cached.Func(func() ([]byte, string, error) { 729 sourceCount += 1 730 if sourceCount%2 == 0 { 731 return nil, "", errors.New("source error") 732 } else { 733 return []byte("source"), "source", nil 734 } 735 }) 736 lastSuccess := &cached.LastSuccess[[]byte]{} 737 lastSuccess.Store(source) 738 result, etag, err := lastSuccess.Get() 739 if err != nil { 740 t.Fatalf("unexpected error: %v", err) 741 } 742 if want := "source"; string(result) != want { 743 t.Fatalf("expected data = %v, got %v", want, string(result)) 744 } 745 if want := "source"; etag != want { 746 t.Fatalf("expected etag = %v, got %v", want, etag) 747 } 748 result, etag, err = lastSuccess.Get() 749 if err != nil { 750 t.Fatalf("unexpected error: %v", err) 751 } 752 if want := "source"; string(result) != want { 753 t.Fatalf("expected data = %v, got %v", want, string(result)) 754 } 755 if want := "source"; etag != want { 756 t.Fatalf("expected etag = %v, got %v", want, etag) 757 } 758 result, etag, err = lastSuccess.Get() 759 if err != nil { 760 t.Fatalf("unexpected error: %v", err) 761 } 762 if want := "source"; string(result) != want { 763 t.Fatalf("expected data = %v, got %v", want, string(result)) 764 } 765 if want := "source"; etag != want { 766 t.Fatalf("expected etag = %v, got %v", want, etag) 767 } 768 result, etag, err = lastSuccess.Get() 769 if err != nil { 770 t.Fatalf("unexpected error: %v", err) 771 } 772 if want := "source"; string(result) != want { 773 t.Fatalf("expected data = %v, got %v", want, string(result)) 774 } 775 if want := "source"; etag != want { 776 t.Fatalf("expected etag = %v, got %v", want, etag) 777 } 778 if sourceCount != 4 { 779 t.Fatalf("Expected sourceData function called 4x, called: %v", sourceCount) 780 } 781 } 782 783 func TestLastSuccessWithTransformer(t *testing.T) { 784 lastSuccess := &cached.LastSuccess[[]byte]{} 785 lastSuccess.Store(cached.Static([]byte("source"), "source")) 786 transformerCount := 0 787 transformed := cached.Transform[[]byte](func(value []byte, etag string, err error) ([]byte, string, error) { 788 transformerCount += 1 789 if err != nil { 790 return nil, "", err 791 } 792 return []byte("transformed " + string(value)), "transformed " + etag, nil 793 }, lastSuccess) 794 result, etag, err := transformed.Get() 795 if err != nil { 796 t.Fatalf("unexpected error: %v", err) 797 } 798 result, etag, err = transformed.Get() 799 if err != nil { 800 t.Fatalf("unexpected error: %v", err) 801 } 802 if want := "transformed source"; string(result) != want { 803 t.Fatalf("expected data = %v, got %v", want, string(result)) 804 } 805 if want := "transformed source"; etag != want { 806 t.Fatalf("expected etag = %v, got %v", want, etag) 807 } 808 // replace with new cache, transformer shouldn't be affected (or called) 809 lastSuccess.Store(cached.Static([]byte("source"), "source")) 810 result, etag, err = transformed.Get() 811 if err != nil { 812 t.Fatalf("unexpected error: %v", err) 813 } 814 result, etag, err = transformed.Get() 815 if err != nil { 816 t.Fatalf("unexpected error: %v", err) 817 } 818 if want := "transformed source"; string(result) != want { 819 t.Fatalf("expected data = %v, got %v", want, string(result)) 820 } 821 if want := "transformed source"; etag != want { 822 t.Fatalf("expected etag = %v, got %v", want, etag) 823 } 824 // replace with failing cache, transformer should still not be affected (or called) 825 lastSuccess.Store(cached.Result[[]byte]{Err: errors.New("source error")}) 826 result, etag, err = transformed.Get() 827 if err != nil { 828 t.Fatalf("unexpected error: %v", err) 829 } 830 result, etag, err = transformed.Get() 831 if err != nil { 832 t.Fatalf("unexpected error: %v", err) 833 } 834 if want := "transformed source"; string(result) != want { 835 t.Fatalf("expected data = %v, got %v", want, string(result)) 836 } 837 if want := "transformed source"; etag != want { 838 t.Fatalf("expected etag = %v, got %v", want, etag) 839 } 840 841 if transformerCount != 1 { 842 t.Fatalf("Expected transformer function called once, called: %v", transformerCount) 843 } 844 } 845 846 // Here is an example of how one can write a cache that will constantly 847 // be pulled, while actually recomputing the results only as needed. 848 func Example() { 849 // Merge Json is a replaceable cache, since we'll want it to 850 // change a few times. 851 mergeJson := &cached.LastSuccess[[]byte]{} 852 853 one := cached.Once(cached.Func(func() ([]byte, string, error) { 854 // This one is computed lazily, only when requested, and only once. 855 return []byte("one"), "one", nil 856 })) 857 two := cached.Func(func() ([]byte, string, error) { 858 // This cache is re-computed every time. 859 return []byte("two"), "two", nil 860 }) 861 // This cache is computed once, and is not lazy at all. 862 three := cached.Static([]byte("three"), "three") 863 864 // This cache will allow us to replace a branch of the tree 865 // efficiently. 866 867 lastSuccess := &cached.LastSuccess[[]byte]{} 868 lastSuccess.Store(cached.Static([]byte("four"), "four")) 869 870 merger := func(results map[string]cached.Result[[]byte]) ([]byte, string, error) { 871 var out = []json.RawMessage{} 872 var resultEtag string 873 for _, result := range results { 874 if result.Err != nil { 875 return nil, "", result.Err 876 } 877 resultEtag += result.Etag 878 out = append(out, result.Value) 879 } 880 data, err := json.Marshal(out) 881 if err != nil { 882 return nil, "", err 883 } 884 return data, resultEtag, nil 885 } 886 887 mergeJson.Store(cached.Merge(merger, map[string]cached.Value[[]byte]{ 888 "one": one, 889 "two": two, 890 "three": three, 891 "replaceable": lastSuccess, 892 })) 893 894 // Create a new cache that indents a buffer. This should only be 895 // called if the buffer has changed. 896 indented := cached.Transform[[]byte](func(js []byte, etag string, err error) ([]byte, string, error) { 897 // Get the json from the previous layer of cache, before 898 // we indent. 899 if err != nil { 900 return nil, "", err 901 } 902 var out bytes.Buffer 903 json.Indent(&out, js, "", "\t") 904 return out.Bytes(), etag, nil 905 }, mergeJson) 906 907 // We have "clients" that constantly pulls the indented format. 908 go func() { 909 for { 910 if _, _, err := indented.Get(); err != nil { 911 panic(fmt.Errorf("invalid error: %v", err)) 912 } 913 } 914 }() 915 916 failure := cached.Result[[]byte]{Err: errors.New("Invalid cache!")} 917 // Insert a new sub-cache that fails, it should just be ignored. 918 mergeJson.Store(cached.Merge(merger, map[string]cached.Value[[]byte]{ 919 "one": one, 920 "two": two, 921 "three": three, 922 "replaceable": lastSuccess, 923 "failure": failure, 924 })) 925 926 // We can replace just a branch of the dependency tree. 927 lastSuccess.Store(cached.Static([]byte("five"), "five")) 928 929 // We can replace to remove the failure and one of the sub-cached. 930 mergeJson.Store(cached.Merge(merger, map[string]cached.Value[[]byte]{ 931 "one": one, 932 "two": two, 933 "replaceable": lastSuccess, 934 })) 935 } 936 937 func TestListMerger(t *testing.T) { 938 source1Count := 0 939 source1 := cached.Func(func() ([]byte, string, error) { 940 source1Count += 1 941 return []byte("source1"), "source1", nil 942 }) 943 source2Count := 0 944 source2 := cached.Func(func() ([]byte, string, error) { 945 source2Count += 1 946 return []byte("source2"), "source2", nil 947 }) 948 mergerCount := 0 949 merger := cached.MergeList(func(results []cached.Result[[]byte]) ([]byte, string, error) { 950 mergerCount += 1 951 d := []string{} 952 e := []string{} 953 for _, result := range results { 954 if result.Err != nil { 955 return nil, "", result.Err 956 } 957 d = append(d, string(result.Value)) 958 e = append(e, result.Etag) 959 } 960 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 961 }, []cached.Value[[]byte]{ 962 source1, source2, 963 }) 964 if _, _, err := merger.Get(); err != nil { 965 t.Fatalf("unexpected error: %v", err) 966 } 967 result, etag, err := merger.Get() 968 if err != nil { 969 t.Fatalf("unexpected error: %v", err) 970 } 971 if want := "merged source1 and source2"; string(result) != want { 972 t.Fatalf("expected data = %v, got %v", want, string(result)) 973 } 974 if want := "merged source1 and source2"; etag != want { 975 t.Fatalf("expected etag = %v, got %v", want, etag) 976 } 977 978 if source1Count != 2 { 979 t.Fatalf("Expected source function called twice, called: %v", source1Count) 980 } 981 if source2Count != 2 { 982 t.Fatalf("Expected source function called twice, called: %v", source2Count) 983 } 984 if mergerCount != 1 { 985 t.Fatalf("Expected merger function called once, called: %v", mergerCount) 986 } 987 } 988 989 func TestMergeListSourceError(t *testing.T) { 990 source1Count := 0 991 source1 := cached.Func(func() ([]byte, string, error) { 992 source1Count += 1 993 return nil, "", errors.New("source1 error") 994 }) 995 source2Count := 0 996 source2 := cached.Func(func() ([]byte, string, error) { 997 source2Count += 1 998 return []byte("source2"), "source2", nil 999 }) 1000 mergerCount := 0 1001 merger := cached.MergeList(func(results []cached.Result[[]byte]) ([]byte, string, error) { 1002 mergerCount += 1 1003 d := []string{} 1004 e := []string{} 1005 for _, result := range results { 1006 if result.Err != nil { 1007 return nil, "", result.Err 1008 } 1009 d = append(d, string(result.Value)) 1010 e = append(e, result.Etag) 1011 } 1012 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 1013 }, []cached.Value[[]byte]{ 1014 source1, source2, 1015 }) 1016 if _, _, err := merger.Get(); err == nil { 1017 t.Fatalf("expected error, none found") 1018 } 1019 if _, _, err := merger.Get(); err == nil { 1020 t.Fatalf("expected error, none found") 1021 } 1022 if source1Count != 2 { 1023 t.Fatalf("Expected source function called twice, called: %v", source1Count) 1024 } 1025 if source2Count != 2 { 1026 t.Fatalf("Expected source function called twice, called: %v", source2Count) 1027 } 1028 if mergerCount != 2 { 1029 t.Fatalf("Expected merger function called twice, called: %v", mergerCount) 1030 } 1031 } 1032 1033 func TestMergeListAlternateSourceError(t *testing.T) { 1034 source1Count := 0 1035 source1 := cached.Func(func() ([]byte, string, error) { 1036 source1Count += 1 1037 if source1Count%2 == 0 { 1038 return nil, "", errors.New("source1 error") 1039 } else { 1040 return []byte("source1"), "source1", nil 1041 } 1042 }) 1043 source2Count := 0 1044 source2 := cached.Func(func() ([]byte, string, error) { 1045 source2Count += 1 1046 return []byte("source2"), "source2", nil 1047 }) 1048 mergerCount := 0 1049 merger := cached.MergeList(func(results []cached.Result[[]byte]) ([]byte, string, error) { 1050 mergerCount += 1 1051 d := []string{} 1052 e := []string{} 1053 for _, result := range results { 1054 if result.Err != nil { 1055 return nil, "", result.Err 1056 } 1057 d = append(d, string(result.Value)) 1058 e = append(e, result.Etag) 1059 } 1060 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 1061 }, []cached.Value[[]byte]{ 1062 source1, source2, 1063 }) 1064 result, etag, err := merger.Get() 1065 if err != nil { 1066 t.Fatalf("unexpected error: %v", err) 1067 } 1068 if want := "merged source1 and source2"; string(result) != want { 1069 t.Fatalf("expected data = %v, got %v", want, string(result)) 1070 } 1071 if want := "merged source1 and source2"; etag != want { 1072 t.Fatalf("expected etag = %v, got %v", want, etag) 1073 } 1074 if _, _, err := merger.Get(); err == nil { 1075 t.Fatalf("expected error, none found") 1076 } 1077 result, etag, err = merger.Get() 1078 if err != nil { 1079 t.Fatalf("unexpected error: %v", err) 1080 } 1081 if want := "merged source1 and source2"; string(result) != want { 1082 t.Fatalf("expected data = %v, got %v", want, string(result)) 1083 } 1084 if want := "merged source1 and source2"; etag != want { 1085 t.Fatalf("expected etag = %v, got %v", want, etag) 1086 } 1087 if _, _, err := merger.Get(); err == nil { 1088 t.Fatalf("expected error, none found") 1089 } 1090 if source1Count != 4 { 1091 t.Fatalf("Expected source function called 4x, called: %v", source1Count) 1092 } 1093 if source2Count != 4 { 1094 t.Fatalf("Expected source function called 4x, called: %v", source2Count) 1095 } 1096 if mergerCount != 4 { 1097 t.Fatalf("Expected merger function called 4x, called: %v", mergerCount) 1098 } 1099 } 1100 1101 func TestListDAG(t *testing.T) { 1102 source := cached.Func(func() ([]byte, string, error) { 1103 return []byte("source"), "source", nil 1104 }) 1105 transformer1 := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 1106 if err != nil { 1107 return nil, "", err 1108 } 1109 return []byte("transformed1 " + string(value)), "transformed1 " + etag, nil 1110 }, source) 1111 transformer2 := cached.Transform(func(value []byte, etag string, err error) ([]byte, string, error) { 1112 if err != nil { 1113 return nil, "", err 1114 } 1115 return []byte("transformed2 " + string(value)), "transformed2 " + etag, nil 1116 }, source) 1117 merger := cached.MergeList(func(results []cached.Result[[]byte]) ([]byte, string, error) { 1118 d := []string{} 1119 e := []string{} 1120 for _, result := range results { 1121 if result.Err != nil { 1122 return nil, "", result.Err 1123 } 1124 d = append(d, string(result.Value)) 1125 e = append(e, result.Etag) 1126 } 1127 return []byte("merged " + strings.Join(d, " and ")), "merged " + strings.Join(e, " and "), nil 1128 }, []cached.Value[[]byte]{ 1129 transformer1, transformer2, 1130 }) 1131 result, etag, err := merger.Get() 1132 if err != nil { 1133 t.Fatalf("Unexpected error: %v", err) 1134 } 1135 if want := "merged transformed1 source and transformed2 source"; string(result) != want { 1136 t.Fatalf("expected data = %v, got %v", want, string(result)) 1137 } 1138 if want := "merged transformed1 source and transformed2 source"; etag != want { 1139 t.Fatalf("expected etag = %v, got %v", want, etag) 1140 } 1141 } 1142 1143 func randomString(length uint) string { 1144 bytes := make([]byte, 6) 1145 rand.Read(bytes) 1146 return string(bytes) 1147 1148 } 1149 1150 func NewRandomSource() cached.Value[int64] { 1151 return cached.Once(cached.Func(func() (int64, string, error) { 1152 bytes := make([]byte, 6) 1153 rand.Read(bytes) 1154 return rand.Int63(), randomString(10), nil 1155 })) 1156 } 1157 1158 func repeatedGet(data cached.Value[int64], end time.Time, wg *sync.WaitGroup) { 1159 for time.Now().Before(end) { 1160 _, _, _ = data.Get() 1161 } 1162 wg.Done() 1163 } 1164 1165 func TestThreadSafe(t *testing.T) { 1166 end := time.Now().Add(time.Second) 1167 wg := sync.WaitGroup{} 1168 static := NewRandomSource() 1169 wg.Add(1) 1170 go repeatedGet(static, end, &wg) 1171 result := cached.Static(rand.Int63(), randomString(10)) 1172 wg.Add(1) 1173 go repeatedGet(result, end, &wg) 1174 replaceable := &cached.LastSuccess[int64]{} 1175 replaceable.Store(NewRandomSource()) 1176 wg.Add(1) 1177 go repeatedGet(replaceable, end, &wg) 1178 wg.Add(1) 1179 go func(r cached.Replaceable[int64], end time.Time, wg *sync.WaitGroup) { 1180 for time.Now().Before(end) { 1181 r.Store(NewRandomSource()) 1182 } 1183 wg.Done() 1184 }(replaceable, end, &wg) 1185 merger := cached.Merge(func(results map[string]cached.Result[int64]) (int64, string, error) { 1186 sum := int64(0) 1187 for _, result := range results { 1188 sum += result.Value 1189 } 1190 return sum, randomString(10), nil 1191 }, map[string]cached.Value[int64]{ 1192 "one": NewRandomSource(), 1193 "two": NewRandomSource(), 1194 }) 1195 wg.Add(1) 1196 go repeatedGet(merger, end, &wg) 1197 transformer := cached.Transform(func(value int64, etag string, err error) (int64, string, error) { 1198 return value + 5, randomString(10), nil 1199 }, NewRandomSource()) 1200 wg.Add(1) 1201 go repeatedGet(transformer, end, &wg) 1202 1203 listmerger := cached.MergeList(func(results []cached.Result[int64]) (int64, string, error) { 1204 sum := int64(0) 1205 for i := range results { 1206 sum += results[i].Value 1207 } 1208 return sum, randomString(10), nil 1209 }, []cached.Value[int64]{static, result, replaceable, merger, transformer}) 1210 wg.Add(1) 1211 go repeatedGet(listmerger, end, &wg) 1212 1213 wg.Wait() 1214 }