github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/scaffold_test.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "net/http" 10 "os" 11 "path/filepath" 12 "strings" 13 "sync" 14 "testing" 15 "time" 16 17 gcemd "cloud.google.com/go/compute/metadata" 18 "github.com/hashicorp/go-multierror" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "google.golang.org/api/option" 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/credentials/insecure" 24 25 "github.com/onflow/flow-go/cmd/bootstrap/utils" 26 "github.com/onflow/flow-go/model/bootstrap" 27 "github.com/onflow/flow-go/module" 28 "github.com/onflow/flow-go/module/component" 29 "github.com/onflow/flow-go/module/irrecoverable" 30 "github.com/onflow/flow-go/module/profiler" 31 p2pbuilder "github.com/onflow/flow-go/network/p2p/builder" 32 "github.com/onflow/flow-go/utils/unittest" 33 ) 34 35 // TestLoadSecretsEncryptionKey checks that the key file is read correctly if it exists 36 // and returns the expected sentinel error if it does not exist. 37 func TestLoadSecretsEncryptionKey(t *testing.T) { 38 myID := unittest.IdentifierFixture() 39 40 unittest.RunWithTempDir(t, func(dir string) { 41 path := filepath.Join(dir, fmt.Sprintf(bootstrap.PathSecretsEncryptionKey, myID)) 42 43 t.Run("should return ErrNotExist if file doesn't exist", func(t *testing.T) { 44 require.NoFileExists(t, path) 45 _, err := loadSecretsEncryptionKey(dir, myID) 46 assert.Error(t, err) 47 assert.True(t, errors.Is(err, os.ErrNotExist)) 48 }) 49 50 t.Run("should return key and no error if file exists", func(t *testing.T) { 51 err := os.MkdirAll(filepath.Join(dir, bootstrap.DirPrivateRoot, fmt.Sprintf("private-node-info_%v", myID)), 0700) 52 require.NoError(t, err) 53 key, err := utils.GenerateSecretsDBEncryptionKey() 54 require.NoError(t, err) 55 err = os.WriteFile(path, key, 0700) 56 require.NoError(t, err) 57 58 data, err := loadSecretsEncryptionKey(dir, myID) 59 assert.NoError(t, err) 60 assert.Equal(t, key, data) 61 }) 62 }) 63 } 64 65 // Test the components are started in the correct order, and are run serially 66 func TestComponentsRunSerially(t *testing.T) { 67 ctx, cancel := context.WithCancel(context.Background()) 68 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 69 70 nb := FlowNode("scaffold test") 71 nb.componentBuilder = component.NewComponentManagerBuilder() 72 73 logger := &testLog{} 74 75 name1 := "component 1" 76 nb.Component(name1, func(node *NodeConfig) (module.ReadyDoneAware, error) { 77 logger.Logf("%s initialized", name1) 78 return newMockReadyDone(logger, name1), nil 79 }) 80 81 name2 := "component 2" 82 nb.Component(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) { 83 logger.Logf("%s initialized", name2) 84 c := newMockComponent(logger, name2) 85 c.startFn = func(ctx irrecoverable.SignalerContext, name string) { 86 // add delay to test components are run serially 87 time.Sleep(5 * time.Millisecond) 88 } 89 return c, nil 90 }) 91 92 name3 := "component 3" 93 nb.Component(name3, func(node *NodeConfig) (module.ReadyDoneAware, error) { 94 logger.Logf("%s initialized", name3) 95 return newMockReadyDone(logger, name3), nil 96 }) 97 98 err := nb.handleComponents() 99 assert.NoError(t, err) 100 101 cm := nb.componentBuilder.Build() 102 103 cm.Start(signalerCtx) 104 <-cm.Ready() 105 cancel() 106 <-cm.Done() 107 108 logs := logger.logs 109 110 assert.Len(t, logs, 10) 111 112 // components are initialized in a specific order, so check that the order is correct 113 startLogs := logs[:len(logs)-3] 114 assert.Equal(t, []string{ 115 "component 1 initialized", 116 "component 1 ready", 117 "component 2 initialized", 118 "component 2 started", 119 "component 2 ready", 120 "component 3 initialized", 121 "component 3 ready", 122 }, startLogs) 123 124 // components are stopped via context cancellation, so the specific order is random 125 doneLogs := logs[len(logs)-3:] 126 assert.ElementsMatch(t, []string{ 127 "component 1 done", 128 "component 2 done", 129 "component 3 done", 130 }, doneLogs) 131 } 132 133 func TestPostShutdown(t *testing.T) { 134 nb := FlowNode("scaffold test") 135 136 logger := testLog{} 137 138 err1 := errors.New("error 1") 139 err3 := errors.New("error 3") 140 errExpected := multierror.Append(&multierror.Error{}, err1, err3) 141 nb. 142 ShutdownFunc(func() error { 143 logger.Log("shutdown 1") 144 return err1 145 }). 146 ShutdownFunc(func() error { 147 logger.Log("shutdown 2") 148 return nil 149 }). 150 ShutdownFunc(func() error { 151 logger.Log("shutdown 3") 152 return err3 153 }) 154 155 err := nb.postShutdown() 156 assert.EqualError(t, err, errExpected.Error()) 157 158 logs := logger.logs 159 assert.Len(t, logs, 3) 160 assert.Equal(t, []string{ 161 "shutdown 1", 162 "shutdown 2", 163 "shutdown 3", 164 }, logs) 165 } 166 167 func TestOverrideComponent(t *testing.T) { 168 ctx, cancel := context.WithCancel(context.Background()) 169 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 170 171 nb := FlowNode("scaffold test") 172 nb.componentBuilder = component.NewComponentManagerBuilder() 173 174 logger := &testLog{} 175 176 name1 := "component 1" 177 nb.Component(name1, func(node *NodeConfig) (module.ReadyDoneAware, error) { 178 logger.Logf("%s initialized", name1) 179 return newMockReadyDone(logger, name1), nil 180 }) 181 182 name2 := "component 2" 183 nb.Component(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) { 184 logger.Logf("%s initialized", name2) 185 return newMockReadyDone(logger, name2), nil 186 }) 187 188 name3 := "component 3" 189 nb.Component(name3, func(node *NodeConfig) (module.ReadyDoneAware, error) { 190 logger.Logf("%s initialized", name3) 191 return newMockReadyDone(logger, name3), nil 192 }) 193 194 // Overrides second component 195 nb.OverrideComponent(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) { 196 logger.Logf("%s overridden", name2) 197 return newMockReadyDone(logger, name2), nil 198 }) 199 200 err := nb.handleComponents() 201 assert.NoError(t, err) 202 203 cm := nb.componentBuilder.Build() 204 205 cm.Start(signalerCtx) 206 207 <-cm.Ready() 208 209 logs := logger.logs 210 211 assert.Len(t, logs, 6) 212 213 // components are initialized in a specific order, so check that the order is correct 214 assert.Equal(t, []string{ 215 "component 1 initialized", 216 "component 1 ready", 217 "component 2 overridden", // overridden version of 2 should be initialized. 218 "component 2 ready", 219 "component 3 initialized", 220 "component 3 ready", 221 }, logs) 222 223 cancel() 224 <-cm.Done() 225 } 226 227 func TestOverrideModules(t *testing.T) { 228 ctx, cancel := context.WithCancel(context.Background()) 229 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 230 231 nb := FlowNode("scaffold test") 232 nb.componentBuilder = component.NewComponentManagerBuilder() 233 234 logger := &testLog{} 235 236 name1 := "module 1" 237 nb.Module(name1, func(nodeConfig *NodeConfig) error { 238 logger.Logf("%s initialized", name1) 239 return nil 240 }) 241 242 name2 := "module 2" 243 nb.Module(name2, func(nodeConfig *NodeConfig) error { 244 logger.Logf("%s initialized", name2) 245 return nil 246 }) 247 248 name3 := "module 3" 249 nb.Module(name3, func(nodeConfig *NodeConfig) error { 250 logger.Logf("%s initialized", name3) 251 return nil 252 }) 253 254 // Overrides second module 255 nb.OverrideModule(name2, func(nodeConfig *NodeConfig) error { 256 logger.Logf("%s overridden", name2) 257 return nil 258 }) 259 260 err := nb.handleModules() 261 assert.NoError(t, err) 262 263 cm := nb.componentBuilder.Build() 264 require.NoError(t, err) 265 266 cm.Start(signalerCtx) 267 268 <-cm.Ready() 269 270 logs := logger.logs 271 272 assert.Len(t, logs, 3) 273 274 // components are initialized in a specific order, so check that the order is correct 275 assert.Equal(t, []string{ 276 "module 1 initialized", 277 "module 2 overridden", // overridden version of 2 should be initialized. 278 "module 3 initialized", 279 }, logs) 280 281 cancel() 282 <-cm.Done() 283 } 284 285 type testComponentDefinition struct { 286 name string 287 factory ReadyDoneFactory 288 errorHandler component.OnError 289 } 290 291 func runRestartableTest(t *testing.T, components []testComponentDefinition, expectedErr error, shutdown <-chan struct{}) { 292 ctx, cancel := context.WithCancel(context.Background()) 293 signalerCtx, errChan := irrecoverable.WithSignaler(ctx) 294 295 go func() { 296 select { 297 case <-ctx.Done(): 298 return 299 case err := <-errChan: 300 if expectedErr == nil { 301 assert.NoError(t, err, "unexpected unhandled irrecoverable error") 302 } else { 303 assert.ErrorIs(t, err, expectedErr) 304 } 305 } 306 }() 307 308 nb := FlowNode("scaffold test") 309 nb.componentBuilder = component.NewComponentManagerBuilder() 310 311 for _, c := range components { 312 if c.errorHandler == nil { 313 nb.Component(c.name, c.factory) 314 } else { 315 nb.RestartableComponent(c.name, c.factory, c.errorHandler) 316 } 317 } 318 319 err := nb.handleComponents() 320 assert.NoError(t, err) 321 322 cm := nb.componentBuilder.Build() 323 324 cm.Start(signalerCtx) 325 326 <-shutdown 327 cancel() 328 329 <-cm.Done() 330 } 331 332 func TestRestartableRestartsSuccessfully(t *testing.T) { 333 334 logger := &testLog{} 335 shutdown := make(chan struct{}) 336 337 name := "component 1" 338 err := fmt.Errorf("%s error", name) 339 340 starts := 0 341 factory := func(node *NodeConfig) (module.ReadyDoneAware, error) { 342 logger.Logf("%s initialized", name) 343 c := newMockComponent(logger, name) 344 c.startFn = func(signalCtx irrecoverable.SignalerContext, name string) { 345 go func() { 346 <-c.Ready() 347 starts++ 348 if starts == 1 { 349 signalCtx.Throw(err) 350 } 351 close(shutdown) 352 }() 353 } 354 return c, nil 355 } 356 357 runRestartableTest(t, []testComponentDefinition{ 358 { 359 name: name, 360 factory: factory, 361 errorHandler: testErrorHandler(logger, err), 362 }, 363 }, nil, shutdown) 364 365 assert.Equal(t, []string{ 366 "component 1 initialized", 367 "component 1 started", 368 "component 1 ready", 369 "component 1 done", 370 "handled error: component 1 error", 371 "component 1 initialized", 372 "component 1 started", 373 "component 1 ready", 374 "component 1 done", 375 }, logger.logs) 376 } 377 378 func TestRestartableStopsSuccessfully(t *testing.T) { 379 logger := &testLog{} 380 shutdown := make(chan struct{}) 381 382 name := "component 1" 383 err := fmt.Errorf("%s error", name) 384 unexpectedErr := fmt.Errorf("%s unexpected error", name) 385 386 starts := 0 387 factory := func(node *NodeConfig) (module.ReadyDoneAware, error) { 388 logger.Logf("%s initialized", name) 389 c := newMockComponent(logger, name) 390 c.startFn = func(signalCtx irrecoverable.SignalerContext, name string) { 391 go func() { 392 <-c.Ready() 393 starts++ 394 if starts < 2 { 395 signalCtx.Throw(err) 396 } 397 if starts == 2 { 398 defer close(shutdown) 399 signalCtx.Throw(unexpectedErr) 400 } 401 }() 402 } 403 return c, nil 404 } 405 406 runRestartableTest(t, []testComponentDefinition{ 407 { 408 name: name, 409 factory: factory, 410 errorHandler: testErrorHandler(logger, err), 411 }, 412 }, unexpectedErr, shutdown) 413 414 assert.Equal(t, []string{ 415 "component 1 initialized", 416 "component 1 started", 417 "component 1 ready", 418 "component 1 done", 419 "handled error: component 1 error", 420 "component 1 initialized", 421 "component 1 started", 422 "component 1 ready", 423 "component 1 done", 424 "handled unexpected error: component 1 unexpected error", 425 }, logger.logs) 426 } 427 428 func TestRestartableWithMultipleComponents(t *testing.T) { 429 logger := &testLog{} 430 shutdown := make(chan struct{}) 431 432 // synchronization is needed since RestartableComponents are non-blocking 433 readyComponents := sync.WaitGroup{} 434 readyComponents.Add(3) 435 436 c1 := func() testComponentDefinition { 437 name := "component 1" 438 factory := func(node *NodeConfig) (module.ReadyDoneAware, error) { 439 logger.Logf("%s initialized", name) 440 c := newMockReadyDone(logger, name) 441 c.readyFn = func(name string) { 442 // delay to demonstrate that components are started serially 443 time.Sleep(5 * time.Millisecond) 444 readyComponents.Done() 445 } 446 return c, nil 447 } 448 449 return testComponentDefinition{ 450 name: name, 451 factory: factory, 452 } 453 } 454 455 c2Initialized := make(chan struct{}) 456 c2 := func() testComponentDefinition { 457 name := "component 2" 458 err := fmt.Errorf("%s error", name) 459 factory := func(node *NodeConfig) (module.ReadyDoneAware, error) { 460 defer close(c2Initialized) 461 logger.Logf("%s initialized", name) 462 c := newMockComponent(logger, name) 463 c.startFn = func(ctx irrecoverable.SignalerContext, name string) { 464 // delay to demonstrate the RestartableComponent startup is non-blocking 465 time.Sleep(5 * time.Millisecond) 466 } 467 c.readyFn = func(name string) { 468 readyComponents.Done() 469 } 470 return c, nil 471 } 472 473 return testComponentDefinition{ 474 name: name, 475 factory: factory, 476 errorHandler: testErrorHandler(logger, err), 477 } 478 } 479 480 c3 := func() testComponentDefinition { 481 name := "component 3" 482 err := fmt.Errorf("%s error", name) 483 starts := 0 484 factory := func(node *NodeConfig) (module.ReadyDoneAware, error) { 485 logger.Logf("%s initialized", name) 486 c := newMockComponent(logger, name) 487 c.startFn = func(signalCtx irrecoverable.SignalerContext, name string) { 488 go func() { 489 <-c.Ready() 490 starts++ 491 if starts == 1 { 492 signalCtx.Throw(err) 493 } 494 <-c2Initialized // can't use ready since it may not be initialized yet 495 readyComponents.Done() 496 }() 497 } 498 return c, nil 499 } 500 501 return testComponentDefinition{ 502 name: name, 503 factory: factory, 504 errorHandler: testErrorHandler(logger, err), 505 } 506 } 507 508 go func() { 509 readyComponents.Wait() 510 close(shutdown) 511 }() 512 513 runRestartableTest(t, []testComponentDefinition{c1(), c2(), c3()}, nil, shutdown) 514 515 logs := logger.logs 516 517 // make sure component 1 is started and ready before any other components start 518 assert.Equal(t, []string{ 519 "component 1 initialized", 520 "component 1 ready", 521 }, logs[:2]) 522 523 // now split logs by component, and verify we got the right messages/order 524 component1 := []string{} 525 component2 := []string{} 526 component3 := []string{} 527 unexpected := []string{} 528 for _, l := range logs { 529 switch { 530 case strings.Contains(l, "component 1"): 531 component1 = append(component1, l) 532 case strings.Contains(l, "component 2"): 533 component2 = append(component2, l) 534 case strings.Contains(l, "component 3"): 535 component3 = append(component3, l) 536 default: 537 unexpected = append(unexpected, l) 538 } 539 } 540 541 // no unexpected logs 542 assert.Len(t, unexpected, 0) 543 544 assert.Equal(t, []string{ 545 "component 1 initialized", 546 "component 1 ready", 547 "component 1 done", 548 }, component1) 549 550 assert.Equal(t, []string{ 551 "component 2 initialized", 552 "component 2 started", 553 "component 2 ready", 554 "component 2 done", 555 }, component2) 556 557 assert.Equal(t, []string{ 558 "component 3 initialized", 559 "component 3 started", 560 "component 3 ready", 561 "component 3 done", 562 "handled error: component 3 error", 563 "component 3 initialized", 564 "component 3 started", 565 "component 3 ready", 566 "component 3 done", 567 }, component3) 568 569 // components are stopped via context cancellation, so the specific order is random 570 doneLogs := logs[len(logs)-3:] 571 assert.ElementsMatch(t, []string{ 572 "component 1 done", 573 "component 2 done", 574 "component 3 done", 575 }, doneLogs) 576 } 577 578 func testErrorHandler(logger *testLog, expected error) component.OnError { 579 return func(err error) component.ErrorHandlingResult { 580 if errors.Is(err, expected) { 581 logger.Logf("handled error: %s", err) 582 return component.ErrorHandlingRestart 583 } 584 logger.Logf("handled unexpected error: %s", err) 585 return component.ErrorHandlingStop 586 } 587 } 588 589 // TestDependableComponentWaitForDependencies tests that dependable components are started after 590 // their dependencies are ready 591 // In this test: 592 // * Components 1 & 2 are DependableComponents 593 // * Component 3 is a normal Component 594 // * 1 depends on 3 595 // * 2 depends on 1 596 // * Start order should be 3, 1, 2 597 // run test 10 times to ensure order is consistent 598 func TestDependableComponentWaitForDependencies(t *testing.T) { 599 for i := 0; i < 10; i++ { 600 testDependableComponentWaitForDependencies(t) 601 } 602 } 603 604 func testDependableComponentWaitForDependencies(t *testing.T) { 605 ctx, cancel := context.WithCancel(context.Background()) 606 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 607 608 nb := FlowNode("scaffold test") 609 nb.componentBuilder = component.NewComponentManagerBuilder() 610 611 logger := &testLog{} 612 613 component1Dependable := module.NewProxiedReadyDoneAware() 614 component3Dependable := module.NewProxiedReadyDoneAware() 615 616 name1 := "component 1" 617 nb.DependableComponent(name1, func(node *NodeConfig) (module.ReadyDoneAware, error) { 618 logger.Logf("%s initialized", name1) 619 c := newMockComponent(logger, name1) 620 component1Dependable.Init(c) 621 return c, nil 622 }, &DependencyList{[]module.ReadyDoneAware{component3Dependable}}) 623 624 name2 := "component 2" 625 nb.DependableComponent(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) { 626 logger.Logf("%s initialized", name2) 627 return newMockComponent(logger, name2), nil 628 }, &DependencyList{[]module.ReadyDoneAware{component1Dependable}}) 629 630 name3 := "component 3" 631 nb.Component(name3, func(node *NodeConfig) (module.ReadyDoneAware, error) { 632 logger.Logf("%s initialized", name3) 633 c := newMockComponent(logger, name3) 634 c.startFn = func(ctx irrecoverable.SignalerContext, name string) { 635 // add delay to test components are run serially 636 time.Sleep(5 * time.Millisecond) 637 } 638 component3Dependable.Init(c) 639 return c, nil 640 }) 641 642 err := nb.handleComponents() 643 require.NoError(t, err) 644 645 cm := nb.componentBuilder.Build() 646 647 cm.Start(signalerCtx) 648 <-cm.Ready() 649 650 cancel() 651 <-cm.Done() 652 653 logs := logger.logs 654 655 assert.Len(t, logs, 12) 656 657 // components are initialized in a specific order, so check that the order is correct 658 startLogs := logs[:len(logs)-3] 659 assert.Equal(t, []string{ 660 "component 3 initialized", 661 "component 3 started", 662 "component 3 ready", 663 "component 1 initialized", 664 "component 1 started", 665 "component 1 ready", 666 "component 2 initialized", 667 "component 2 started", 668 "component 2 ready", 669 }, startLogs) 670 671 // components are stopped via context cancellation, so the specific order is random 672 doneLogs := logs[len(logs)-3:] 673 assert.ElementsMatch(t, []string{ 674 "component 1 done", 675 "component 2 done", 676 "component 3 done", 677 }, doneLogs) 678 } 679 680 func TestCreateUploader(t *testing.T) { 681 t.Parallel() 682 t.Run("create uploader", func(t *testing.T) { 683 t.Parallel() 684 nb := FlowNode("scaffold_uploader") 685 mockHttp := &http.Client{ 686 Transport: &mockRoundTripper{ 687 DoFunc: func(req *http.Request) (*http.Response, error) { 688 switch req.URL.Path { 689 case "/computeMetadata/v1/project/project-id": 690 return &http.Response{ 691 StatusCode: 200, 692 Body: io.NopCloser(bytes.NewBufferString("test-project-id")), 693 }, nil 694 case "/computeMetadata/v1/instance/id": 695 return &http.Response{ 696 StatusCode: 200, 697 Body: io.NopCloser(bytes.NewBufferString("test-instance-id")), 698 }, nil 699 default: 700 return nil, fmt.Errorf("unexpected request: %s", req.URL.Path) 701 } 702 }, 703 }, 704 } 705 706 testClient := gcemd.NewClient(mockHttp) 707 uploader, err := nb.createGCEProfileUploader( 708 testClient, 709 710 option.WithoutAuthentication(), 711 option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())), 712 ) 713 require.NoError(t, err) 714 require.NotNil(t, uploader) 715 716 uploaderImpl, ok := uploader.(*profiler.UploaderImpl) 717 require.True(t, ok) 718 719 assert.Equal(t, "test-project-id", uploaderImpl.Deployment.ProjectId) 720 assert.Equal(t, "unknown-scaffold_uploader", uploaderImpl.Deployment.Target) 721 assert.Equal(t, "test-instance-id", uploaderImpl.Deployment.Labels["instance"]) 722 assert.Equal(t, "undefined-undefined", uploaderImpl.Deployment.Labels["version"]) 723 }) 724 } 725 726 type mockRoundTripper struct { 727 DoFunc func(req *http.Request) (*http.Response, error) 728 } 729 730 func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 731 return m.DoFunc(req) 732 } 733 734 // TestDhtSystemActivationStatus tests that the DHT system activation status is correctly 735 // determined based on the role string. 736 // This test is not exhaustive, but should cover the most common cases. 737 func TestDhtSystemActivationStatus(t *testing.T) { 738 tests := []struct { 739 name string 740 roleStr string 741 enabled bool 742 expected p2pbuilder.DhtSystemActivation 743 expectErr bool 744 }{ 745 { 746 name: "ghost role returns disabled", 747 roleStr: "ghost", 748 enabled: true, 749 expected: p2pbuilder.DhtSystemDisabled, 750 expectErr: false, 751 }, 752 { 753 name: "access role returns enabled", 754 roleStr: "access", 755 enabled: true, 756 expected: p2pbuilder.DhtSystemEnabled, 757 expectErr: false, 758 }, 759 { 760 name: "execution role returns enabled", 761 roleStr: "execution", 762 enabled: true, 763 expected: p2pbuilder.DhtSystemEnabled, 764 expectErr: false, 765 }, 766 { 767 name: "access role with disabled returns disabled", 768 roleStr: "access", 769 enabled: false, 770 expected: p2pbuilder.DhtSystemDisabled, 771 expectErr: false, 772 }, 773 { 774 name: "execution role with disabled returns disabled", 775 roleStr: "execution", 776 enabled: false, 777 expected: p2pbuilder.DhtSystemDisabled, 778 expectErr: false, 779 }, 780 { 781 name: "collection role returns disabled", 782 roleStr: "collection", 783 enabled: true, 784 expected: p2pbuilder.DhtSystemDisabled, 785 expectErr: false, 786 }, 787 { 788 name: "consensus role returns disabled", 789 roleStr: "consensus", 790 enabled: true, 791 expected: p2pbuilder.DhtSystemDisabled, 792 expectErr: false, 793 }, 794 { 795 name: "verification nodes return disabled", 796 roleStr: "verification", 797 enabled: true, 798 expected: p2pbuilder.DhtSystemDisabled, 799 expectErr: false, 800 }, 801 { 802 name: "invalid role returns error", 803 roleStr: "invalidRole", 804 enabled: true, 805 expected: p2pbuilder.DhtSystemDisabled, 806 expectErr: true, 807 }, // Add more test cases for other roles, if needed. 808 } 809 810 for _, tt := range tests { 811 t.Run(tt.name, func(t *testing.T) { 812 result, err := DhtSystemActivationStatus(tt.roleStr, tt.enabled) 813 require.Equal(t, tt.expectErr, err != nil, "unexpected error status") 814 require.Equal(t, tt.expected, result, "unexpected activation status") 815 }) 816 } 817 }