github.com/kiali/kiali@v1.84.0/graph/telemetry/istio/appender/aggregate_node_test.go (about)

     1  package appender
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/prometheus/common/model"
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	"github.com/kiali/kiali/config"
    11  	"github.com/kiali/kiali/graph"
    12  )
    13  
    14  func TestNamespacesGraphWithServiceInjection(t *testing.T) {
    15  	assert := assert.New(t)
    16  
    17  	q0 := `round(sum(rate(istio_requests_total{reporter="destination",source_workload_namespace!="bookinfo",destination_service_namespace="bookinfo",request_operation!="unknown"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
    18  	v0 := model.Vector{}
    19  
    20  	q1 := `round(sum(rate(istio_requests_total{reporter="destination",source_workload_namespace="bookinfo",request_operation!="unknown"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
    21  	q1m0 := model.Metric{
    22  		"source_cluster":                 config.DefaultClusterID,
    23  		"source_workload_namespace":      "bookinfo",
    24  		"source_workload":                "productpage-v1",
    25  		"source_canonical_service":       "productpage",
    26  		"source_canonical_revision":      "v1",
    27  		"destination_cluster":            config.DefaultClusterID,
    28  		"destination_service_namespace":  "bookinfo",
    29  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
    30  		"destination_service_name":       "reviews",
    31  		"destination_workload_namespace": "bookinfo",
    32  		"destination_workload":           "reviews-v1",
    33  		"destination_canonical_service":  "reviews",
    34  		"destination_canonical_revision": "v1",
    35  		"response_code":                  "200",
    36  		"response_flags":                 "",
    37  		"request_protocol":               "http",
    38  		"request_operation":              "Top"}
    39  	q1m1 := model.Metric{
    40  		"source_cluster":                 config.DefaultClusterID,
    41  		"source_workload_namespace":      "bookinfo",
    42  		"source_workload":                "productpage-v1",
    43  		"source_canonical_service":       "productpage",
    44  		"source_canonical_revision":      "v1",
    45  		"destination_cluster":            config.DefaultClusterID,
    46  		"destination_service_namespace":  "bookinfo",
    47  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
    48  		"destination_service_name":       "reviews",
    49  		"destination_workload_namespace": "bookinfo",
    50  		"destination_workload":           "reviews-v1",
    51  		"destination_canonical_service":  "reviews",
    52  		"destination_canonical_revision": "v1",
    53  		"response_code":                  "200",
    54  		"response_flags":                 "",
    55  		"request_protocol":               "http",
    56  		"request_operation":              "All"}
    57  	v1 := model.Vector{
    58  		&model.Sample{
    59  			Metric: q1m0,
    60  			Value:  70},
    61  		&model.Sample{
    62  			Metric: q1m1,
    63  			Value:  30}}
    64  
    65  	client, api, err := setupMocked()
    66  	if err != nil {
    67  		t.Error(err)
    68  		return
    69  	}
    70  	mockQuery(api, q0, &v0)
    71  	mockQuery(api, q1, &v1)
    72  
    73  	trafficMap := aggregateNodeTestTraffic(true)
    74  	ppID, _, _ := graph.Id(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
    75  	pp, ok := trafficMap[ppID]
    76  	assert.Equal(true, ok)
    77  	assert.Equal(1, len(pp.Edges))
    78  	assert.Equal(graph.NodeTypeService, pp.Edges[0].Dest.NodeType)
    79  
    80  	duration, _ := time.ParseDuration("60s")
    81  	appender := AggregateNodeAppender{
    82  		Aggregate:          "request_operation",
    83  		GraphType:          graph.GraphTypeVersionedApp,
    84  		InjectServiceNodes: true,
    85  		Namespaces: map[string]graph.NamespaceInfo{
    86  			"bookinfo": {
    87  				Name:     "bookinfo",
    88  				Duration: duration,
    89  			},
    90  		},
    91  		QueryTime: time.Now().Unix(),
    92  		Rates: graph.RequestedRates{
    93  			Grpc: graph.RateRequests,
    94  			Http: graph.RateRequests,
    95  			Tcp:  graph.RateTotal,
    96  		},
    97  	}
    98  
    99  	appender.appendGraph(trafficMap, "bookinfo", client)
   100  
   101  	pp, ok = trafficMap[ppID]
   102  	assert.Equal(true, ok)
   103  	assert.Equal(2, len(pp.Edges))
   104  	assert.Equal(graph.NodeTypeAggregate, pp.Edges[0].Dest.NodeType)
   105  	assert.Equal(graph.NodeTypeAggregate, pp.Edges[1].Dest.NodeType)
   106  
   107  	topReviews := pp.Edges[0].Dest
   108  	if "Top" != topReviews.Metadata[graph.AggregateValue] {
   109  		topReviews = pp.Edges[1].Dest
   110  	}
   111  	assert.Equal("request_operation", topReviews.Metadata[graph.Aggregate])
   112  	assert.Equal("Top", topReviews.Metadata[graph.AggregateValue])
   113  	assert.Equal("reviews", topReviews.App)
   114  	assert.Equal("reviews", topReviews.Service)
   115  	assert.Equal(1, len(topReviews.Edges))
   116  	assert.Equal(graph.NodeTypeService, topReviews.Edges[0].Dest.NodeType)
   117  
   118  	allReviews := pp.Edges[1].Dest
   119  	if "All" != allReviews.Metadata[graph.AggregateValue] {
   120  		allReviews = pp.Edges[0].Dest
   121  	}
   122  	assert.Equal("request_operation", allReviews.Metadata[graph.Aggregate])
   123  	assert.Equal("All", allReviews.Metadata[graph.AggregateValue])
   124  	assert.Equal("reviews", allReviews.App)
   125  	assert.Equal("reviews", allReviews.Service)
   126  	assert.Equal(1, len(allReviews.Edges))
   127  	assert.Equal(graph.NodeTypeService, allReviews.Edges[0].Dest.NodeType)
   128  
   129  	assert.Equal(topReviews.Edges[0].Dest.ID, allReviews.Edges[0].Dest.ID)
   130  
   131  	reviewsService := topReviews.Edges[0].Dest
   132  	assert.Equal(graph.NodeTypeService, reviewsService.NodeType)
   133  	assert.Equal("reviews", reviewsService.Service)
   134  	assert.Equal(1, len(reviewsService.Edges))
   135  
   136  	reviews := reviewsService.Edges[0].Dest
   137  	assert.Equal("reviews", reviews.App)
   138  	assert.Equal("v1", reviews.Version)
   139  }
   140  
   141  func TestNamespacesGraphNoServiceInjection(t *testing.T) {
   142  	assert := assert.New(t)
   143  
   144  	q0 := `round(sum(rate(istio_requests_total{reporter="destination",source_workload_namespace!="bookinfo",destination_service_namespace="bookinfo",request_operation!="unknown"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   145  	v0 := model.Vector{}
   146  
   147  	q1 := `round(sum(rate(istio_requests_total{reporter="destination",source_workload_namespace="bookinfo",request_operation!="unknown"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   148  	q1m0 := model.Metric{
   149  		"source_cluster":                 config.DefaultClusterID,
   150  		"source_workload_namespace":      "bookinfo",
   151  		"source_workload":                "productpage-v1",
   152  		"source_canonical_service":       "productpage",
   153  		"source_canonical_revision":      "v1",
   154  		"destination_cluster":            config.DefaultClusterID,
   155  		"destination_service_namespace":  "bookinfo",
   156  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   157  		"destination_service_name":       "reviews",
   158  		"destination_workload_namespace": "bookinfo",
   159  		"destination_workload":           "reviews-v1",
   160  		"destination_canonical_service":  "reviews",
   161  		"destination_canonical_revision": "v1",
   162  		"response_code":                  "200",
   163  		"response_flags":                 "",
   164  		"request_protocol":               "http",
   165  		"request_operation":              "Top"}
   166  	q1m1 := model.Metric{
   167  		"source_cluster":                 config.DefaultClusterID,
   168  		"source_workload_namespace":      "bookinfo",
   169  		"source_workload":                "productpage-v1",
   170  		"source_canonical_service":       "productpage",
   171  		"source_canonical_revision":      "v1",
   172  		"destination_cluster":            config.DefaultClusterID,
   173  		"destination_service_namespace":  "bookinfo",
   174  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   175  		"destination_service_name":       "reviews",
   176  		"destination_workload_namespace": "bookinfo",
   177  		"destination_workload":           "reviews-v1",
   178  		"destination_canonical_service":  "reviews",
   179  		"destination_canonical_revision": "v1",
   180  		"response_code":                  "200",
   181  		"response_flags":                 "",
   182  		"request_protocol":               "http",
   183  		"request_operation":              "All"}
   184  	v1 := model.Vector{
   185  		&model.Sample{
   186  			Metric: q1m0,
   187  			Value:  70},
   188  		&model.Sample{
   189  			Metric: q1m1,
   190  			Value:  30}}
   191  
   192  	client, api, err := setupMocked()
   193  	if err != nil {
   194  		t.Error(err)
   195  		return
   196  	}
   197  	mockQuery(api, q0, &v0)
   198  	mockQuery(api, q1, &v1)
   199  
   200  	trafficMap := aggregateNodeTestTraffic(false)
   201  	ppID, _, _ := graph.Id(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
   202  	pp, ok := trafficMap[ppID]
   203  	assert.Equal(true, ok)
   204  	assert.Equal(1, len(pp.Edges))
   205  	assert.Equal(graph.NodeTypeApp, pp.Edges[0].Dest.NodeType)
   206  
   207  	duration, _ := time.ParseDuration("60s")
   208  	appender := AggregateNodeAppender{
   209  		Aggregate:          "request_operation",
   210  		GraphType:          graph.GraphTypeVersionedApp,
   211  		InjectServiceNodes: false,
   212  		Namespaces: map[string]graph.NamespaceInfo{
   213  			"bookinfo": {
   214  				Name:     "bookinfo",
   215  				Duration: duration,
   216  			},
   217  		},
   218  		QueryTime: time.Now().Unix(),
   219  		Rates: graph.RequestedRates{
   220  			Grpc: graph.RateRequests,
   221  			Http: graph.RateRequests,
   222  			Tcp:  graph.RateTotal,
   223  		}}
   224  
   225  	appender.appendGraph(trafficMap, "bookinfo", client)
   226  
   227  	pp, ok = trafficMap[ppID]
   228  	assert.Equal(true, ok)
   229  	assert.Equal(2, len(pp.Edges))
   230  	assert.Equal(graph.NodeTypeAggregate, pp.Edges[0].Dest.NodeType)
   231  	assert.Equal(graph.NodeTypeAggregate, pp.Edges[1].Dest.NodeType)
   232  
   233  	topReviews := pp.Edges[0].Dest
   234  	if "Top" != topReviews.Metadata[graph.AggregateValue] {
   235  		topReviews = pp.Edges[1].Dest
   236  	}
   237  	assert.Equal("request_operation", topReviews.Metadata[graph.Aggregate])
   238  	assert.Equal("Top", topReviews.Metadata[graph.AggregateValue])
   239  	assert.Equal("", topReviews.App)
   240  	assert.Equal(1, len(topReviews.Edges))
   241  	assert.Equal(graph.NodeTypeApp, topReviews.Edges[0].Dest.NodeType)
   242  
   243  	allReviews := pp.Edges[1].Dest
   244  	if "All" != allReviews.Metadata[graph.AggregateValue] {
   245  		allReviews = pp.Edges[0].Dest
   246  	}
   247  	assert.Equal("request_operation", allReviews.Metadata[graph.Aggregate])
   248  	assert.Equal("All", allReviews.Metadata[graph.AggregateValue])
   249  	assert.Equal("", allReviews.App)
   250  	assert.Equal(1, len(allReviews.Edges))
   251  	assert.Equal(graph.NodeTypeApp, allReviews.Edges[0].Dest.NodeType)
   252  
   253  	assert.Equal(topReviews.Edges[0].Dest.ID, allReviews.Edges[0].Dest.ID)
   254  
   255  	reviews := topReviews.Edges[0].Dest
   256  	assert.Equal(graph.NodeTypeApp, reviews.NodeType)
   257  	assert.Equal("reviews", reviews.App)
   258  	assert.Equal(0, len(reviews.Edges))
   259  }
   260  
   261  func TestNodeGraphWithServiceInjection(t *testing.T) {
   262  	assert := assert.New(t)
   263  
   264  	q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_service_namespace="bookinfo",request_operation="Top",destination_service_name="reviews"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   265  	q0m0 := model.Metric{
   266  		"source_cluster":                 config.DefaultClusterID,
   267  		"source_workload_namespace":      "bookinfo",
   268  		"source_workload":                "productpage-v1",
   269  		"source_canonical_service":       "productpage",
   270  		"source_canonical_revision":      "v1",
   271  		"destination_cluster":            config.DefaultClusterID,
   272  		"destination_service_namespace":  "bookinfo",
   273  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   274  		"destination_service_name":       "reviews",
   275  		"destination_workload_namespace": "bookinfo",
   276  		"destination_workload":           "reviews-v1",
   277  		"destination_canonical_service":  "reviews",
   278  		"destination_canonical_revision": "v1",
   279  		"response_code":                  "200",
   280  		"response_flags":                 "",
   281  		"request_protocol":               "http",
   282  		"request_operation":              "Top"}
   283  	v0 := model.Vector{
   284  		&model.Sample{
   285  			Metric: q0m0,
   286  			Value:  70}}
   287  
   288  	client, api, err := setupMocked()
   289  	if err != nil {
   290  		t.Error(err)
   291  		return
   292  	}
   293  	mockQuery(api, q0, &v0)
   294  
   295  	trafficMap := aggregateNodeTestTraffic(true)
   296  	ppID, _, _ := graph.Id(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
   297  	pp, ok := trafficMap[ppID]
   298  	assert.Equal(true, ok)
   299  	assert.Equal(1, len(pp.Edges))
   300  	assert.Equal(graph.NodeTypeService, pp.Edges[0].Dest.NodeType)
   301  
   302  	duration, _ := time.ParseDuration("60s")
   303  	appender := AggregateNodeAppender{
   304  		Aggregate:          "request_operation",
   305  		AggregateValue:     "Top",
   306  		GraphType:          graph.GraphTypeVersionedApp,
   307  		InjectServiceNodes: true,
   308  		Namespaces: map[string]graph.NamespaceInfo{
   309  			"bookinfo": {
   310  				Name:     "bookinfo",
   311  				Duration: duration,
   312  			},
   313  		},
   314  		QueryTime: time.Now().Unix(),
   315  		Rates: graph.RequestedRates{
   316  			Grpc: graph.RateRequests,
   317  			Http: graph.RateRequests,
   318  			Tcp:  graph.RateTotal,
   319  		},
   320  		Service: "reviews",
   321  	}
   322  
   323  	appender.appendNodeGraph(trafficMap, "bookinfo", client)
   324  
   325  	pp, ok = trafficMap[ppID]
   326  	assert.Equal(true, ok)
   327  	assert.Equal(1, len(pp.Edges))
   328  	assert.Equal(graph.NodeTypeAggregate, pp.Edges[0].Dest.NodeType)
   329  
   330  	topReviews := pp.Edges[0].Dest
   331  	assert.Equal("Top", topReviews.Metadata[graph.AggregateValue])
   332  	assert.Equal("request_operation", topReviews.Metadata[graph.Aggregate])
   333  	assert.Equal("Top", topReviews.Metadata[graph.AggregateValue])
   334  	assert.Equal("reviews", topReviews.App)
   335  	assert.Equal(1, len(topReviews.Edges))
   336  	assert.Equal(graph.NodeTypeService, topReviews.Edges[0].Dest.NodeType)
   337  
   338  	reviewsService := topReviews.Edges[0].Dest
   339  	assert.Equal(graph.NodeTypeService, reviewsService.NodeType)
   340  	assert.Equal("reviews", reviewsService.Service)
   341  	assert.Equal(1, len(reviewsService.Edges))
   342  
   343  	reviews := reviewsService.Edges[0].Dest
   344  	assert.Equal("reviews", reviews.App)
   345  	assert.Equal("v1", reviews.Version)
   346  }
   347  
   348  func TestNamespacesGraphWithServiceInjectionSkipRates(t *testing.T) {
   349  	assert := assert.New(t)
   350  
   351  	q0 := `round(sum(rate(istio_requests_total{reporter="destination",source_workload_namespace!="bookinfo",destination_service_namespace="bookinfo",request_operation!="unknown"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   352  	v0 := model.Vector{}
   353  
   354  	q1 := `round(sum(rate(istio_requests_total{reporter="destination",source_workload_namespace="bookinfo",request_operation!="unknown"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   355  	q1m0 := model.Metric{
   356  		"source_cluster":                 config.DefaultClusterID,
   357  		"source_workload_namespace":      "bookinfo",
   358  		"source_workload":                "productpage-v1",
   359  		"source_canonical_service":       "productpage",
   360  		"source_canonical_revision":      "v1",
   361  		"destination_cluster":            config.DefaultClusterID,
   362  		"destination_service_namespace":  "bookinfo",
   363  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   364  		"destination_service_name":       "reviews",
   365  		"destination_workload_namespace": "bookinfo",
   366  		"destination_workload":           "reviews-v1",
   367  		"destination_canonical_service":  "reviews",
   368  		"destination_canonical_revision": "v1",
   369  		"response_code":                  "200",
   370  		"response_flags":                 "",
   371  		"request_protocol":               "http",
   372  		"request_operation":              "Top"}
   373  	q1m1 := model.Metric{
   374  		"source_cluster":                 config.DefaultClusterID,
   375  		"source_workload_namespace":      "bookinfo",
   376  		"source_workload":                "productpage-v1",
   377  		"source_canonical_service":       "productpage",
   378  		"source_canonical_revision":      "v1",
   379  		"destination_cluster":            config.DefaultClusterID,
   380  		"destination_service_namespace":  "bookinfo",
   381  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   382  		"destination_service_name":       "reviews",
   383  		"destination_workload_namespace": "bookinfo",
   384  		"destination_workload":           "reviews-v1",
   385  		"destination_canonical_service":  "reviews",
   386  		"destination_canonical_revision": "v1",
   387  		"response_code":                  "200",
   388  		"response_flags":                 "",
   389  		"request_protocol":               "http",
   390  		"request_operation":              "All"}
   391  	v1 := model.Vector{
   392  		&model.Sample{
   393  			Metric: q1m0,
   394  			Value:  70},
   395  		&model.Sample{
   396  			Metric: q1m1,
   397  			Value:  30}}
   398  
   399  	client, api, err := setupMocked()
   400  	if err != nil {
   401  		t.Error(err)
   402  		return
   403  	}
   404  	mockQuery(api, q0, &v0)
   405  	mockQuery(api, q1, &v1)
   406  
   407  	trafficMap := aggregateNodeTestTraffic(true)
   408  	ppID, _, _ := graph.Id(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
   409  	pp, ok := trafficMap[ppID]
   410  	assert.Equal(true, ok)
   411  	assert.Equal(1, len(pp.Edges))
   412  	assert.Equal(graph.NodeTypeService, pp.Edges[0].Dest.NodeType)
   413  
   414  	duration, _ := time.ParseDuration("60s")
   415  	appender := AggregateNodeAppender{
   416  		Aggregate:          "request_operation",
   417  		GraphType:          graph.GraphTypeVersionedApp,
   418  		InjectServiceNodes: true,
   419  		Namespaces: map[string]graph.NamespaceInfo{
   420  			"bookinfo": {
   421  				Name:     "bookinfo",
   422  				Duration: duration,
   423  			},
   424  		},
   425  		QueryTime: time.Now().Unix(),
   426  		Rates: graph.RequestedRates{
   427  			Grpc: graph.RateRequests,
   428  			Http: graph.RateNone,
   429  			Tcp:  graph.RateTotal,
   430  		},
   431  	}
   432  
   433  	appender.appendGraph(trafficMap, "bookinfo", client)
   434  
   435  	pp, ok = trafficMap[ppID]
   436  	assert.Equal(true, ok)
   437  	assert.Equal(1, len(pp.Edges))
   438  	assert.Equal(graph.NodeTypeService, pp.Edges[0].Dest.NodeType)
   439  
   440  	reviewsService := pp.Edges[0].Dest
   441  	assert.Equal(graph.NodeTypeService, reviewsService.NodeType)
   442  	assert.Equal("reviews", reviewsService.Service)
   443  	assert.Equal(1, len(reviewsService.Edges))
   444  
   445  	reviews := reviewsService.Edges[0].Dest
   446  	assert.Equal("reviews", reviews.App)
   447  	assert.Equal("v1", reviews.Version)
   448  }
   449  
   450  func TestNodeGraphNoServiceInjection(t *testing.T) {
   451  	assert := assert.New(t)
   452  
   453  	q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_service_namespace="bookinfo",request_operation="Top"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   454  	q0m0 := model.Metric{
   455  		"source_cluster":                 config.DefaultClusterID,
   456  		"source_workload_namespace":      "bookinfo",
   457  		"source_workload":                "productpage-v1",
   458  		"source_canonical_service":       "productpage",
   459  		"source_canonical_revision":      "v1",
   460  		"destination_cluster":            config.DefaultClusterID,
   461  		"destination_service_namespace":  "bookinfo",
   462  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   463  		"destination_service_name":       "reviews",
   464  		"destination_workload_namespace": "bookinfo",
   465  		"destination_workload":           "reviews-v1",
   466  		"destination_canonical_service":  "reviews",
   467  		"destination_canonical_revision": "v1",
   468  		"response_code":                  "200",
   469  		"response_flags":                 "",
   470  		"request_protocol":               "http",
   471  		"request_operation":              "Top"}
   472  	v0 := model.Vector{
   473  		&model.Sample{
   474  			Metric: q0m0,
   475  			Value:  70}}
   476  
   477  	client, api, err := setupMocked()
   478  	if err != nil {
   479  		t.Error(err)
   480  		return
   481  	}
   482  	mockQuery(api, q0, &v0)
   483  
   484  	trafficMap := aggregateNodeTestTraffic(false)
   485  	ppID, _, _ := graph.Id(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
   486  	pp, ok := trafficMap[ppID]
   487  	assert.Equal(true, ok)
   488  	assert.Equal(1, len(pp.Edges))
   489  	assert.Equal(graph.NodeTypeApp, pp.Edges[0].Dest.NodeType)
   490  
   491  	duration, _ := time.ParseDuration("60s")
   492  	appender := AggregateNodeAppender{
   493  		Aggregate:          "request_operation",
   494  		AggregateValue:     "Top",
   495  		GraphType:          graph.GraphTypeVersionedApp,
   496  		InjectServiceNodes: false,
   497  		Namespaces: map[string]graph.NamespaceInfo{
   498  			"bookinfo": {
   499  				Name:     "bookinfo",
   500  				Duration: duration,
   501  			},
   502  		},
   503  		QueryTime: time.Now().Unix(),
   504  		Rates: graph.RequestedRates{
   505  			Grpc: graph.RateRequests,
   506  			Http: graph.RateRequests,
   507  			Tcp:  graph.RateTotal,
   508  		},
   509  	}
   510  
   511  	appender.appendNodeGraph(trafficMap, "bookinfo", client)
   512  
   513  	pp, ok = trafficMap[ppID]
   514  	assert.Equal(true, ok)
   515  	assert.Equal(1, len(pp.Edges))
   516  	assert.Equal(graph.NodeTypeAggregate, pp.Edges[0].Dest.NodeType)
   517  
   518  	topReviews := pp.Edges[0].Dest
   519  	assert.Equal("Top", topReviews.Metadata[graph.AggregateValue])
   520  	assert.Equal("request_operation", topReviews.Metadata[graph.Aggregate])
   521  	assert.Equal("Top", topReviews.Metadata[graph.AggregateValue])
   522  	assert.Equal("", topReviews.App)
   523  	assert.Equal(1, len(topReviews.Edges))
   524  	assert.Equal(graph.NodeTypeApp, topReviews.Edges[0].Dest.NodeType)
   525  
   526  	reviews := topReviews.Edges[0].Dest
   527  	assert.Equal("reviews", reviews.App)
   528  	assert.Equal("v1", reviews.Version)
   529  }
   530  
   531  func TestNodeGraphWithServiceInjectionSkipRates(t *testing.T) {
   532  	assert := assert.New(t)
   533  
   534  	q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_service_namespace="bookinfo",request_operation="Top",destination_service_name="reviews"}[60s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,request_operation) > 0,0.001)`
   535  	q0m0 := model.Metric{
   536  		"source_cluster":                 config.DefaultClusterID,
   537  		"source_workload_namespace":      "bookinfo",
   538  		"source_workload":                "productpage-v1",
   539  		"source_canonical_service":       "productpage",
   540  		"source_canonical_revision":      "v1",
   541  		"destination_cluster":            config.DefaultClusterID,
   542  		"destination_service_namespace":  "bookinfo",
   543  		"destination_service":            "reviews.bookinfo.svc.cluster.local",
   544  		"destination_service_name":       "reviews",
   545  		"destination_workload_namespace": "bookinfo",
   546  		"destination_workload":           "reviews-v1",
   547  		"destination_canonical_service":  "reviews",
   548  		"destination_canonical_revision": "v1",
   549  		"response_code":                  "200",
   550  		"response_flags":                 "",
   551  		"request_protocol":               "http",
   552  		"request_operation":              "Top"}
   553  	v0 := model.Vector{
   554  		&model.Sample{
   555  			Metric: q0m0,
   556  			Value:  70}}
   557  
   558  	client, api, err := setupMocked()
   559  	if err != nil {
   560  		t.Error(err)
   561  		return
   562  	}
   563  	mockQuery(api, q0, &v0)
   564  
   565  	trafficMap := aggregateNodeTestTraffic(true)
   566  	ppID, _, _ := graph.Id(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
   567  	pp, ok := trafficMap[ppID]
   568  	assert.Equal(true, ok)
   569  	assert.Equal(1, len(pp.Edges))
   570  	assert.Equal(graph.NodeTypeService, pp.Edges[0].Dest.NodeType)
   571  
   572  	duration, _ := time.ParseDuration("60s")
   573  	appender := AggregateNodeAppender{
   574  		Aggregate:          "request_operation",
   575  		AggregateValue:     "Top",
   576  		GraphType:          graph.GraphTypeVersionedApp,
   577  		InjectServiceNodes: true,
   578  		Namespaces: map[string]graph.NamespaceInfo{
   579  			"bookinfo": {
   580  				Name:     "bookinfo",
   581  				Duration: duration,
   582  			},
   583  		},
   584  		QueryTime: time.Now().Unix(),
   585  		Rates: graph.RequestedRates{
   586  			Grpc: graph.RateRequests,
   587  			Http: graph.RateNone,
   588  			Tcp:  graph.RateTotal,
   589  		},
   590  		Service: "reviews",
   591  	}
   592  
   593  	appender.appendNodeGraph(trafficMap, "bookinfo", client)
   594  
   595  	pp, ok = trafficMap[ppID]
   596  	assert.Equal(true, ok)
   597  	assert.Equal(1, len(pp.Edges))
   598  	assert.Equal(graph.NodeTypeService, pp.Edges[0].Dest.NodeType)
   599  
   600  	reviewsService := pp.Edges[0].Dest
   601  	assert.Equal(graph.NodeTypeService, reviewsService.NodeType)
   602  	assert.Equal("reviews", reviewsService.Service)
   603  	assert.Equal(1, len(reviewsService.Edges))
   604  
   605  	reviews := reviewsService.Edges[0].Dest
   606  	assert.Equal("reviews", reviews.App)
   607  	assert.Equal("v1", reviews.Version)
   608  }
   609  
   610  func aggregateNodeTestTraffic(injectServices bool) graph.TrafficMap {
   611  	productpage, _ := graph.NewNode(config.DefaultClusterID, "bookinfo", "productpage", "bookinfo", "productpage-v1", "productpage", "v1", graph.GraphTypeVersionedApp)
   612  	reviews, _ := graph.NewNode(config.DefaultClusterID, "bookinfo", "reviews", "bookinfo", "reviews-v1", "reviews", "v1", graph.GraphTypeVersionedApp)
   613  	reviewsService, _ := graph.NewNode(config.DefaultClusterID, "bookinfo", "reviews", "", "", "", "", graph.GraphTypeVersionedApp)
   614  
   615  	trafficMap := graph.NewTrafficMap()
   616  	trafficMap[productpage.ID] = productpage
   617  	trafficMap[reviews.ID] = reviews
   618  	if injectServices {
   619  		trafficMap[reviewsService.ID] = reviewsService
   620  		productpage.AddEdge(reviewsService)
   621  		reviewsService.AddEdge(reviews)
   622  	} else {
   623  		productpage.AddEdge(reviews)
   624  	}
   625  
   626  	return trafficMap
   627  }