Best Keploy code snippet using graph.TestCase
compile_test.go
Source:compile_test.go
...5 "github.com/hashicorp/consul/agent/connect"6 "github.com/hashicorp/consul/agent/structs"7 "github.com/stretchr/testify/require"8)9type compileTestCase struct {10 entries *structs.DiscoveryChainConfigEntries11 setup func(req *CompileRequest)12 expect *structs.CompiledDiscoveryChain13 // expectIsDefault tests behavior of CompiledDiscoveryChain.IsDefault()14 expectIsDefault bool15 expectCustom bool16 expectErr string17 expectGraphErr bool18}19func TestCompile(t *testing.T) {20 t.Parallel()21 cases := map[string]compileTestCase{22 "router with defaults": testcase_JustRouterWithDefaults(),23 "router with defaults and resolver": testcase_RouterWithDefaults_NoSplit_WithResolver(),24 "router with defaults and noop split": testcase_RouterWithDefaults_WithNoopSplit_DefaultResolver(),25 "router with defaults and noop split and resolver": testcase_RouterWithDefaults_WithNoopSplit_WithResolver(),26 "route bypasses splitter": testcase_RouteBypassesSplit(),27 "noop split": testcase_NoopSplit_DefaultResolver(),28 "noop split with protocol from proxy defaults": testcase_NoopSplit_DefaultResolver_ProtocolFromProxyDefaults(),29 "noop split with resolver": testcase_NoopSplit_WithResolver(),30 "subset split": testcase_SubsetSplit(),31 "service split": testcase_ServiceSplit(),32 "split bypasses next splitter": testcase_SplitBypassesSplit(),33 "service redirect": testcase_ServiceRedirect(),34 "service and subset redirect": testcase_ServiceAndSubsetRedirect(),35 "datacenter redirect": testcase_DatacenterRedirect(),36 "datacenter redirect with mesh gateways": testcase_DatacenterRedirect_WithMeshGateways(),37 "service failover": testcase_ServiceFailover(),38 "service failover through redirect": testcase_ServiceFailoverThroughRedirect(),39 "circular resolver failover": testcase_Resolver_CircularFailover(),40 "service and subset failover": testcase_ServiceAndSubsetFailover(),41 "datacenter failover": testcase_DatacenterFailover(),42 "datacenter failover with mesh gateways": testcase_DatacenterFailover_WithMeshGateways(),43 "noop split to resolver with default subset": testcase_NoopSplit_WithDefaultSubset(),44 "resolver with default subset": testcase_Resolve_WithDefaultSubset(),45 "default resolver with external sni": testcase_DefaultResolver_ExternalSNI(),46 "resolver with no entries and inferring defaults": testcase_DefaultResolver(),47 "default resolver with proxy defaults": testcase_DefaultResolver_WithProxyDefaults(),48 "service redirect to service with default resolver is not a default chain": testcase_RedirectToDefaultResolverIsNotDefaultChain(),49 "all the bells and whistles": testcase_AllBellsAndWhistles(),50 "multi dc canary": testcase_MultiDatacenterCanary(),51 // various errors52 "splitter requires valid protocol": testcase_SplitterRequiresValidProtocol(),53 "router requires valid protocol": testcase_RouterRequiresValidProtocol(),54 "split to unsplittable protocol": testcase_SplitToUnsplittableProtocol(),55 "route to unroutable protocol": testcase_RouteToUnroutableProtocol(),56 "failover crosses protocols": testcase_FailoverCrossesProtocols(),57 "redirect crosses protocols": testcase_RedirectCrossesProtocols(),58 "redirect to missing subset": testcase_RedirectToMissingSubset(),59 "resolver with failover and external sni": testcase_Resolver_ExternalSNI_FailoverNotAllowed(),60 "resolver with subsets and external sni": testcase_Resolver_ExternalSNI_SubsetsNotAllowed(),61 "resolver with redirect and external sni": testcase_Resolver_ExternalSNI_RedirectNotAllowed(),62 // overrides63 "resolver with protocol from override": testcase_ResolverProtocolOverride(),64 "resolver with protocol from override ignored": testcase_ResolverProtocolOverrideIgnored(),65 "router ignored due to protocol override": testcase_RouterIgnored_ResolverProtocolOverride(),66 // circular references67 "circular resolver redirect": testcase_Resolver_CircularRedirect(),68 "circular split": testcase_CircularSplit(),69 }70 for name, tc := range cases {71 tc := tc72 t.Run(name, func(t *testing.T) {73 t.Parallel()74 // sanity check entries are normalized and valid75 for _, entry := range tc.entries.Routers {76 require.NoError(t, entry.Normalize())77 require.NoError(t, entry.Validate())78 }79 for _, entry := range tc.entries.Splitters {80 require.NoError(t, entry.Normalize())81 require.NoError(t, entry.Validate())82 }83 for _, entry := range tc.entries.Resolvers {84 require.NoError(t, entry.Normalize())85 require.NoError(t, entry.Validate())86 }87 req := CompileRequest{88 ServiceName: "main",89 EvaluateInNamespace: "default",90 EvaluateInDatacenter: "dc1",91 EvaluateInTrustDomain: "trustdomain.consul",92 UseInDatacenter: "dc1",93 Entries: tc.entries,94 }95 if tc.setup != nil {96 tc.setup(&req)97 }98 res, err := Compile(req)99 if tc.expectErr != "" {100 require.Error(t, err)101 require.Contains(t, err.Error(), tc.expectErr)102 _, ok := err.(*structs.ConfigEntryGraphError)103 if tc.expectGraphErr {104 require.True(t, ok, "%T is not a *ConfigEntryGraphError", err)105 } else {106 require.False(t, ok, "did not expect a *ConfigEntryGraphError here: %v", err)107 }108 } else {109 require.NoError(t, err)110 // Avoid requiring unnecessary test boilerplate and inject these111 // ourselves.112 tc.expect.ServiceName = "main"113 tc.expect.Namespace = "default"114 tc.expect.Datacenter = "dc1"115 if tc.expectCustom {116 require.NotEmpty(t, res.CustomizationHash)117 res.CustomizationHash = ""118 } else {119 require.Empty(t, res.CustomizationHash)120 }121 require.Equal(t, tc.expect, res)122 require.Equal(t, tc.expectIsDefault, res.IsDefault())123 }124 })125 }126}127func testcase_JustRouterWithDefaults() compileTestCase {128 entries := newEntries()129 setServiceProtocol(entries, "main", "http")130 entries.AddRouters(131 &structs.ServiceRouterConfigEntry{132 Kind: "service-router",133 Name: "main",134 },135 )136 expect := &structs.CompiledDiscoveryChain{137 Protocol: "http",138 StartNode: "router:main",139 Nodes: map[string]*structs.DiscoveryGraphNode{140 "router:main": &structs.DiscoveryGraphNode{141 Type: structs.DiscoveryGraphNodeTypeRouter,142 Name: "main",143 Routes: []*structs.DiscoveryRoute{144 {145 Definition: newDefaultServiceRoute("main"),146 NextNode: "resolver:main.default.dc1",147 },148 },149 },150 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{151 Type: structs.DiscoveryGraphNodeTypeResolver,152 Name: "main.default.dc1",153 Resolver: &structs.DiscoveryResolver{154 Default: true,155 ConnectTimeout: 5 * time.Second,156 Target: "main.default.dc1",157 },158 },159 },160 Targets: map[string]*structs.DiscoveryTarget{161 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),162 },163 }164 return compileTestCase{entries: entries, expect: expect}165}166func testcase_RouterWithDefaults_NoSplit_WithResolver() compileTestCase {167 entries := newEntries()168 setServiceProtocol(entries, "main", "http")169 entries.AddRouters(170 &structs.ServiceRouterConfigEntry{171 Kind: "service-router",172 Name: "main",173 },174 )175 entries.AddResolvers(176 &structs.ServiceResolverConfigEntry{177 Kind: "service-resolver",178 Name: "main",179 ConnectTimeout: 33 * time.Second,180 },181 )182 expect := &structs.CompiledDiscoveryChain{183 Protocol: "http",184 StartNode: "router:main",185 Nodes: map[string]*structs.DiscoveryGraphNode{186 "router:main": &structs.DiscoveryGraphNode{187 Type: structs.DiscoveryGraphNodeTypeRouter,188 Name: "main",189 Routes: []*structs.DiscoveryRoute{190 {191 Definition: newDefaultServiceRoute("main"),192 NextNode: "resolver:main.default.dc1",193 },194 },195 },196 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{197 Type: structs.DiscoveryGraphNodeTypeResolver,198 Name: "main.default.dc1",199 Resolver: &structs.DiscoveryResolver{200 ConnectTimeout: 33 * time.Second,201 Target: "main.default.dc1",202 },203 },204 },205 Targets: map[string]*structs.DiscoveryTarget{206 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),207 },208 }209 return compileTestCase{entries: entries, expect: expect}210}211func testcase_RouterWithDefaults_WithNoopSplit_DefaultResolver() compileTestCase {212 entries := newEntries()213 setServiceProtocol(entries, "main", "http")214 entries.AddRouters(215 &structs.ServiceRouterConfigEntry{216 Kind: "service-router",217 Name: "main",218 },219 )220 entries.AddSplitters(221 &structs.ServiceSplitterConfigEntry{222 Kind: "service-splitter",223 Name: "main",224 Splits: []structs.ServiceSplit{225 {Weight: 100},226 },227 },228 )229 expect := &structs.CompiledDiscoveryChain{230 Protocol: "http",231 StartNode: "router:main",232 Nodes: map[string]*structs.DiscoveryGraphNode{233 "router:main": &structs.DiscoveryGraphNode{234 Type: structs.DiscoveryGraphNodeTypeRouter,235 Name: "main",236 Routes: []*structs.DiscoveryRoute{237 {238 Definition: newDefaultServiceRoute("main"),239 NextNode: "splitter:main",240 },241 },242 },243 "splitter:main": &structs.DiscoveryGraphNode{244 Type: structs.DiscoveryGraphNodeTypeSplitter,245 Name: "main",246 Splits: []*structs.DiscoverySplit{247 {248 Weight: 100,249 NextNode: "resolver:main.default.dc1",250 },251 },252 },253 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{254 Type: structs.DiscoveryGraphNodeTypeResolver,255 Name: "main.default.dc1",256 Resolver: &structs.DiscoveryResolver{257 Default: true,258 ConnectTimeout: 5 * time.Second,259 Target: "main.default.dc1",260 },261 },262 },263 Targets: map[string]*structs.DiscoveryTarget{264 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),265 },266 }267 return compileTestCase{entries: entries, expect: expect}268}269func testcase_NoopSplit_DefaultResolver_ProtocolFromProxyDefaults() compileTestCase {270 entries := newEntries()271 setGlobalProxyProtocol(entries, "http")272 entries.AddRouters(273 &structs.ServiceRouterConfigEntry{274 Kind: "service-router",275 Name: "main",276 },277 )278 entries.AddSplitters(279 &structs.ServiceSplitterConfigEntry{280 Kind: "service-splitter",281 Name: "main",282 Splits: []structs.ServiceSplit{283 {Weight: 100},284 },285 },286 )287 expect := &structs.CompiledDiscoveryChain{288 Protocol: "http",289 StartNode: "router:main",290 Nodes: map[string]*structs.DiscoveryGraphNode{291 "router:main": &structs.DiscoveryGraphNode{292 Type: structs.DiscoveryGraphNodeTypeRouter,293 Name: "main",294 Routes: []*structs.DiscoveryRoute{295 {296 Definition: newDefaultServiceRoute("main"),297 NextNode: "splitter:main",298 },299 },300 },301 "splitter:main": &structs.DiscoveryGraphNode{302 Type: structs.DiscoveryGraphNodeTypeSplitter,303 Name: "main",304 Splits: []*structs.DiscoverySplit{305 {306 Weight: 100,307 NextNode: "resolver:main.default.dc1",308 },309 },310 },311 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{312 Type: structs.DiscoveryGraphNodeTypeResolver,313 Name: "main.default.dc1",314 Resolver: &structs.DiscoveryResolver{315 Default: true,316 ConnectTimeout: 5 * time.Second,317 Target: "main.default.dc1",318 },319 },320 },321 Targets: map[string]*structs.DiscoveryTarget{322 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),323 },324 }325 return compileTestCase{entries: entries, expect: expect}326}327func testcase_RouterWithDefaults_WithNoopSplit_WithResolver() compileTestCase {328 entries := newEntries()329 setServiceProtocol(entries, "main", "http")330 entries.AddRouters(331 &structs.ServiceRouterConfigEntry{332 Kind: "service-router",333 Name: "main",334 },335 )336 entries.AddSplitters(337 &structs.ServiceSplitterConfigEntry{338 Kind: "service-splitter",339 Name: "main",340 Splits: []structs.ServiceSplit{341 {Weight: 100},342 },343 },344 )345 entries.AddResolvers(346 &structs.ServiceResolverConfigEntry{347 Kind: "service-resolver",348 Name: "main",349 ConnectTimeout: 33 * time.Second,350 },351 )352 expect := &structs.CompiledDiscoveryChain{353 Protocol: "http",354 StartNode: "router:main",355 Nodes: map[string]*structs.DiscoveryGraphNode{356 "router:main": &structs.DiscoveryGraphNode{357 Type: structs.DiscoveryGraphNodeTypeRouter,358 Name: "main",359 Routes: []*structs.DiscoveryRoute{360 {361 Definition: newDefaultServiceRoute("main"),362 NextNode: "splitter:main",363 },364 },365 },366 "splitter:main": &structs.DiscoveryGraphNode{367 Type: structs.DiscoveryGraphNodeTypeSplitter,368 Name: "main",369 Splits: []*structs.DiscoverySplit{370 {371 Weight: 100,372 NextNode: "resolver:main.default.dc1",373 },374 },375 },376 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{377 Type: structs.DiscoveryGraphNodeTypeResolver,378 Name: "main.default.dc1",379 Resolver: &structs.DiscoveryResolver{380 ConnectTimeout: 33 * time.Second,381 Target: "main.default.dc1",382 },383 },384 },385 Targets: map[string]*structs.DiscoveryTarget{386 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),387 },388 }389 return compileTestCase{entries: entries, expect: expect}390}391func testcase_RouteBypassesSplit() compileTestCase {392 entries := newEntries()393 setServiceProtocol(entries, "main", "http")394 setServiceProtocol(entries, "other", "http")395 entries.AddRouters(396 &structs.ServiceRouterConfigEntry{397 Kind: "service-router",398 Name: "main",399 Routes: []structs.ServiceRoute{400 // route direct subset reference (bypass split)401 newSimpleRoute("other", func(r *structs.ServiceRoute) {402 r.Destination.ServiceSubset = "bypass"403 }),404 },405 },406 )407 entries.AddSplitters(408 &structs.ServiceSplitterConfigEntry{409 Kind: "service-splitter",410 Name: "other",411 Splits: []structs.ServiceSplit{412 {Weight: 100, Service: "ignored"},413 },414 },415 )416 entries.AddResolvers(417 &structs.ServiceResolverConfigEntry{418 Kind: "service-resolver",419 Name: "other",420 Subsets: map[string]structs.ServiceResolverSubset{421 "bypass": {422 Filter: "Service.Meta.version == bypass",423 },424 },425 },426 )427 router := entries.GetRouter("main")428 expect := &structs.CompiledDiscoveryChain{429 Protocol: "http",430 StartNode: "router:main",431 Nodes: map[string]*structs.DiscoveryGraphNode{432 "router:main": &structs.DiscoveryGraphNode{433 Type: structs.DiscoveryGraphNodeTypeRouter,434 Name: "main",435 Routes: []*structs.DiscoveryRoute{436 {437 Definition: &router.Routes[0],438 NextNode: "resolver:bypass.other.default.dc1",439 },440 {441 Definition: newDefaultServiceRoute("main"),442 NextNode: "resolver:main.default.dc1",443 },444 },445 },446 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{447 Type: structs.DiscoveryGraphNodeTypeResolver,448 Name: "main.default.dc1",449 Resolver: &structs.DiscoveryResolver{450 Default: true,451 ConnectTimeout: 5 * time.Second,452 Target: "main.default.dc1",453 },454 },455 "resolver:bypass.other.default.dc1": &structs.DiscoveryGraphNode{456 Type: structs.DiscoveryGraphNodeTypeResolver,457 Name: "bypass.other.default.dc1",458 Resolver: &structs.DiscoveryResolver{459 ConnectTimeout: 5 * time.Second,460 Target: "bypass.other.default.dc1",461 },462 },463 },464 Targets: map[string]*structs.DiscoveryTarget{465 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),466 "bypass.other.default.dc1": newTarget("other", "bypass", "default", "dc1", func(t *structs.DiscoveryTarget) {467 t.Subset = structs.ServiceResolverSubset{468 Filter: "Service.Meta.version == bypass",469 }470 }),471 },472 }473 return compileTestCase{entries: entries, expect: expect}474}475func testcase_NoopSplit_DefaultResolver() compileTestCase {476 entries := newEntries()477 setServiceProtocol(entries, "main", "http")478 entries.AddSplitters(479 &structs.ServiceSplitterConfigEntry{480 Kind: "service-splitter",481 Name: "main",482 Splits: []structs.ServiceSplit{483 {Weight: 100},484 },485 },486 )487 expect := &structs.CompiledDiscoveryChain{488 Protocol: "http",489 StartNode: "splitter:main",490 Nodes: map[string]*structs.DiscoveryGraphNode{491 "splitter:main": &structs.DiscoveryGraphNode{492 Type: structs.DiscoveryGraphNodeTypeSplitter,493 Name: "main",494 Splits: []*structs.DiscoverySplit{495 {496 Weight: 100,497 NextNode: "resolver:main.default.dc1",498 },499 },500 },501 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{502 Type: structs.DiscoveryGraphNodeTypeResolver,503 Name: "main.default.dc1",504 Resolver: &structs.DiscoveryResolver{505 Default: true,506 ConnectTimeout: 5 * time.Second,507 Target: "main.default.dc1",508 },509 },510 },511 Targets: map[string]*structs.DiscoveryTarget{512 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),513 },514 }515 return compileTestCase{entries: entries, expect: expect}516}517func testcase_NoopSplit_WithResolver() compileTestCase {518 entries := newEntries()519 setServiceProtocol(entries, "main", "http")520 entries.AddSplitters(521 &structs.ServiceSplitterConfigEntry{522 Kind: "service-splitter",523 Name: "main",524 Splits: []structs.ServiceSplit{525 {Weight: 100},526 },527 },528 )529 entries.AddResolvers(530 &structs.ServiceResolverConfigEntry{531 Kind: "service-resolver",532 Name: "main",533 ConnectTimeout: 33 * time.Second,534 },535 )536 expect := &structs.CompiledDiscoveryChain{537 Protocol: "http",538 StartNode: "splitter:main",539 Nodes: map[string]*structs.DiscoveryGraphNode{540 "splitter:main": &structs.DiscoveryGraphNode{541 Type: structs.DiscoveryGraphNodeTypeSplitter,542 Name: "main",543 Splits: []*structs.DiscoverySplit{544 {545 Weight: 100,546 NextNode: "resolver:main.default.dc1",547 },548 },549 },550 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{551 Type: structs.DiscoveryGraphNodeTypeResolver,552 Name: "main.default.dc1",553 Resolver: &structs.DiscoveryResolver{554 ConnectTimeout: 33 * time.Second,555 Target: "main.default.dc1",556 },557 },558 },559 Targets: map[string]*structs.DiscoveryTarget{560 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),561 },562 }563 return compileTestCase{entries: entries, expect: expect}564}565func testcase_SubsetSplit() compileTestCase {566 entries := newEntries()567 setServiceProtocol(entries, "main", "http")568 entries.AddSplitters(569 &structs.ServiceSplitterConfigEntry{570 Kind: "service-splitter",571 Name: "main",572 Splits: []structs.ServiceSplit{573 {Weight: 60, ServiceSubset: "v2"},574 {Weight: 40, ServiceSubset: "v1"},575 },576 },577 )578 entries.AddResolvers(579 &structs.ServiceResolverConfigEntry{580 Kind: "service-resolver",581 Name: "main",582 Subsets: map[string]structs.ServiceResolverSubset{583 "v1": {584 Filter: "Service.Meta.version == 1",585 },586 "v2": {587 Filter: "Service.Meta.version == 2",588 },589 },590 },591 )592 expect := &structs.CompiledDiscoveryChain{593 Protocol: "http",594 StartNode: "splitter:main",595 Nodes: map[string]*structs.DiscoveryGraphNode{596 "splitter:main": &structs.DiscoveryGraphNode{597 Type: structs.DiscoveryGraphNodeTypeSplitter,598 Name: "main",599 Splits: []*structs.DiscoverySplit{600 {601 Weight: 60,602 NextNode: "resolver:v2.main.default.dc1",603 },604 {605 Weight: 40,606 NextNode: "resolver:v1.main.default.dc1",607 },608 },609 },610 "resolver:v2.main.default.dc1": &structs.DiscoveryGraphNode{611 Type: structs.DiscoveryGraphNodeTypeResolver,612 Name: "v2.main.default.dc1",613 Resolver: &structs.DiscoveryResolver{614 ConnectTimeout: 5 * time.Second,615 Target: "v2.main.default.dc1",616 },617 },618 "resolver:v1.main.default.dc1": &structs.DiscoveryGraphNode{619 Type: structs.DiscoveryGraphNodeTypeResolver,620 Name: "v1.main.default.dc1",621 Resolver: &structs.DiscoveryResolver{622 ConnectTimeout: 5 * time.Second,623 Target: "v1.main.default.dc1",624 },625 },626 },627 Targets: map[string]*structs.DiscoveryTarget{628 "v2.main.default.dc1": newTarget("main", "v2", "default", "dc1", func(t *structs.DiscoveryTarget) {629 t.Subset = structs.ServiceResolverSubset{630 Filter: "Service.Meta.version == 2",631 }632 }),633 "v1.main.default.dc1": newTarget("main", "v1", "default", "dc1", func(t *structs.DiscoveryTarget) {634 t.Subset = structs.ServiceResolverSubset{635 Filter: "Service.Meta.version == 1",636 }637 }),638 },639 }640 return compileTestCase{entries: entries, expect: expect}641}642func testcase_ServiceSplit() compileTestCase {643 entries := newEntries()644 setServiceProtocol(entries, "main", "http")645 setServiceProtocol(entries, "foo", "http")646 setServiceProtocol(entries, "bar", "http")647 entries.AddSplitters(648 &structs.ServiceSplitterConfigEntry{649 Kind: "service-splitter",650 Name: "main",651 Splits: []structs.ServiceSplit{652 {Weight: 60, Service: "foo"},653 {Weight: 40, Service: "bar"},654 },655 },656 )657 expect := &structs.CompiledDiscoveryChain{658 Protocol: "http",659 StartNode: "splitter:main",660 Nodes: map[string]*structs.DiscoveryGraphNode{661 "splitter:main": &structs.DiscoveryGraphNode{662 Type: structs.DiscoveryGraphNodeTypeSplitter,663 Name: "main",664 Splits: []*structs.DiscoverySplit{665 {666 Weight: 60,667 NextNode: "resolver:foo.default.dc1",668 },669 {670 Weight: 40,671 NextNode: "resolver:bar.default.dc1",672 },673 },674 },675 "resolver:foo.default.dc1": &structs.DiscoveryGraphNode{676 Type: structs.DiscoveryGraphNodeTypeResolver,677 Name: "foo.default.dc1",678 Resolver: &structs.DiscoveryResolver{679 Default: true,680 ConnectTimeout: 5 * time.Second,681 Target: "foo.default.dc1",682 },683 },684 "resolver:bar.default.dc1": &structs.DiscoveryGraphNode{685 Type: structs.DiscoveryGraphNodeTypeResolver,686 Name: "bar.default.dc1",687 Resolver: &structs.DiscoveryResolver{688 Default: true,689 ConnectTimeout: 5 * time.Second,690 Target: "bar.default.dc1",691 },692 },693 },694 Targets: map[string]*structs.DiscoveryTarget{695 "foo.default.dc1": newTarget("foo", "", "default", "dc1", nil),696 "bar.default.dc1": newTarget("bar", "", "default", "dc1", nil),697 },698 }699 return compileTestCase{entries: entries, expect: expect}700}701func testcase_SplitBypassesSplit() compileTestCase {702 entries := newEntries()703 setServiceProtocol(entries, "main", "http")704 setServiceProtocol(entries, "next", "http")705 entries.AddSplitters(706 &structs.ServiceSplitterConfigEntry{707 Kind: "service-splitter",708 Name: "main",709 Splits: []structs.ServiceSplit{710 {711 Weight: 100,712 Service: "next",713 ServiceSubset: "bypassed",714 },715 },716 },717 &structs.ServiceSplitterConfigEntry{718 Kind: "service-splitter",719 Name: "next",720 Splits: []structs.ServiceSplit{721 {722 Weight: 100,723 ServiceSubset: "not-bypassed",724 },725 },726 },727 )728 entries.AddResolvers(729 &structs.ServiceResolverConfigEntry{730 Kind: "service-resolver",731 Name: "next",732 Subsets: map[string]structs.ServiceResolverSubset{733 "bypassed": {734 Filter: "Service.Meta.version == bypass",735 },736 "not-bypassed": {737 Filter: "Service.Meta.version != bypass",738 },739 },740 },741 )742 expect := &structs.CompiledDiscoveryChain{743 Protocol: "http",744 StartNode: "splitter:main",745 Nodes: map[string]*structs.DiscoveryGraphNode{746 "splitter:main": &structs.DiscoveryGraphNode{747 Type: structs.DiscoveryGraphNodeTypeSplitter,748 Name: "main",749 Splits: []*structs.DiscoverySplit{750 {751 Weight: 100,752 NextNode: "resolver:bypassed.next.default.dc1",753 },754 },755 },756 "resolver:bypassed.next.default.dc1": &structs.DiscoveryGraphNode{757 Type: structs.DiscoveryGraphNodeTypeResolver,758 Name: "bypassed.next.default.dc1",759 Resolver: &structs.DiscoveryResolver{760 ConnectTimeout: 5 * time.Second,761 Target: "bypassed.next.default.dc1",762 },763 },764 },765 Targets: map[string]*structs.DiscoveryTarget{766 "bypassed.next.default.dc1": newTarget("next", "bypassed", "default", "dc1", func(t *structs.DiscoveryTarget) {767 t.Subset = structs.ServiceResolverSubset{768 Filter: "Service.Meta.version == bypass",769 }770 }),771 },772 }773 return compileTestCase{entries: entries, expect: expect}774}775func testcase_ServiceRedirect() compileTestCase {776 entries := newEntries()777 entries.AddResolvers(778 &structs.ServiceResolverConfigEntry{779 Kind: "service-resolver",780 Name: "main",781 Redirect: &structs.ServiceResolverRedirect{782 Service: "other",783 },784 },785 )786 expect := &structs.CompiledDiscoveryChain{787 Protocol: "tcp",788 StartNode: "resolver:other.default.dc1",789 Nodes: map[string]*structs.DiscoveryGraphNode{790 "resolver:other.default.dc1": &structs.DiscoveryGraphNode{791 Type: structs.DiscoveryGraphNodeTypeResolver,792 Name: "other.default.dc1",793 Resolver: &structs.DiscoveryResolver{794 Default: true,795 ConnectTimeout: 5 * time.Second,796 Target: "other.default.dc1",797 },798 },799 },800 Targets: map[string]*structs.DiscoveryTarget{801 "other.default.dc1": newTarget("other", "", "default", "dc1", nil),802 },803 }804 return compileTestCase{entries: entries, expect: expect}805}806func testcase_ServiceAndSubsetRedirect() compileTestCase {807 entries := newEntries()808 entries.AddResolvers(809 &structs.ServiceResolverConfigEntry{810 Kind: "service-resolver",811 Name: "main",812 Redirect: &structs.ServiceResolverRedirect{813 Service: "other",814 ServiceSubset: "v2",815 },816 },817 &structs.ServiceResolverConfigEntry{818 Kind: "service-resolver",819 Name: "other",820 Subsets: map[string]structs.ServiceResolverSubset{821 "v1": {822 Filter: "Service.Meta.version == 1",823 },824 "v2": {825 Filter: "Service.Meta.version == 2",826 },827 },828 },829 )830 expect := &structs.CompiledDiscoveryChain{831 Protocol: "tcp",832 StartNode: "resolver:v2.other.default.dc1",833 Nodes: map[string]*structs.DiscoveryGraphNode{834 "resolver:v2.other.default.dc1": &structs.DiscoveryGraphNode{835 Type: structs.DiscoveryGraphNodeTypeResolver,836 Name: "v2.other.default.dc1",837 Resolver: &structs.DiscoveryResolver{838 ConnectTimeout: 5 * time.Second,839 Target: "v2.other.default.dc1",840 },841 },842 },843 Targets: map[string]*structs.DiscoveryTarget{844 "v2.other.default.dc1": newTarget("other", "v2", "default", "dc1", func(t *structs.DiscoveryTarget) {845 t.Subset = structs.ServiceResolverSubset{846 Filter: "Service.Meta.version == 2",847 }848 }),849 },850 }851 return compileTestCase{entries: entries, expect: expect}852}853func testcase_DatacenterRedirect() compileTestCase {854 entries := newEntries()855 entries.AddResolvers(856 &structs.ServiceResolverConfigEntry{857 Kind: "service-resolver",858 Name: "main",859 Redirect: &structs.ServiceResolverRedirect{860 Datacenter: "dc9",861 },862 },863 )864 expect := &structs.CompiledDiscoveryChain{865 Protocol: "tcp",866 StartNode: "resolver:main.default.dc9",867 Nodes: map[string]*structs.DiscoveryGraphNode{868 "resolver:main.default.dc9": &structs.DiscoveryGraphNode{869 Type: structs.DiscoveryGraphNodeTypeResolver,870 Name: "main.default.dc9",871 Resolver: &structs.DiscoveryResolver{872 ConnectTimeout: 5 * time.Second,873 Target: "main.default.dc9",874 },875 },876 },877 Targets: map[string]*structs.DiscoveryTarget{878 "main.default.dc9": newTarget("main", "", "default", "dc9", nil),879 },880 }881 return compileTestCase{entries: entries, expect: expect}882}883func testcase_DatacenterRedirect_WithMeshGateways() compileTestCase {884 entries := newEntries()885 entries.GlobalProxy = &structs.ProxyConfigEntry{886 Kind: structs.ProxyDefaults,887 Name: structs.ProxyConfigGlobal,888 MeshGateway: structs.MeshGatewayConfig{889 Mode: structs.MeshGatewayModeRemote,890 },891 }892 entries.AddResolvers(893 &structs.ServiceResolverConfigEntry{894 Kind: "service-resolver",895 Name: "main",896 Redirect: &structs.ServiceResolverRedirect{897 Datacenter: "dc9",898 },899 },900 )901 expect := &structs.CompiledDiscoveryChain{902 Protocol: "tcp",903 StartNode: "resolver:main.default.dc9",904 Nodes: map[string]*structs.DiscoveryGraphNode{905 "resolver:main.default.dc9": &structs.DiscoveryGraphNode{906 Type: structs.DiscoveryGraphNodeTypeResolver,907 Name: "main.default.dc9",908 Resolver: &structs.DiscoveryResolver{909 ConnectTimeout: 5 * time.Second,910 Target: "main.default.dc9",911 },912 },913 },914 Targets: map[string]*structs.DiscoveryTarget{915 "main.default.dc9": newTarget("main", "", "default", "dc9", func(t *structs.DiscoveryTarget) {916 t.MeshGateway = structs.MeshGatewayConfig{917 Mode: structs.MeshGatewayModeRemote,918 }919 }),920 },921 }922 return compileTestCase{entries: entries, expect: expect}923}924func testcase_ServiceFailover() compileTestCase {925 entries := newEntries()926 entries.AddResolvers(927 &structs.ServiceResolverConfigEntry{928 Kind: "service-resolver",929 Name: "main",930 Failover: map[string]structs.ServiceResolverFailover{931 "*": {Service: "backup"},932 },933 },934 )935 expect := &structs.CompiledDiscoveryChain{936 Protocol: "tcp",937 StartNode: "resolver:main.default.dc1",938 Nodes: map[string]*structs.DiscoveryGraphNode{939 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{940 Type: structs.DiscoveryGraphNodeTypeResolver,941 Name: "main.default.dc1",942 Resolver: &structs.DiscoveryResolver{943 ConnectTimeout: 5 * time.Second,944 Target: "main.default.dc1",945 Failover: &structs.DiscoveryFailover{946 Targets: []string{"backup.default.dc1"},947 },948 },949 },950 },951 Targets: map[string]*structs.DiscoveryTarget{952 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),953 "backup.default.dc1": newTarget("backup", "", "default", "dc1", nil),954 },955 }956 return compileTestCase{entries: entries, expect: expect}957}958func testcase_ServiceFailoverThroughRedirect() compileTestCase {959 entries := newEntries()960 entries.AddResolvers(961 &structs.ServiceResolverConfigEntry{962 Kind: "service-resolver",963 Name: "backup",964 Redirect: &structs.ServiceResolverRedirect{965 Service: "actual",966 },967 },968 &structs.ServiceResolverConfigEntry{969 Kind: "service-resolver",970 Name: "main",971 Failover: map[string]structs.ServiceResolverFailover{972 "*": {Service: "backup"},973 },974 },975 )976 expect := &structs.CompiledDiscoveryChain{977 Protocol: "tcp",978 StartNode: "resolver:main.default.dc1",979 Nodes: map[string]*structs.DiscoveryGraphNode{980 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{981 Type: structs.DiscoveryGraphNodeTypeResolver,982 Name: "main.default.dc1",983 Resolver: &structs.DiscoveryResolver{984 ConnectTimeout: 5 * time.Second,985 Target: "main.default.dc1",986 Failover: &structs.DiscoveryFailover{987 Targets: []string{"actual.default.dc1"},988 },989 },990 },991 },992 Targets: map[string]*structs.DiscoveryTarget{993 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),994 "actual.default.dc1": newTarget("actual", "", "default", "dc1", nil),995 },996 }997 return compileTestCase{entries: entries, expect: expect}998}999func testcase_Resolver_CircularFailover() compileTestCase {1000 entries := newEntries()1001 entries.AddResolvers(1002 &structs.ServiceResolverConfigEntry{1003 Kind: "service-resolver",1004 Name: "backup",1005 Failover: map[string]structs.ServiceResolverFailover{1006 "*": {Service: "main"},1007 },1008 },1009 &structs.ServiceResolverConfigEntry{1010 Kind: "service-resolver",1011 Name: "main",1012 Failover: map[string]structs.ServiceResolverFailover{1013 "*": {Service: "backup"},1014 },1015 },1016 )1017 expect := &structs.CompiledDiscoveryChain{1018 Protocol: "tcp",1019 StartNode: "resolver:main.default.dc1",1020 Nodes: map[string]*structs.DiscoveryGraphNode{1021 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1022 Type: structs.DiscoveryGraphNodeTypeResolver,1023 Name: "main.default.dc1",1024 Resolver: &structs.DiscoveryResolver{1025 ConnectTimeout: 5 * time.Second,1026 Target: "main.default.dc1",1027 Failover: &structs.DiscoveryFailover{1028 Targets: []string{"backup.default.dc1"},1029 },1030 },1031 },1032 },1033 Targets: map[string]*structs.DiscoveryTarget{1034 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1035 "backup.default.dc1": newTarget("backup", "", "default", "dc1", nil),1036 },1037 }1038 return compileTestCase{entries: entries, expect: expect}1039}1040func testcase_ServiceAndSubsetFailover() compileTestCase {1041 entries := newEntries()1042 entries.AddResolvers(1043 &structs.ServiceResolverConfigEntry{1044 Kind: "service-resolver",1045 Name: "main",1046 Subsets: map[string]structs.ServiceResolverSubset{1047 "backup": {1048 Filter: "Service.Meta.version == backup",1049 },1050 },1051 Failover: map[string]structs.ServiceResolverFailover{1052 "*": {ServiceSubset: "backup"},1053 },1054 },1055 )1056 expect := &structs.CompiledDiscoveryChain{1057 Protocol: "tcp",1058 StartNode: "resolver:main.default.dc1",1059 Nodes: map[string]*structs.DiscoveryGraphNode{1060 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1061 Type: structs.DiscoveryGraphNodeTypeResolver,1062 Name: "main.default.dc1",1063 Resolver: &structs.DiscoveryResolver{1064 ConnectTimeout: 5 * time.Second,1065 Target: "main.default.dc1",1066 Failover: &structs.DiscoveryFailover{1067 Targets: []string{"backup.main.default.dc1"},1068 },1069 },1070 },1071 },1072 Targets: map[string]*structs.DiscoveryTarget{1073 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1074 "backup.main.default.dc1": newTarget("main", "backup", "default", "dc1", func(t *structs.DiscoveryTarget) {1075 t.Subset = structs.ServiceResolverSubset{1076 Filter: "Service.Meta.version == backup",1077 }1078 }),1079 },1080 }1081 return compileTestCase{entries: entries, expect: expect}1082}1083func testcase_DatacenterFailover() compileTestCase {1084 entries := newEntries()1085 entries.AddResolvers(1086 &structs.ServiceResolverConfigEntry{1087 Kind: "service-resolver",1088 Name: "main",1089 Failover: map[string]structs.ServiceResolverFailover{1090 "*": {Datacenters: []string{"dc2", "dc4"}},1091 },1092 },1093 )1094 expect := &structs.CompiledDiscoveryChain{1095 Protocol: "tcp",1096 StartNode: "resolver:main.default.dc1",1097 Nodes: map[string]*structs.DiscoveryGraphNode{1098 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1099 Type: structs.DiscoveryGraphNodeTypeResolver,1100 Name: "main.default.dc1",1101 Resolver: &structs.DiscoveryResolver{1102 ConnectTimeout: 5 * time.Second,1103 Target: "main.default.dc1",1104 Failover: &structs.DiscoveryFailover{1105 Targets: []string{"main.default.dc2", "main.default.dc4"},1106 },1107 },1108 },1109 },1110 Targets: map[string]*structs.DiscoveryTarget{1111 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1112 "main.default.dc2": newTarget("main", "", "default", "dc2", nil),1113 "main.default.dc4": newTarget("main", "", "default", "dc4", nil),1114 },1115 }1116 return compileTestCase{entries: entries, expect: expect}1117}1118func testcase_DatacenterFailover_WithMeshGateways() compileTestCase {1119 entries := newEntries()1120 entries.GlobalProxy = &structs.ProxyConfigEntry{1121 Kind: structs.ProxyDefaults,1122 Name: structs.ProxyConfigGlobal,1123 MeshGateway: structs.MeshGatewayConfig{1124 Mode: structs.MeshGatewayModeRemote,1125 },1126 }1127 entries.AddResolvers(1128 &structs.ServiceResolverConfigEntry{1129 Kind: "service-resolver",1130 Name: "main",1131 Failover: map[string]structs.ServiceResolverFailover{1132 "*": {Datacenters: []string{"dc2", "dc4"}},1133 },1134 },1135 )1136 expect := &structs.CompiledDiscoveryChain{1137 Protocol: "tcp",1138 StartNode: "resolver:main.default.dc1",1139 Nodes: map[string]*structs.DiscoveryGraphNode{1140 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1141 Type: structs.DiscoveryGraphNodeTypeResolver,1142 Name: "main.default.dc1",1143 Resolver: &structs.DiscoveryResolver{1144 ConnectTimeout: 5 * time.Second,1145 Target: "main.default.dc1",1146 Failover: &structs.DiscoveryFailover{1147 Targets: []string{1148 "main.default.dc2",1149 "main.default.dc4",1150 },1151 },1152 },1153 },1154 },1155 Targets: map[string]*structs.DiscoveryTarget{1156 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1157 "main.default.dc2": newTarget("main", "", "default", "dc2", func(t *structs.DiscoveryTarget) {1158 t.MeshGateway = structs.MeshGatewayConfig{1159 Mode: structs.MeshGatewayModeRemote,1160 }1161 }),1162 "main.default.dc4": newTarget("main", "", "default", "dc4", func(t *structs.DiscoveryTarget) {1163 t.MeshGateway = structs.MeshGatewayConfig{1164 Mode: structs.MeshGatewayModeRemote,1165 }1166 }),1167 },1168 }1169 return compileTestCase{entries: entries, expect: expect}1170}1171func testcase_NoopSplit_WithDefaultSubset() compileTestCase {1172 entries := newEntries()1173 setServiceProtocol(entries, "main", "http")1174 entries.AddSplitters(1175 &structs.ServiceSplitterConfigEntry{1176 Kind: "service-splitter",1177 Name: "main",1178 Splits: []structs.ServiceSplit{1179 {Weight: 100},1180 },1181 },1182 )1183 entries.AddResolvers(1184 &structs.ServiceResolverConfigEntry{1185 Kind: "service-resolver",1186 Name: "main",1187 DefaultSubset: "v2",1188 Subsets: map[string]structs.ServiceResolverSubset{1189 "v1": {Filter: "Service.Meta.version == 1"},1190 "v2": {Filter: "Service.Meta.version == 2"},1191 },1192 },1193 )1194 expect := &structs.CompiledDiscoveryChain{1195 Protocol: "http",1196 StartNode: "splitter:main",1197 Nodes: map[string]*structs.DiscoveryGraphNode{1198 "splitter:main": &structs.DiscoveryGraphNode{1199 Type: structs.DiscoveryGraphNodeTypeSplitter,1200 Name: "main",1201 Splits: []*structs.DiscoverySplit{1202 {1203 Weight: 100,1204 NextNode: "resolver:v2.main.default.dc1",1205 },1206 },1207 },1208 "resolver:v2.main.default.dc1": &structs.DiscoveryGraphNode{1209 Type: structs.DiscoveryGraphNodeTypeResolver,1210 Name: "v2.main.default.dc1",1211 Resolver: &structs.DiscoveryResolver{1212 ConnectTimeout: 5 * time.Second,1213 Target: "v2.main.default.dc1",1214 },1215 },1216 },1217 Targets: map[string]*structs.DiscoveryTarget{1218 "v2.main.default.dc1": newTarget("main", "v2", "default", "dc1", func(t *structs.DiscoveryTarget) {1219 t.Subset = structs.ServiceResolverSubset{1220 Filter: "Service.Meta.version == 2",1221 }1222 }),1223 },1224 }1225 return compileTestCase{entries: entries, expect: expect}1226}1227func testcase_DefaultResolver() compileTestCase {1228 entries := newEntries()1229 expect := &structs.CompiledDiscoveryChain{1230 Protocol: "tcp",1231 StartNode: "resolver:main.default.dc1",1232 Nodes: map[string]*structs.DiscoveryGraphNode{1233 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1234 Type: structs.DiscoveryGraphNodeTypeResolver,1235 Name: "main.default.dc1",1236 Resolver: &structs.DiscoveryResolver{1237 Default: true,1238 ConnectTimeout: 5 * time.Second,1239 Target: "main.default.dc1",1240 },1241 },1242 },1243 Targets: map[string]*structs.DiscoveryTarget{1244 // TODO-TARGET1245 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1246 },1247 }1248 return compileTestCase{entries: entries, expect: expect, expectIsDefault: true}1249}1250func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {1251 entries := newEntries()1252 entries.GlobalProxy = &structs.ProxyConfigEntry{1253 Kind: structs.ProxyDefaults,1254 Name: structs.ProxyConfigGlobal,1255 Config: map[string]interface{}{1256 "protocol": "grpc",1257 },1258 MeshGateway: structs.MeshGatewayConfig{1259 Mode: structs.MeshGatewayModeRemote,1260 },1261 }1262 expect := &structs.CompiledDiscoveryChain{1263 Protocol: "grpc",1264 StartNode: "resolver:main.default.dc1",1265 Nodes: map[string]*structs.DiscoveryGraphNode{1266 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1267 Type: structs.DiscoveryGraphNodeTypeResolver,1268 Name: "main.default.dc1",1269 Resolver: &structs.DiscoveryResolver{1270 Default: true,1271 ConnectTimeout: 5 * time.Second,1272 Target: "main.default.dc1",1273 },1274 },1275 },1276 Targets: map[string]*structs.DiscoveryTarget{1277 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1278 },1279 }1280 return compileTestCase{entries: entries, expect: expect, expectIsDefault: true}1281}1282func testcase_RedirectToDefaultResolverIsNotDefaultChain() compileTestCase {1283 entries := newEntries()1284 entries.AddResolvers(1285 &structs.ServiceResolverConfigEntry{1286 Kind: structs.ServiceResolver,1287 Name: "main",1288 Redirect: &structs.ServiceResolverRedirect{1289 Service: "other",1290 },1291 },1292 )1293 expect := &structs.CompiledDiscoveryChain{1294 Protocol: "tcp",1295 StartNode: "resolver:other.default.dc1",1296 Nodes: map[string]*structs.DiscoveryGraphNode{1297 "resolver:other.default.dc1": &structs.DiscoveryGraphNode{1298 Type: structs.DiscoveryGraphNodeTypeResolver,1299 Name: "other.default.dc1",1300 Resolver: &structs.DiscoveryResolver{1301 Default: true,1302 ConnectTimeout: 5 * time.Second,1303 Target: "other.default.dc1",1304 },1305 },1306 },1307 Targets: map[string]*structs.DiscoveryTarget{1308 "other.default.dc1": newTarget("other", "", "default", "dc1", nil),1309 },1310 }1311 return compileTestCase{entries: entries, expect: expect, expectIsDefault: false /*being explicit here because this is the whole point of this test*/}1312}1313func testcase_Resolve_WithDefaultSubset() compileTestCase {1314 entries := newEntries()1315 entries.AddResolvers(1316 &structs.ServiceResolverConfigEntry{1317 Kind: "service-resolver",1318 Name: "main",1319 DefaultSubset: "v2",1320 Subsets: map[string]structs.ServiceResolverSubset{1321 "v1": {Filter: "Service.Meta.version == 1"},1322 "v2": {Filter: "Service.Meta.version == 2"},1323 },1324 },1325 )1326 expect := &structs.CompiledDiscoveryChain{1327 Protocol: "tcp",1328 StartNode: "resolver:v2.main.default.dc1",1329 Nodes: map[string]*structs.DiscoveryGraphNode{1330 "resolver:v2.main.default.dc1": &structs.DiscoveryGraphNode{1331 Type: structs.DiscoveryGraphNodeTypeResolver,1332 Name: "v2.main.default.dc1",1333 Resolver: &structs.DiscoveryResolver{1334 ConnectTimeout: 5 * time.Second,1335 Target: "v2.main.default.dc1",1336 },1337 },1338 },1339 Targets: map[string]*structs.DiscoveryTarget{1340 "v2.main.default.dc1": newTarget("main", "v2", "default", "dc1", func(t *structs.DiscoveryTarget) {1341 t.Subset = structs.ServiceResolverSubset{1342 Filter: "Service.Meta.version == 2",1343 }1344 }),1345 },1346 }1347 return compileTestCase{entries: entries, expect: expect}1348}1349func testcase_DefaultResolver_ExternalSNI() compileTestCase {1350 entries := newEntries()1351 entries.AddServices(&structs.ServiceConfigEntry{1352 Kind: structs.ServiceDefaults,1353 Name: "main",1354 ExternalSNI: "main.some.other.service.mesh",1355 })1356 expect := &structs.CompiledDiscoveryChain{1357 Protocol: "tcp",1358 StartNode: "resolver:main.default.dc1",1359 Nodes: map[string]*structs.DiscoveryGraphNode{1360 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1361 Type: structs.DiscoveryGraphNodeTypeResolver,1362 Name: "main.default.dc1",1363 Resolver: &structs.DiscoveryResolver{1364 Default: true,1365 ConnectTimeout: 5 * time.Second,1366 Target: "main.default.dc1",1367 },1368 },1369 },1370 Targets: map[string]*structs.DiscoveryTarget{1371 "main.default.dc1": newTarget("main", "", "default", "dc1", func(t *structs.DiscoveryTarget) {1372 t.SNI = "main.some.other.service.mesh"1373 t.External = true1374 }),1375 },1376 }1377 return compileTestCase{entries: entries, expect: expect, expectIsDefault: true}1378}1379func testcase_Resolver_ExternalSNI_FailoverNotAllowed() compileTestCase {1380 entries := newEntries()1381 entries.AddServices(&structs.ServiceConfigEntry{1382 Kind: structs.ServiceDefaults,1383 Name: "main",1384 ExternalSNI: "main.some.other.service.mesh",1385 })1386 entries.AddResolvers(&structs.ServiceResolverConfigEntry{1387 Kind: "service-resolver",1388 Name: "main",1389 ConnectTimeout: 33 * time.Second,1390 Failover: map[string]structs.ServiceResolverFailover{1391 "*": {Service: "backup"},1392 },1393 })1394 return compileTestCase{1395 entries: entries,1396 expectErr: `service "main" has an external SNI set; cannot define failover for external services`,1397 expectGraphErr: true,1398 }1399}1400func testcase_Resolver_ExternalSNI_SubsetsNotAllowed() compileTestCase {1401 entries := newEntries()1402 entries.AddServices(&structs.ServiceConfigEntry{1403 Kind: structs.ServiceDefaults,1404 Name: "main",1405 ExternalSNI: "main.some.other.service.mesh",1406 })1407 entries.AddResolvers(&structs.ServiceResolverConfigEntry{1408 Kind: "service-resolver",1409 Name: "main",1410 ConnectTimeout: 33 * time.Second,1411 Subsets: map[string]structs.ServiceResolverSubset{1412 "canary": {1413 Filter: "Service.Meta.version == canary",1414 },1415 },1416 })1417 return compileTestCase{1418 entries: entries,1419 expectErr: `service "main" has an external SNI set; cannot define subsets for external services`,1420 expectGraphErr: true,1421 }1422}1423func testcase_Resolver_ExternalSNI_RedirectNotAllowed() compileTestCase {1424 entries := newEntries()1425 entries.AddServices(&structs.ServiceConfigEntry{1426 Kind: structs.ServiceDefaults,1427 Name: "main",1428 ExternalSNI: "main.some.other.service.mesh",1429 })1430 entries.AddResolvers(&structs.ServiceResolverConfigEntry{1431 Kind: "service-resolver",1432 Name: "main",1433 ConnectTimeout: 33 * time.Second,1434 Redirect: &structs.ServiceResolverRedirect{1435 Datacenter: "dc2",1436 },1437 })1438 return compileTestCase{1439 entries: entries,1440 expectErr: `service "main" has an external SNI set; cannot define redirects for external services`,1441 expectGraphErr: true,1442 }1443}1444func testcase_MultiDatacenterCanary() compileTestCase {1445 entries := newEntries()1446 setServiceProtocol(entries, "main", "http")1447 setServiceProtocol(entries, "main-dc2", "http")1448 setServiceProtocol(entries, "main-dc3", "http")1449 entries.AddSplitters(1450 &structs.ServiceSplitterConfigEntry{1451 Kind: "service-splitter",1452 Name: "main",1453 Splits: []structs.ServiceSplit{1454 {Weight: 60, Service: "main-dc2"},1455 {Weight: 40, Service: "main-dc3"},1456 },1457 },1458 )1459 entries.AddResolvers(1460 &structs.ServiceResolverConfigEntry{1461 Kind: "service-resolver",1462 Name: "main-dc2",1463 Redirect: &structs.ServiceResolverRedirect{1464 Service: "main",1465 Datacenter: "dc2",1466 },1467 },1468 &structs.ServiceResolverConfigEntry{1469 Kind: "service-resolver",1470 Name: "main-dc3",1471 Redirect: &structs.ServiceResolverRedirect{1472 Service: "main",1473 Datacenter: "dc3",1474 },1475 },1476 &structs.ServiceResolverConfigEntry{1477 Kind: "service-resolver",1478 Name: "main",1479 ConnectTimeout: 33 * time.Second,1480 },1481 )1482 expect := &structs.CompiledDiscoveryChain{1483 Protocol: "http",1484 StartNode: "splitter:main",1485 Nodes: map[string]*structs.DiscoveryGraphNode{1486 "splitter:main": &structs.DiscoveryGraphNode{1487 Type: structs.DiscoveryGraphNodeTypeSplitter,1488 Name: "main",1489 Splits: []*structs.DiscoverySplit{1490 {1491 Weight: 60,1492 NextNode: "resolver:main.default.dc2",1493 },1494 {1495 Weight: 40,1496 NextNode: "resolver:main.default.dc3",1497 },1498 },1499 },1500 "resolver:main.default.dc2": &structs.DiscoveryGraphNode{1501 Type: structs.DiscoveryGraphNodeTypeResolver,1502 Name: "main.default.dc2",1503 Resolver: &structs.DiscoveryResolver{1504 ConnectTimeout: 33 * time.Second,1505 Target: "main.default.dc2",1506 },1507 },1508 "resolver:main.default.dc3": &structs.DiscoveryGraphNode{1509 Type: structs.DiscoveryGraphNodeTypeResolver,1510 Name: "main.default.dc3",1511 Resolver: &structs.DiscoveryResolver{1512 ConnectTimeout: 33 * time.Second,1513 Target: "main.default.dc3",1514 },1515 },1516 },1517 Targets: map[string]*structs.DiscoveryTarget{1518 "main.default.dc2": newTarget("main", "", "default", "dc2", nil),1519 "main.default.dc3": newTarget("main", "", "default", "dc3", nil),1520 },1521 }1522 return compileTestCase{entries: entries, expect: expect}1523}1524func testcase_AllBellsAndWhistles() compileTestCase {1525 entries := newEntries()1526 setServiceProtocol(entries, "main", "http")1527 setServiceProtocol(entries, "svc-redirect", "http")1528 setServiceProtocol(entries, "svc-redirect-again", "http")1529 setServiceProtocol(entries, "svc-split", "http")1530 setServiceProtocol(entries, "svc-split-again", "http")1531 setServiceProtocol(entries, "svc-split-one-more-time", "http")1532 setServiceProtocol(entries, "redirected", "http")1533 entries.AddRouters(1534 &structs.ServiceRouterConfigEntry{1535 Kind: "service-router",1536 Name: "main",1537 Routes: []structs.ServiceRoute{1538 newSimpleRoute("svc-redirect"), // double redirected to a default subset1539 newSimpleRoute("svc-split"), // one split is split further1540 },1541 },1542 )1543 entries.AddSplitters(1544 &structs.ServiceSplitterConfigEntry{1545 Kind: "service-splitter",1546 Name: "svc-split",1547 Splits: []structs.ServiceSplit{1548 {Weight: 60, Service: "svc-redirect"}, // double redirected to a default subset1549 {Weight: 40, Service: "svc-split-again"}, // split again1550 },1551 },1552 &structs.ServiceSplitterConfigEntry{1553 Kind: "service-splitter",1554 Name: "svc-split-again",1555 Splits: []structs.ServiceSplit{1556 {Weight: 75, Service: "main", ServiceSubset: "v1"},1557 {Weight: 25, Service: "svc-split-one-more-time"},1558 },1559 },1560 &structs.ServiceSplitterConfigEntry{1561 Kind: "service-splitter",1562 Name: "svc-split-one-more-time",1563 Splits: []structs.ServiceSplit{1564 {Weight: 80, Service: "main", ServiceSubset: "v2"},1565 {Weight: 20, Service: "main", ServiceSubset: "v3"},1566 },1567 },1568 )1569 entries.AddResolvers(1570 &structs.ServiceResolverConfigEntry{1571 Kind: "service-resolver",1572 Name: "svc-redirect",1573 Redirect: &structs.ServiceResolverRedirect{1574 Service: "svc-redirect-again",1575 },1576 },1577 &structs.ServiceResolverConfigEntry{1578 Kind: "service-resolver",1579 Name: "svc-redirect-again",1580 Redirect: &structs.ServiceResolverRedirect{1581 Service: "redirected",1582 },1583 },1584 &structs.ServiceResolverConfigEntry{1585 Kind: "service-resolver",1586 Name: "redirected",1587 DefaultSubset: "prod",1588 Subsets: map[string]structs.ServiceResolverSubset{1589 "prod": {Filter: "ServiceMeta.env == prod"},1590 "qa": {Filter: "ServiceMeta.env == qa"},1591 },1592 },1593 &structs.ServiceResolverConfigEntry{1594 Kind: "service-resolver",1595 Name: "main",1596 DefaultSubset: "default-subset",1597 Subsets: map[string]structs.ServiceResolverSubset{1598 "v1": {Filter: "Service.Meta.version == 1"},1599 "v2": {Filter: "Service.Meta.version == 2"},1600 "v3": {Filter: "Service.Meta.version == 3"},1601 "default-subset": {OnlyPassing: true},1602 },1603 },1604 )1605 var (1606 router = entries.GetRouter("main")1607 )1608 expect := &structs.CompiledDiscoveryChain{1609 Protocol: "http",1610 StartNode: "router:main",1611 Nodes: map[string]*structs.DiscoveryGraphNode{1612 "router:main": &structs.DiscoveryGraphNode{1613 Type: structs.DiscoveryGraphNodeTypeRouter,1614 Name: "main",1615 Routes: []*structs.DiscoveryRoute{1616 {1617 Definition: &router.Routes[0],1618 NextNode: "resolver:prod.redirected.default.dc1",1619 },1620 {1621 Definition: &router.Routes[1],1622 NextNode: "splitter:svc-split",1623 },1624 {1625 Definition: newDefaultServiceRoute("main"),1626 NextNode: "resolver:default-subset.main.default.dc1",1627 },1628 },1629 },1630 "splitter:svc-split": &structs.DiscoveryGraphNode{1631 Type: structs.DiscoveryGraphNodeTypeSplitter,1632 Name: "svc-split",1633 Splits: []*structs.DiscoverySplit{1634 {1635 Weight: 60,1636 NextNode: "resolver:prod.redirected.default.dc1",1637 },1638 {1639 Weight: 30,1640 NextNode: "resolver:v1.main.default.dc1",1641 },1642 {1643 Weight: 8,1644 NextNode: "resolver:v2.main.default.dc1",1645 },1646 {1647 Weight: 2,1648 NextNode: "resolver:v3.main.default.dc1",1649 },1650 },1651 },1652 "resolver:prod.redirected.default.dc1": &structs.DiscoveryGraphNode{1653 Type: structs.DiscoveryGraphNodeTypeResolver,1654 Name: "prod.redirected.default.dc1",1655 Resolver: &structs.DiscoveryResolver{1656 ConnectTimeout: 5 * time.Second,1657 Target: "prod.redirected.default.dc1",1658 },1659 },1660 "resolver:v1.main.default.dc1": &structs.DiscoveryGraphNode{1661 Type: structs.DiscoveryGraphNodeTypeResolver,1662 Name: "v1.main.default.dc1",1663 Resolver: &structs.DiscoveryResolver{1664 ConnectTimeout: 5 * time.Second,1665 Target: "v1.main.default.dc1",1666 },1667 },1668 "resolver:v2.main.default.dc1": &structs.DiscoveryGraphNode{1669 Type: structs.DiscoveryGraphNodeTypeResolver,1670 Name: "v2.main.default.dc1",1671 Resolver: &structs.DiscoveryResolver{1672 ConnectTimeout: 5 * time.Second,1673 Target: "v2.main.default.dc1",1674 },1675 },1676 "resolver:v3.main.default.dc1": &structs.DiscoveryGraphNode{1677 Type: structs.DiscoveryGraphNodeTypeResolver,1678 Name: "v3.main.default.dc1",1679 Resolver: &structs.DiscoveryResolver{1680 ConnectTimeout: 5 * time.Second,1681 Target: "v3.main.default.dc1",1682 },1683 },1684 "resolver:default-subset.main.default.dc1": &structs.DiscoveryGraphNode{1685 Type: structs.DiscoveryGraphNodeTypeResolver,1686 Name: "default-subset.main.default.dc1",1687 Resolver: &structs.DiscoveryResolver{1688 ConnectTimeout: 5 * time.Second,1689 Target: "default-subset.main.default.dc1",1690 },1691 },1692 },1693 Targets: map[string]*structs.DiscoveryTarget{1694 "prod.redirected.default.dc1": newTarget("redirected", "prod", "default", "dc1", func(t *structs.DiscoveryTarget) {1695 t.Subset = structs.ServiceResolverSubset{1696 Filter: "ServiceMeta.env == prod",1697 }1698 }),1699 "v1.main.default.dc1": newTarget("main", "v1", "default", "dc1", func(t *structs.DiscoveryTarget) {1700 t.Subset = structs.ServiceResolverSubset{1701 Filter: "Service.Meta.version == 1",1702 }1703 }),1704 "v2.main.default.dc1": newTarget("main", "v2", "default", "dc1", func(t *structs.DiscoveryTarget) {1705 t.Subset = structs.ServiceResolverSubset{1706 Filter: "Service.Meta.version == 2",1707 }1708 }),1709 "v3.main.default.dc1": newTarget("main", "v3", "default", "dc1", func(t *structs.DiscoveryTarget) {1710 t.Subset = structs.ServiceResolverSubset{1711 Filter: "Service.Meta.version == 3",1712 }1713 }),1714 "default-subset.main.default.dc1": newTarget("main", "default-subset", "default", "dc1", func(t *structs.DiscoveryTarget) {1715 t.Subset = structs.ServiceResolverSubset{OnlyPassing: true}1716 }),1717 },1718 }1719 return compileTestCase{entries: entries, expect: expect}1720}1721func testcase_SplitterRequiresValidProtocol() compileTestCase {1722 entries := newEntries()1723 setServiceProtocol(entries, "main", "tcp")1724 entries.AddSplitters(1725 &structs.ServiceSplitterConfigEntry{1726 Kind: structs.ServiceSplitter,1727 Name: "main",1728 Splits: []structs.ServiceSplit{1729 {Weight: 90, Namespace: "v1"},1730 {Weight: 10, Namespace: "v2"},1731 },1732 },1733 )1734 return compileTestCase{1735 entries: entries,1736 expectErr: "does not permit advanced routing or splitting behavior",1737 expectGraphErr: true,1738 }1739}1740func testcase_RouterRequiresValidProtocol() compileTestCase {1741 entries := newEntries()1742 setServiceProtocol(entries, "main", "tcp")1743 entries.AddRouters(1744 &structs.ServiceRouterConfigEntry{1745 Kind: structs.ServiceRouter,1746 Name: "main",1747 Routes: []structs.ServiceRoute{1748 {1749 Match: &structs.ServiceRouteMatch{1750 HTTP: &structs.ServiceRouteHTTPMatch{1751 PathExact: "/other",1752 },1753 },1754 Destination: &structs.ServiceRouteDestination{1755 Namespace: "other",1756 },1757 },1758 },1759 },1760 )1761 return compileTestCase{1762 entries: entries,1763 expectErr: "does not permit advanced routing or splitting behavior",1764 expectGraphErr: true,1765 }1766}1767func testcase_SplitToUnsplittableProtocol() compileTestCase {1768 entries := newEntries()1769 setServiceProtocol(entries, "main", "tcp")1770 setServiceProtocol(entries, "other", "tcp")1771 entries.AddSplitters(1772 &structs.ServiceSplitterConfigEntry{1773 Kind: structs.ServiceSplitter,1774 Name: "main",1775 Splits: []structs.ServiceSplit{1776 {Weight: 90},1777 {Weight: 10, Service: "other"},1778 },1779 },1780 )1781 return compileTestCase{1782 entries: entries,1783 expectErr: "does not permit advanced routing or splitting behavior",1784 expectGraphErr: true,1785 }1786}1787func testcase_RouteToUnroutableProtocol() compileTestCase {1788 entries := newEntries()1789 setServiceProtocol(entries, "main", "tcp")1790 setServiceProtocol(entries, "other", "tcp")1791 entries.AddRouters(1792 &structs.ServiceRouterConfigEntry{1793 Kind: structs.ServiceRouter,1794 Name: "main",1795 Routes: []structs.ServiceRoute{1796 {1797 Match: &structs.ServiceRouteMatch{1798 HTTP: &structs.ServiceRouteHTTPMatch{1799 PathExact: "/other",1800 },1801 },1802 Destination: &structs.ServiceRouteDestination{1803 Service: "other",1804 },1805 },1806 },1807 },1808 )1809 return compileTestCase{1810 entries: entries,1811 expectErr: "does not permit advanced routing or splitting behavior",1812 expectGraphErr: true,1813 }1814}1815func testcase_FailoverCrossesProtocols() compileTestCase {1816 entries := newEntries()1817 setServiceProtocol(entries, "main", "grpc")1818 setServiceProtocol(entries, "other", "tcp")1819 entries.AddResolvers(1820 &structs.ServiceResolverConfigEntry{1821 Kind: structs.ServiceResolver,1822 Name: "main",1823 Failover: map[string]structs.ServiceResolverFailover{1824 "*": structs.ServiceResolverFailover{1825 Service: "other",1826 },1827 },1828 },1829 )1830 return compileTestCase{1831 entries: entries,1832 expectErr: "uses inconsistent protocols",1833 expectGraphErr: true,1834 }1835}1836func testcase_RedirectCrossesProtocols() compileTestCase {1837 entries := newEntries()1838 setServiceProtocol(entries, "main", "grpc")1839 setServiceProtocol(entries, "other", "tcp")1840 entries.AddResolvers(1841 &structs.ServiceResolverConfigEntry{1842 Kind: structs.ServiceResolver,1843 Name: "main",1844 Redirect: &structs.ServiceResolverRedirect{1845 Service: "other",1846 },1847 },1848 )1849 return compileTestCase{1850 entries: entries,1851 expectErr: "uses inconsistent protocols",1852 expectGraphErr: true,1853 }1854}1855func testcase_RedirectToMissingSubset() compileTestCase {1856 entries := newEntries()1857 entries.AddResolvers(1858 &structs.ServiceResolverConfigEntry{1859 Kind: structs.ServiceResolver,1860 Name: "other",1861 ConnectTimeout: 33 * time.Second,1862 },1863 &structs.ServiceResolverConfigEntry{1864 Kind: structs.ServiceResolver,1865 Name: "main",1866 Redirect: &structs.ServiceResolverRedirect{1867 Service: "other",1868 ServiceSubset: "v1",1869 },1870 },1871 )1872 return compileTestCase{1873 entries: entries,1874 expectErr: `does not have a subset named "v1"`,1875 expectGraphErr: true,1876 }1877}1878func testcase_ResolverProtocolOverride() compileTestCase {1879 entries := newEntries()1880 setServiceProtocol(entries, "main", "grpc")1881 expect := &structs.CompiledDiscoveryChain{1882 Protocol: "http2",1883 StartNode: "resolver:main.default.dc1",1884 Nodes: map[string]*structs.DiscoveryGraphNode{1885 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1886 Type: structs.DiscoveryGraphNodeTypeResolver,1887 Name: "main.default.dc1",1888 Resolver: &structs.DiscoveryResolver{1889 Default: true,1890 ConnectTimeout: 5 * time.Second,1891 Target: "main.default.dc1",1892 },1893 },1894 },1895 Targets: map[string]*structs.DiscoveryTarget{1896 // TODO-TARGET1897 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1898 },1899 }1900 return compileTestCase{entries: entries, expect: expect, expectIsDefault: true,1901 expectCustom: true,1902 setup: func(req *CompileRequest) {1903 req.OverrideProtocol = "http2"1904 },1905 }1906}1907func testcase_ResolverProtocolOverrideIgnored() compileTestCase {1908 // This shows that if you try to override the protocol to its current value1909 // the override is completely ignored.1910 entries := newEntries()1911 setServiceProtocol(entries, "main", "http2")1912 expect := &structs.CompiledDiscoveryChain{1913 Protocol: "http2",1914 StartNode: "resolver:main.default.dc1",1915 Nodes: map[string]*structs.DiscoveryGraphNode{1916 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1917 Type: structs.DiscoveryGraphNodeTypeResolver,1918 Name: "main.default.dc1",1919 Resolver: &structs.DiscoveryResolver{1920 Default: true,1921 ConnectTimeout: 5 * time.Second,1922 Target: "main.default.dc1",1923 },1924 },1925 },1926 Targets: map[string]*structs.DiscoveryTarget{1927 // TODO-TARGET1928 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1929 },1930 }1931 return compileTestCase{entries: entries, expect: expect, expectIsDefault: true,1932 setup: func(req *CompileRequest) {1933 req.OverrideProtocol = "http2"1934 },1935 }1936}1937func testcase_RouterIgnored_ResolverProtocolOverride() compileTestCase {1938 entries := newEntries()1939 setServiceProtocol(entries, "main", "grpc")1940 entries.AddRouters(1941 &structs.ServiceRouterConfigEntry{1942 Kind: "service-router",1943 Name: "main",1944 },1945 )1946 expect := &structs.CompiledDiscoveryChain{1947 Protocol: "tcp",1948 StartNode: "resolver:main.default.dc1",1949 Nodes: map[string]*structs.DiscoveryGraphNode{1950 "resolver:main.default.dc1": &structs.DiscoveryGraphNode{1951 Type: structs.DiscoveryGraphNodeTypeResolver,1952 Name: "main.default.dc1",1953 Resolver: &structs.DiscoveryResolver{1954 Default: true,1955 ConnectTimeout: 5 * time.Second,1956 Target: "main.default.dc1",1957 },1958 },1959 },1960 Targets: map[string]*structs.DiscoveryTarget{1961 // TODO-TARGET1962 "main.default.dc1": newTarget("main", "", "default", "dc1", nil),1963 },1964 }1965 return compileTestCase{entries: entries, expect: expect, expectIsDefault: true,1966 expectCustom: true,1967 setup: func(req *CompileRequest) {1968 req.OverrideProtocol = "tcp"1969 },1970 }1971}1972func testcase_Resolver_CircularRedirect() compileTestCase {1973 entries := newEntries()1974 entries.AddResolvers(1975 &structs.ServiceResolverConfigEntry{1976 Kind: "service-resolver",1977 Name: "main",1978 Redirect: &structs.ServiceResolverRedirect{1979 Service: "other",1980 },1981 },1982 &structs.ServiceResolverConfigEntry{1983 Kind: "service-resolver",1984 Name: "other",1985 Redirect: &structs.ServiceResolverRedirect{1986 Service: "main",1987 },1988 },1989 )1990 return compileTestCase{entries: entries,1991 expectErr: "detected circular resolver redirect",1992 expectGraphErr: true,1993 }1994}1995func testcase_CircularSplit() compileTestCase {1996 entries := newEntries()1997 setGlobalProxyProtocol(entries, "http")1998 entries.AddSplitters(1999 &structs.ServiceSplitterConfigEntry{2000 Kind: "service-splitter",2001 Name: "main",2002 Splits: []structs.ServiceSplit{2003 {Weight: 100, Service: "other"},2004 },2005 },2006 &structs.ServiceSplitterConfigEntry{2007 Kind: "service-splitter",2008 Name: "other",2009 Splits: []structs.ServiceSplit{2010 {Weight: 100, Service: "main"},2011 },2012 },2013 )2014 return compileTestCase{entries: entries,2015 expectErr: "detected circular reference",2016 expectGraphErr: true,2017 }2018}2019func newSimpleRoute(name string, muts ...func(*structs.ServiceRoute)) structs.ServiceRoute {2020 r := structs.ServiceRoute{2021 Match: &structs.ServiceRouteMatch{2022 HTTP: &structs.ServiceRouteHTTPMatch{PathPrefix: "/" + name},2023 },2024 Destination: &structs.ServiceRouteDestination{Service: name},2025 }2026 for _, mut := range muts {2027 mut(&r)2028 }...
graph_test.go
Source:graph_test.go
1// Copyright 2016 The Gitea Authors. All rights reserved.2// Use of this source code is governed by a MIT-style3// license that can be found in the LICENSE file.4package gitgraph5import (6 "bytes"7 "fmt"8 "strings"9 "testing"10 "code.gitea.io/gitea/modules/git"11)12func BenchmarkGetCommitGraph(b *testing.B) {13 currentRepo, err := git.OpenRepository(".")14 if err != nil || currentRepo == nil {15 b.Error("Could not open repository")16 }17 defer currentRepo.Close()18 for i := 0; i < b.N; i++ {19 graph, err := GetCommitGraph(currentRepo, 1, 0, false, nil, nil)20 if err != nil {21 b.Error("Could get commit graph")22 }23 if len(graph.Commits) < 100 {24 b.Error("Should get 100 log lines.")25 }26 }27}28func BenchmarkParseCommitString(b *testing.B) {29 testString := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|Add route for graph"30 parser := &Parser{}31 parser.Reset()32 for i := 0; i < b.N; i++ {33 parser.Reset()34 graph := NewGraph()35 if err := parser.AddLineToGraph(graph, 0, []byte(testString)); err != nil {36 b.Error("could not parse teststring")37 }38 if graph.Flows[1].Commits[0].Rev != "4e61bacab44e9b4730e44a6615d04098dd3a8eaf" {39 b.Error("Did not get expected data")40 }41 }42}43func BenchmarkParseGlyphs(b *testing.B) {44 parser := &Parser{}45 parser.Reset()46 tgBytes := []byte(testglyphs)47 tg := tgBytes48 idx := bytes.Index(tg, []byte("\n"))49 for i := 0; i < b.N; i++ {50 parser.Reset()51 tg = tgBytes52 idx = bytes.Index(tg, []byte("\n"))53 for idx > 0 {54 parser.ParseGlyphs(tg[:idx])55 tg = tg[idx+1:]56 idx = bytes.Index(tg, []byte("\n"))57 }58 }59}60func TestReleaseUnusedColors(t *testing.T) {61 testcases := []struct {62 availableColors []int63 oldColors []int64 firstInUse int // these values have to be either be correct or suggest less is65 firstAvailable int // available than possibly is - i.e. you cannot say 10 is available when it66 }{67 {68 availableColors: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},69 oldColors: []int{1, 1, 1, 1, 1},70 firstAvailable: -1,71 firstInUse: 1,72 },73 {74 availableColors: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},75 oldColors: []int{1, 2, 3, 4},76 firstAvailable: 6,77 firstInUse: 0,78 },79 {80 availableColors: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},81 oldColors: []int{6, 0, 3, 5, 3, 4, 0, 0},82 firstAvailable: 6,83 firstInUse: 0,84 },85 {86 availableColors: []int{1, 2, 3, 4, 5, 6, 7},87 oldColors: []int{6, 1, 3, 5, 3, 4, 2, 7},88 firstAvailable: -1,89 firstInUse: 0,90 },91 {92 availableColors: []int{1, 2, 3, 4, 5, 6, 7},93 oldColors: []int{6, 0, 3, 5, 3, 4, 2, 7},94 firstAvailable: -1,95 firstInUse: 0,96 },97 }98 for _, testcase := range testcases {99 parser := &Parser{}100 parser.Reset()101 parser.availableColors = append([]int{}, testcase.availableColors...)102 parser.oldColors = append(parser.oldColors, testcase.oldColors...)103 parser.firstAvailable = testcase.firstAvailable104 parser.firstInUse = testcase.firstInUse105 parser.releaseUnusedColors()106 if parser.firstAvailable == -1 {107 // All in use108 for _, color := range parser.availableColors {109 found := false110 for _, oldColor := range parser.oldColors {111 if oldColor == color {112 found = true113 break114 }115 }116 if !found {117 t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should be available but is not",118 testcase.availableColors,119 testcase.oldColors,120 testcase.firstAvailable,121 testcase.firstInUse,122 parser.availableColors,123 parser.oldColors,124 parser.firstAvailable,125 parser.firstInUse,126 color)127 }128 }129 } else if parser.firstInUse != -1 {130 // Some in use131 for i := parser.firstInUse; i != parser.firstAvailable; i = (i + 1) % len(parser.availableColors) {132 color := parser.availableColors[i]133 found := false134 for _, oldColor := range parser.oldColors {135 if oldColor == color {136 found = true137 break138 }139 }140 if !found {141 t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should be available but is not",142 testcase.availableColors,143 testcase.oldColors,144 testcase.firstAvailable,145 testcase.firstInUse,146 parser.availableColors,147 parser.oldColors,148 parser.firstAvailable,149 parser.firstInUse,150 color)151 }152 }153 for i := parser.firstAvailable; i != parser.firstInUse; i = (i + 1) % len(parser.availableColors) {154 color := parser.availableColors[i]155 found := false156 for _, oldColor := range parser.oldColors {157 if oldColor == color {158 found = true159 break160 }161 }162 if found {163 t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should not be available but is",164 testcase.availableColors,165 testcase.oldColors,166 testcase.firstAvailable,167 testcase.firstInUse,168 parser.availableColors,169 parser.oldColors,170 parser.firstAvailable,171 parser.firstInUse,172 color)173 }174 }175 } else {176 // None in use177 for _, color := range parser.oldColors {178 if color != 0 {179 t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should not be available but is",180 testcase.availableColors,181 testcase.oldColors,182 testcase.firstAvailable,183 testcase.firstInUse,184 parser.availableColors,185 parser.oldColors,186 parser.firstAvailable,187 parser.firstInUse,188 color)189 }190 }191 }192 }193}194func TestParseGlyphs(t *testing.T) {195 parser := &Parser{}196 parser.Reset()197 tgBytes := []byte(testglyphs)198 tg := tgBytes199 idx := bytes.Index(tg, []byte("\n"))200 row := 0201 for idx > 0 {202 parser.ParseGlyphs(tg[:idx])203 tg = tg[idx+1:]204 idx = bytes.Index(tg, []byte("\n"))205 if parser.flows[0] != 1 {206 t.Errorf("First column flow should be 1 but was %d", parser.flows[0])207 }208 colorToFlow := map[int]int64{}209 flowToColor := map[int64]int{}210 for i, flow := range parser.flows {211 if flow == 0 {212 continue213 }214 color := parser.colors[i]215 if fColor, in := flowToColor[flow]; in && fColor != color {216 t.Errorf("Row %d column %d flow %d has color %d but should be %d", row, i, flow, color, fColor)217 }218 flowToColor[flow] = color219 if cFlow, in := colorToFlow[color]; in && cFlow != flow {220 t.Errorf("Row %d column %d flow %d has color %d but conflicts with flow %d", row, i, flow, color, cFlow)221 }222 colorToFlow[color] = flow223 }224 row++225 }226 if len(parser.availableColors) != 9 {227 t.Errorf("Expected 9 colors but have %d", len(parser.availableColors))228 }229}230func TestCommitStringParsing(t *testing.T) {231 dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|"232 tests := []struct {233 shouldPass bool234 testName string235 commitMessage string236 }{237 {true, "normal", "not a fancy message"},238 {true, "extra pipe", "An extra pipe: |"},239 {true, "extra 'Data:'", "DATA: might be trouble"},240 }241 for _, test := range tests {242 t.Run(test.testName, func(t *testing.T) {243 testString := fmt.Sprintf("%s%s", dataFirstPart, test.commitMessage)244 idx := strings.Index(testString, "DATA:")245 commit, err := NewCommit(0, 0, []byte(testString[idx+5:]))246 if err != nil && test.shouldPass {247 t.Errorf("Could not parse %s", testString)248 return249 }250 if test.commitMessage != commit.Subject {251 t.Errorf("%s does not match %s", test.commitMessage, commit.Subject)252 }253 })254 }255}256var testglyphs = `* 257* 258* 259* 260* 261* 262* 263* 264|\ 265* | 266* | 267* | 268* | 269* | 270| * 271* | 272| * 273| |\ 274* | | 275| | * 276| | |\ 277* | | \ 278|\ \ \ \ 279| * | | | 280| |\| | | 281* | | | | 282|/ / / / 283| | | * 284| * | | 285| * | | 286| * | | 287* | | | 288* | | | 289* | | | 290* | | | 291* | | | 292|\ \ \ \ 293| | * | | 294| | |\| | 295| | | * | 296| | | | * 297* | | | | 298* | | | | 299* | | | | 300* | | | | 301* | | | | 302|\ \ \ \ \ 303| * | | | | 304|/| | | | | 305| | |/ / / 306| |/| | | 307| | | | * 308| * | | | 309|/| | | | 310| * | | | 311|/| | | | 312| | |/ / 313| |/| | 314| * | | 315| * | | 316| |\ \ \ 317| | * | | 318| |/| | | 319| | | |/ 320| | |/| 321| * | | 322| * | | 323| * | | 324| | * | 325| | |\ \ 326| | | * | 327| | |/| | 328| | | * | 329| | | |\ \ 330| | | | * | 331| | | |/| | 332| | * | | | 333| | * | | | 334| | |\ \ \ \ 335| | | * | | | 336| | |/| | | | 337| | | | | * | 338| | | | |/ / 339* | | | / / 340|/ / / / / 341* | | | | 342|\ \ \ \ \ 343| * | | | | 344|/| | | | | 345| * | | | | 346| * | | | | 347| |\ \ \ \ \ 348| | | * \ \ \ 349| | | |\ \ \ \ 350| | | | * | | | 351| | | |/| | | | 352| | | | | |/ / 353| | | | |/| | 354* | | | | | | 355* | | | | | | 356* | | | | | | 357| | | | * | | 358* | | | | | | 359| | * | | | | 360| |/| | | | | 361* | | | | | | 362| |/ / / / / 363|/| | | | | 364| | | | * | 365| | | |/ / 366| | |/| | 367| * | | | 368| | | | * 369| | * | | 370| | |\ \ \ 371| | | * | | 372| | |/| | | 373| | | |/ / 374| | | * | 375| | * | | 376| | |\ \ \ 377| | | * | | 378| | |/| | | 379| | | |/ / 380| | | * | 381* | | | | 382|\ \ \ \ \ 383| * \ \ \ \ 384| |\ \ \ \ \ 385| | | |/ / / 386| | |/| | | 387| | | | * | 388| | | | * | 389* | | | | | 390* | | | | | 391|/ / / / / 392| | | * | 393* | | | | 394* | | | | 395* | | | | 396* | | | | 397|\ \ \ \ \ 398| * | | | | 399|/| | | | | 400| | * | | | 401| | |\ \ \ \ 402| | | * | | | 403| | |/| | | | 404| |/| | |/ / 405| | | |/| | 406| | | | | * 407| |_|_|_|/ 408|/| | | | 409| | * | | 410| |/ / / 411* | | | 412* | | | 413| | * | 414* | | | 415* | | | 416| * | | 417| | * | 418| * | | 419* | | | 420|\ \ \ \ 421| * | | | 422|/| | | | 423| |/ / / 424| * | | 425| |\ \ \ 426| | * | | 427| |/| | | 428| | |/ / 429| | * | 430| | |\ \ 431| | | * | 432| | |/| | 433* | | | | 434* | | | | 435|\ \ \ \ \ 436| * | | | | 437|/| | | | | 438| | * | | | 439| | * | | | 440| | * | | | 441| |/ / / / 442| * | | | 443| |\ \ \ \ 444| | * | | | 445| |/| | | | 446* | | | | | 447* | | | | | 448* | | | | | 449* | | | | | 450* | | | | | 451| | | | * | 452* | | | | | 453|\ \ \ \ \ \ 454| * | | | | | 455|/| | | | | | 456| | | | | * | 457| | | | |/ / 458* | | | | | 459|\ \ \ \ \ \ 460* | | | | | | 461* | | | | | | 462| | | | * | | 463* | | | | | | 464* | | | | | | 465|\ \ \ \ \ \ \ 466| | |_|_|/ / / 467| |/| | | | | 468| | | | * | | 469| | | | * | | 470| | | | * | | 471| | | | * | | 472| | | | * | | 473| | | | * | | 474| | | |/ / / 475| | | * | | 476| | | * | | 477| | | * | | 478| | |/| | | 479| | | * | | 480| | |/| | | 481| | | |/ / 482| | * | | 483| |/| | | 484| | | * | 485| | |/ / 486| | * | 487| * | | 488| |\ \ \ 489| * | | | 490| | * | | 491| |/| | | 492| | |/ / 493| | * | 494| | |\ \ 495| | * | | 496* | | | | 497|\| | | | 498| * | | | 499| * | | | 500| * | | | 501| | * | | 502| * | | | 503| |\| | | 504| * | | | 505| | * | | 506| | * | | 507| * | | | 508| * | | | 509| * | | | 510| * | | | 511| * | | | 512| * | | | 513| * | | | 514| * | | | 515| | * | | 516| * | | | 517| * | | | 518| * | | | 519| * | | | 520| | * | | 521* | | | | 522|\| | | | 523| | * | | 524| * | | | 525| |\| | | 526| | * | | 527| | * | | 528| | * | | 529| | | * | 530* | | | | 531|\| | | | 532| | * | | 533| | |/ / 534| * | | 535| * | | 536| |\| | 537* | | | 538|\| | | 539| | * | 540| | * | 541| | * | 542| * | | 543| | * | 544| * | | 545| | * | 546| | * | 547| | * | 548| * | | 549| * | | 550| * | | 551| * | | 552| * | | 553| * | | 554| * | | 555* | | | 556|\| | | 557| * | | 558| |\| | 559| | * | 560| | |\ \ 561* | | | | 562|\| | | | 563| * | | | 564| |\| | | 565| | * | | 566| | | * | 567| | |/ / 568* | | | 569* | | | 570|\| | | 571| * | | 572| |\| | 573| | * | 574| | * | 575| | * | 576| | | * 577* | | | 578|\| | | 579| * | | 580| * | | 581| | | * 582| | | |\ 583* | | | | 584| |_|_|/ 585|/| | | 586| * | | 587| |\| | 588| | * | 589| | * | 590| | * | 591| | * | 592| | * | 593| * | | 594* | | | 595|\| | | 596| * | | 597|/| | | 598| |/ / 599| * | 600| |\ \ 601| * | | 602| * | | 603* | | | 604|\| | | 605| | * | 606| * | | 607| * | | 608| * | | 609* | | | 610|\| | | 611| * | | 612| * | | 613| | * | 614| | |\ \ 615| | |/ / 616| |/| | 617| * | | 618* | | | 619|\| | | 620| * | | 621* | | | 622|\| | | 623| * | | 624| |\ \ \ 625| * | | | 626| * | | | 627| | | * | 628| * | | | 629| * | | | 630| | |/ / 631| |/| | 632| | * | 633* | | | 634|\| | | 635| * | | 636| * | | 637| * | | 638| * | | 639| * | | 640| |\ \ \ 641* | | | | 642|\| | | | 643| * | | | 644| * | | | 645* | | | | 646* | | | | 647|\| | | | 648| | | | * 649| | | | |\ 650| |_|_|_|/ 651|/| | | | 652| * | | | 653* | | | | 654* | | | | 655|\| | | | 656| * | | | 657| |\ \ \ \ 658| | | |/ / 659| | |/| | 660| * | | | 661| * | | | 662| * | | | 663| * | | | 664| | * | | 665| | | * | 666| | |/ / 667| |/| | 668* | | | 669|\| | | 670| * | | 671| * | | 672| * | | 673| * | | 674| * | | 675* | | | 676|\| | | 677| * | | 678| * | | 679* | | | 680| * | | 681| * | | 682| * | | 683* | | | 684* | | | 685* | | | 686|\| | | 687| * | | 688* | | | 689* | | | 690* | | | 691* | | | 692| | | * 693* | | | 694|\| | | 695| * | | 696| * | | 697| * | | 698`...
TestCase
Using AI Code Generation
1import (2func main() {3 g := graph.NewGraph(5)4 g.AddEdge(0, 1)5 g.AddEdge(0, 2)6 g.AddEdge(1, 2)7 g.AddEdge(2, 0)8 g.AddEdge(2, 3)9 g.AddEdge(3, 3)10 g.AddEdge(4, 4)11 g.AddEdge(4, 3)12 g.AddEdge(4, 1)13 g.AddEdge(4, 2)14 g.AddEdge(4, 0)15 g.AddEdge(4, 3)16 g.AddEdge(4, 4)17 fmt.Println(g)18}19Related posts: GoLang | Program to implement Depth First Search (DFS) in a Graph GoLang | Program to implement Breadth First Search (BFS) in a Graph GoLang | Program to implement Depth First Search (DFS) in a Graph using Recursion GoLang | Program to implement Depth First Search (DFS) in a Graph using Stack GoLang | Program to implement Breadth First Search (BFS) in a Graph using Queue GoLang | Program to implement Breadth First Search (BFS) in a Graph using Recursion GoLang | Program to implement Breadth First Search (BFS) in a Graph using Stack GoLang | Program to implement Depth First Search (DFS) in a Graph using Queue
TestCase
Using AI Code Generation
1import "fmt"2func main() {3 g := graph{}4 g.AddEdge(0, 1)5 g.AddEdge(1, 2)6 g.AddEdge(2, 0)7 g.AddEdge(2, 3)8 g.AddEdge(3, 3)9 g.AddEdge(4, 5)10}11import "fmt"12func main() {13 g := graph{}14 g.AddEdge(0, 1)15 g.AddEdge(1, 2)16 g.AddEdge(2, 0)17 g.AddEdge(2, 3)18 g.AddEdge(3, 3)19 g.AddEdge(4, 5)20}21import "fmt"22func main() {23 g := graph{}24 g.AddEdge(0, 1)25 g.AddEdge(1, 2)26 g.AddEdge(2, 0)27 g.AddEdge(2, 3)28 g.AddEdge(3, 3)29 g.AddEdge(4, 5)30}31import "fmt"32func main() {33 g := graph{}34 g.AddEdge(0, 1)35 g.AddEdge(1, 2)36 g.AddEdge(2, 0)37 g.AddEdge(2, 3)38 g.AddEdge(3, 3)39 g.AddEdge(4, 5)
TestCase
Using AI Code Generation
1import (2func main() {3 g.AddEdge(0, 1)4 g.AddEdge(0, 2)5 g.AddEdge(1, 2)6 g.AddEdge(2, 0)7 g.AddEdge(2, 3)8 g.AddEdge(3, 3)9 g.TestCase(2, 1)10 g.TestCase(1, 3)11}12import (13func main() {14 g.AddEdge(0, 1)15 g.AddEdge(0, 2)16 g.AddEdge(1, 2)17 g.AddEdge(2, 0)18 g.AddEdge(2, 3)19 g.AddEdge(3, 3)20 g.TestCase(2, 1)21 g.TestCase(1, 3)22}23import (24func main() {25 g.AddEdge(0, 1)26 g.AddEdge(0, 2)27 g.AddEdge(1, 2)28 g.AddEdge(2, 0)29 g.AddEdge(2, 3)30 g.AddEdge(3, 3)31 g.TestCase(2, 1)32 g.TestCase(1, 3)33}34import (35func main() {36 g.AddEdge(0, 1)37 g.AddEdge(0, 2)38 g.AddEdge(1, 2)39 g.AddEdge(2, 0)40 g.AddEdge(2, 3)41 g.AddEdge(3, 3)42 g.TestCase(2, 1)43 g.TestCase(1, 3)44}45import (46func main() {47 g.AddEdge(0, 1)48 g.AddEdge(0, 2)49 g.AddEdge(1, 2)
TestCase
Using AI Code Generation
1import (2func main() {3 reader := bufio.NewReader(os.Stdin)4 fmt.Print("Enter the number of test cases: ")5 text, _ := reader.ReadString('6 text = strings.TrimSuffix(text, "7 test, _ := strconv.Atoi(text)8 g := make([]graph, test)9 for i := 0; i < test; i++ {10 fmt.Print("Enter the number of vertices: ")11 text, _ := reader.ReadString('12 text = strings.TrimSuffix(text, "13 vertices, _ := strconv.Atoi(text)14 g[i] = graph{vertices, make(map[int][]int)}15 fmt.Print("Enter the number of edges: ")16 text, _ = reader.ReadString('17 text = strings.TrimSuffix(text, "18 edges, _ := strconv.Atoi(text)19 fmt.Println("Enter the edges: ")20 for j := 0; j < edges; j++ {21 text, _ = reader.ReadString('22 text = strings.TrimSuffix(text, "23 edge := strings.Fields(text)24 v1, _ := strconv.Atoi(edge[0])25 v2, _ := strconv.Atoi(edge[1])26 g[i].addEdge(v1, v2)27 }28 }29 for i := 0; i < test; i++ {30 g[i].testcase()31 }32}33type graph struct {34}35func (g *graph) addEdge(v1 int, v2 int) {36 g.adj[v1] = append(g.adj[v1], v2)37 g.adj[v2] = append(g.adj[v2], v1)38}39func (g *graph) testcase() {40 visited := make([]bool, g.vertices)41 for i := 0; i < g.vertices; i++ {42 if len(g.adj[i]) > 0 {43 }44 }45 g.dfs(s, visited)46 for i := 0; i <
TestCase
Using AI Code Generation
1func main() {2 fmt.Println("Hello World!")3 g := graph.NewGraph()4 g.AddEdge(0, 1)5 g.AddEdge(0, 2)6 g.AddEdge(1, 3)7 g.AddEdge(1, 4)8 g.AddEdge(2, 5)9 g.AddEdge(2, 6)10 g.AddEdge(3, 7)11 g.AddEdge(3, 8)12 g.AddEdge(4, 9)13 g.AddEdge(4, 10)14 g.AddEdge(5, 11)15 g.AddEdge(5, 12)16 g.AddEdge(6, 13)17 g.AddEdge(6, 14)18 g.AddEdge(7, 15)19 g.AddEdge(7, 16)20 g.AddEdge(8, 17)21 g.AddEdge(8, 18)22 g.AddEdge(9, 19)23 g.AddEdge(9, 20)24 g.AddEdge(10, 21)25 g.AddEdge(10, 22)26 g.AddEdge(11, 23)27 g.AddEdge(11, 24)28 g.AddEdge(12, 25)29 g.AddEdge(12, 26)30 g.AddEdge(13, 27)31 g.AddEdge(13, 28)32 g.AddEdge(14, 29)33 g.AddEdge(14, 30)34 g.AddEdge(15, 31)35 g.AddEdge(15, 32)36 g.AddEdge(16, 33)37 g.AddEdge(16, 34)38 g.AddEdge(17, 35)39 g.AddEdge(17, 36)40 g.AddEdge(18, 37)41 g.AddEdge(18, 38)42 g.AddEdge(19, 39)43 g.AddEdge(19, 40)44 g.AddEdge(20, 41)45 g.AddEdge(20, 42)46 g.AddEdge(21, 43)47 g.AddEdge(21, 44)48 g.AddEdge(22, 45)49 g.AddEdge(22, 46)50 g.AddEdge(23, 47)51 g.AddEdge(23, 48)
TestCase
Using AI Code Generation
1import (2func main() {3 fmt.Println("Enter the number of vertices")4 fmt.Scanln(&num)5 g := graph{num}6 fmt.Println("Enter the vertices")7 for i := 0; i < num; i++ {8 fmt.Scanln(&str)9 }10 fmt.Println("Enter the number of edges")11 fmt.Scanln(&num)12 g.edges = make([]edge, num)13 fmt.Println("Enter the edges")14 for i := 0; i < num; i++ {15 fmt.Scanln(&str)16 fmt.Scanln(&str)17 }18 fmt.Println("Enter the number of test cases")19 fmt.Scanln(&num)20 for i := 0; i < num; i++ {21 fmt.Println("Enter the test case")22 fmt.Scanln(&str)23 g.TestCase(str)24 }25}26import (27type edge struct {28}29type graph struct {30}31func (g graph) TestCase(str string) {32 for i := 0; i < len(g.vertices); i++ {33 temp = append(temp, g.vertices[i])34 }35 for i := 0; i < len(g.edges); i++ {36 if str == g.edges[i].v1 {37 temp = append(temp, g.edges[i].v2)38 } else if str == g.edges[i].v2 {39 temp = append(temp, g.edges[i].v1)40 }41 }42 sort.Strings(temp)43 for i := 0; i < len(temp); i++ {44 if temp[i] == str {45 }46 }47 if flag {48 fmt.Println("The vertices are")49 for i := 0; i < len(temp); i++ {50 if i == index {51 }52 fmt.Println(temp[i])53 }54 } else {55 fmt.Println("Invalid vertex")56 }57}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!