package envoy

import (
	"encoding/json"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	expectedSelfAdminCluster = `{
  "name": "self_admin",
  "ignore_health_on_host_removal": false,
  "connect_timeout": "5s",
  "type": "STATIC",
  "http_protocol_options": {},
  "loadAssignment": {
    "clusterName": "self_admin",
    "endpoints": [
      {
        "lbEndpoints": [
          {
            "endpoint": {
              "address": {
                "socket_address": {
                  "address": "127.0.0.1",
                  "port_value": 19000
                }
              }
            }
          }
        ]
      }
    ]
  }
}`
	expectedSelfAdminClusterNonLoopbackIP = `{
  "name": "self_admin",
  "ignore_health_on_host_removal": false,
  "connect_timeout": "5s",
  "type": "STATIC",
  "http_protocol_options": {},
  "loadAssignment": {
    "clusterName": "self_admin",
    "endpoints": [
      {
        "lbEndpoints": [
          {
            "endpoint": {
              "address": {
                "socket_address": {
                  "address": "192.0.2.10",
                  "port_value": 19002
                }
              }
            }
          }
        ]
      }
    ]
  }
}`
	expectedPrometheusBackendCluster = `{
  "name": "prometheus_backend",
  "ignore_health_on_host_removal": false,
  "connect_timeout": "5s",
  "type": "STATIC",
  "http_protocol_options": {},
  "loadAssignment": {
    "clusterName": "prometheus_backend",
    "endpoints": [
      {
        "lbEndpoints": [
          {
            "endpoint": {
              "address": {
                "socket_address": {
                  "address": "127.0.0.1",
                  "port_value": 20100
                }
              }
            }
          }
        ]
      }
    ]
  }
}`
	expectedPromListener = `{
	"name": "envoy_prometheus_metrics_listener",
	"address": {
	  "socket_address": {
		"address": "0.0.0.0",
		"port_value": 9000
	  }
	},
	"filter_chains": [
	  {
		"filters": [
		  {
			"name": "envoy.filters.network.http_connection_manager",
			"typedConfig": {
			  "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
			  "stat_prefix": "envoy_prometheus_metrics",
			  "codec_type": "HTTP1",
			  "route_config": {
				"name": "self_admin_route",
				"virtual_hosts": [
				  {
					"name": "self_admin",
					"domains": [
					  "*"
					],
					"routes": [
					  {
						"match": {
						  "path": "/metrics"
						},
						"route": {
						  "cluster": "self_admin",
						  "prefix_rewrite": "/stats/prometheus"
						}
					  },
					  {
						"match": {
						  "prefix": "/"
						},
						"direct_response": {
						  "status": 404
						}
					  }
					]
				  }
				]
			  },
			  "http_filters": [
				{
				  "name": "envoy.filters.http.router"
				}
			  ]
			}
		  }
		]
	  }
	]
  }`
	expectedPromListenerCustomScrapePath = `{
	"name": "envoy_prometheus_metrics_listener",
	"address": {
	  "socket_address": {
		"address": "0.0.0.0",
		"port_value": 9000
	  }
	},
	"filter_chains": [
	  {
		"filters": [
		  {
			"name": "envoy.filters.network.http_connection_manager",
			"typedConfig": {
			  "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
			  "stat_prefix": "envoy_prometheus_metrics",
			  "codec_type": "HTTP1",
			  "route_config": {
				"name": "self_admin_route",
				"virtual_hosts": [
				  {
					"name": "self_admin",
					"domains": [
					  "*"
					],
					"routes": [
					  {
						"match": {
						  "path": "/scrape-path"
						},
						"route": {
						  "cluster": "self_admin",
						  "prefix_rewrite": "/stats/prometheus"
						}
					  },
					  {
						"match": {
						  "prefix": "/"
						},
						"direct_response": {
						  "status": 404
						}
					  }
					]
				  }
				]
			  },
			  "http_filters": [
				{
				  "name": "envoy.filters.http.router"
				}
			  ]
			}
		  }
		]
	  }
	]
  }`
	expectedPromListenerWithPrometheusBackendCluster = `{
	"name": "envoy_prometheus_metrics_listener",
	"address": {
	  "socket_address": {
		"address": "0.0.0.0",
		"port_value": 9000
	  }
	},
	"filter_chains": [
	  {
		"filters": [
		  {
			"name": "envoy.filters.network.http_connection_manager",
			"typedConfig": {
			  "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
			  "stat_prefix": "envoy_prometheus_metrics",
			  "codec_type": "HTTP1",
			  "route_config": {
				"name": "self_admin_route",
				"virtual_hosts": [
				  {
					"name": "self_admin",
					"domains": [
					  "*"
					],
					"routes": [
					  {
						"match": {
						  "path": "/metrics"
						},
						"route": {
						  "cluster": "prometheus_backend",
						  "prefix_rewrite": "/stats/prometheus"
						}
					  },
					  {
						"match": {
						  "prefix": "/"
						},
						"direct_response": {
						  "status": 404
						}
					  }
					]
				  }
				]
			  },
			  "http_filters": [
				{
				  "name": "envoy.filters.http.router"
				}
			  ]
			}
		  }
		]
	  }
	]
  }`
	expectedStatsListener = `{
  "name": "envoy_metrics_listener",
  "address": {
    "socket_address": {
      "address": "0.0.0.0",
      "port_value": 9000
    }
  },
  "filter_chains": [
    {
      "filters": [
        {
          "name": "envoy.filters.network.http_connection_manager",
          "typedConfig": {
            "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
            "stat_prefix": "envoy_metrics",
            "codec_type": "HTTP1",
            "route_config": {
              "name": "self_admin_route",
              "virtual_hosts": [
                {
                  "name": "self_admin",
                  "domains": [
                    "*"
                  ],
                  "routes": [
                    {
                      "match": {
                        "prefix": "/stats"
                      },
                      "route": {
                        "cluster": "self_admin",
                        "prefix_rewrite": "/stats"
                      }
                    },
                    {
                      "match": {
                        "prefix": "/"
                      },
                      "direct_response": {
                        "status": 404
                      }
                    }
                  ]
                }
              ]
            },
            "http_filters": [
              {
                "name": "envoy.filters.http.router"
              }
            ]
          }
        }
      ]
    }
  ]
}`
	expectedReadyListener = `{
  "name": "envoy_ready_listener",
  "address": {
    "socket_address": {
      "address": "0.0.0.0",
      "port_value": 4444
    }
  },
  "filter_chains": [
    {
      "filters": [
        {
          "name": "envoy.filters.network.http_connection_manager",
          "typedConfig": {
            "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
            "stat_prefix": "envoy_ready",
            "codec_type": "HTTP1",
            "route_config": {
              "name": "self_admin_route",
              "virtual_hosts": [
                {
                  "name": "self_admin",
                  "domains": [
                    "*"
                  ],
                  "routes": [
                    {
                      "match": {
                        "path": "/ready"
                      },
                      "route": {
                        "cluster": "self_admin",
                        "prefix_rewrite": "/ready"
                      }
                    },
                    {
                      "match": {
                        "prefix": "/"
                      },
                      "direct_response": {
                        "status": 404
                      }
                    }
                  ]
                }
              ]
            },
            "http_filters": [
              {
                "name": "envoy.filters.http.router"
              }
            ]
          }
        }
      ]
    }
  ]
}`
)

func TestBootstrapConfig_ConfigureArgs(t *testing.T) {
	defaultTags, err := generateStatsTags(&BootstrapTplArgs{}, nil, false)
	require.NoError(t, err)

	defaultTagsJSON := strings.Join(defaultTags, ",\n")
	defaultStatsConfigJSON := formatStatsTags(defaultTags)

	// The updated tags exclude the ones deprecated in Consul 1.9
	updatedTags, err := generateStatsTags(&BootstrapTplArgs{}, nil, true)
	require.NoError(t, err)

	updatedStatsConfigJSON := formatStatsTags(updatedTags)

	tests := []struct {
		name               string
		input              BootstrapConfig
		env                []string
		baseArgs           BootstrapTplArgs
		wantArgs           BootstrapTplArgs
		omitDeprecatedTags bool
		wantErr            bool
	}{
		{
			name:  "defaults",
			input: BootstrapConfig{},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "extra-stats-sinks",
			input: BootstrapConfig{
				StatsSinksJSON: `{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}]`,
			},
		},
		{
			name: "simple-statsd-sink",
			input: BootstrapConfig{
				StatsdURL: "udp://127.0.0.1:9125",
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink",
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-statsd-sink-plus-extra",
			input: BootstrapConfig{
				StatsdURL: "udp://127.0.0.1:9125",
				StatsSinksJSON: `{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink",
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				},
				{
					"name": "envoy.custom_exciting_sink",
					"config": {
						"foo": "bar"
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-statsd-sink-env",
			input: BootstrapConfig{
				StatsdURL: "$MY_STATSD_URL",
			},
			env: []string{"MY_STATSD_URL=udp://127.0.0.1:9125"},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink",
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-statsd-sink-inline-env-allowed",
			input: BootstrapConfig{
				StatsdURL: "udp://$HOST_IP:9125",
			},
			env: []string{"HOST_IP=127.0.0.1"},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.StatsdSink",
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-statsd-sink-inline-env-disallowed",
			input: BootstrapConfig{
				StatsdURL: "udp://$HOST_ADDRESS:9125",
			},
			env: []string{"HOST_ADDRESS=127.0.0.1"},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
			},
			wantErr: true,
		},
		{
			name: "simple-dogstatsd-sink",
			input: BootstrapConfig{
				DogstatsdURL: "udp://127.0.0.1:9125",
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.dog_statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.DogStatsdSink",
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "simple-dogstatsd-unix-sink",
			input: BootstrapConfig{
				DogstatsdURL: "unix:///var/run/dogstatsd.sock",
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.dog_statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.DogStatsdSink",
						"address": {
							"pipe": {
								"path": "/var/run/dogstatsd.sock"
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},

		{
			name: "simple-dogstatsd-sink-env",
			input: BootstrapConfig{
				DogstatsdURL: "$MY_STATSD_URL",
			},
			env: []string{"MY_STATSD_URL=udp://127.0.0.1:9125"},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: defaultStatsConfigJSON,
				StatsSinksJSON: `[{
					"name": "envoy.stat_sinks.dog_statsd",
					"typedConfig": {
						"@type": "type.googleapis.com/envoy.config.metrics.v3.DogStatsdSink",
						"address": {
							"socket_address": {
								"address": "127.0.0.1",
								"port_value": 9125
							}
						}
					}
				}]`,
			},
			wantErr: false,
		},
		{
			name: "stats-config-override",
			input: BootstrapConfig{
				StatsConfigJSON: `{
					"use_all_default_tags": true
				}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: `{
					"use_all_default_tags": true
				}`,
			},
			wantErr: false,
		},
		{
			name: "simple-tags",
			input: BootstrapConfig{
				StatsTags: []string{"canary", "foo=bar", "baz=2"},
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON: `{
					"stats_tags": [
						{
							"tag_name": "canary",
							"fixed_value": "1"
						},
						{
							"tag_name": "foo",
							"fixed_value": "bar"
						},
						{
							"tag_name": "baz",
							"fixed_value": "2"
						},
						` + defaultTagsJSON + `
					],
					"use_all_default_tags": true
				}`,
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr",
			input: BootstrapConfig{
				PrometheusBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress:     "127.0.0.1",
				AdminBindPort:        "19000",
				PrometheusScrapePath: "/metrics",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON:  expectedPromListener,
				StatsConfigJSON:      defaultStatsConfigJSON,
				PrometheusScrapePath: "/metrics",
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr-non-loopback-ip",
			input: BootstrapConfig{
				PrometheusBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress:     "192.0.2.10",
				AdminBindPort:        "19002",
				PrometheusScrapePath: "/metrics",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "192.0.2.10",
				AdminBindPort:    "19002",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminClusterNonLoopbackIP,
				// Should add a static http listener too
				StaticListenersJSON:  expectedPromListener,
				StatsConfigJSON:      defaultStatsConfigJSON,
				PrometheusScrapePath: "/metrics",
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr-with-overrides",
			input: BootstrapConfig{
				PrometheusBindAddr:  "0.0.0.0:9000",
				StaticClustersJSON:  `{"foo":"bar"}`,
				StaticListenersJSON: `{"baz":"qux"}`,
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress:     "127.0.0.1",
				AdminBindPort:        "19000",
				PrometheusScrapePath: "/scrape-path",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: `{"foo":"bar"},` + expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON:  `{"baz":"qux"},` + expectedPromListenerCustomScrapePath,
				StatsConfigJSON:      defaultStatsConfigJSON,
				PrometheusScrapePath: "/scrape-path",
			},
			wantErr: false,
		},
		{
			name: "prometheus-bind-addr-with-prometheus-backend",
			input: BootstrapConfig{
				PrometheusBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress:      "127.0.0.1",
				AdminBindPort:         "19000",
				PrometheusBackendPort: "20100",
				PrometheusScrapePath:  "/metrics",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should use the "prometheus_backend" cluster instead, which
				// uses the PrometheusBackendPort rather than Envoy admin port
				StaticClustersJSON:    expectedPrometheusBackendCluster,
				StaticListenersJSON:   expectedPromListenerWithPrometheusBackendCluster,
				StatsConfigJSON:       defaultStatsConfigJSON,
				PrometheusBackendPort: "20100",
				PrometheusScrapePath:  "/metrics",
			},
			wantErr: false,
		},
		{
			name: "stats-bind-addr",
			input: BootstrapConfig{
				StatsBindAddr: "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: expectedStatsListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "stats-bind-addr-with-overrides",
			input: BootstrapConfig{
				StatsBindAddr:       "0.0.0.0:9000",
				StaticClustersJSON:  `{"foo":"bar"}`,
				StaticListenersJSON: `{"baz":"qux"}`,
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: `{"foo":"bar"},` + expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: `{"baz":"qux"},` + expectedStatsListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "stats-flush-interval",
			input: BootstrapConfig{
				StatsFlushInterval: `10s`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON:    defaultStatsConfigJSON,
				StatsFlushInterval: `10s`,
			},
			wantErr: false,
		},
		{
			name: "override-tracing",
			input: BootstrapConfig{
				TracingConfigJSON: `{"foo": "bar"}`,
			},
			wantArgs: BootstrapTplArgs{
				StatsConfigJSON:   defaultStatsConfigJSON,
				TracingConfigJSON: `{"foo": "bar"}`,
			},
			wantErr: false,
		},
		{
			name: "err-bad-prometheus-addr",
			input: BootstrapConfig{
				PrometheusBindAddr: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "err-bad-stats-addr",
			input: BootstrapConfig{
				StatsBindAddr: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "err-bad-statsd-addr",
			input: BootstrapConfig{
				StatsdURL: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "err-bad-dogstatsd-addr",
			input: BootstrapConfig{
				DogstatsdURL: "asdasdsad",
			},
			wantErr: true,
		},
		{
			name: "ready-bind-addr",
			input: BootstrapConfig{
				ReadyBindAddr: "0.0.0.0:4444",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: expectedReadyListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "ready-bind-addr-with-overrides",
			input: BootstrapConfig{
				ReadyBindAddr:       "0.0.0.0:4444",
				StaticClustersJSON:  `{"foo":"bar"}`,
				StaticListenersJSON: `{"baz":"qux"}`,
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: `{"foo":"bar"},` + expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: `{"baz":"qux"},` + expectedReadyListener,
				StatsConfigJSON:     defaultStatsConfigJSON,
			},
			wantErr: false,
		},
		{
			name: "ready-bind-addr-and-prometheus-and-stats",
			input: BootstrapConfig{
				ReadyBindAddr:      "0.0.0.0:4444",
				PrometheusBindAddr: "0.0.0.0:9000",
				StatsBindAddr:      "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress:     "127.0.0.1",
				AdminBindPort:        "19000",
				PrometheusScrapePath: "/metrics",
			},
			wantArgs: BootstrapTplArgs{
				AdminBindAddress: "127.0.0.1",
				AdminBindPort:    "19000",
				// Should add a static cluster for the self-proxy to admin
				StaticClustersJSON: expectedSelfAdminCluster,
				// Should add a static http listener too
				StaticListenersJSON: strings.Join(
					[]string{expectedPromListener, expectedStatsListener, expectedReadyListener},
					", ",
				),
				StatsConfigJSON:      defaultStatsConfigJSON,
				PrometheusScrapePath: "/metrics",
			},
			wantErr: false,
		},
		{
			name: "omit-deprecated-tags",
			input: BootstrapConfig{
				ReadyBindAddr:      "0.0.0.0:4444",
				PrometheusBindAddr: "0.0.0.0:9000",
				StatsBindAddr:      "0.0.0.0:9000",
			},
			baseArgs: BootstrapTplArgs{
				AdminBindAddress:     "127.0.0.1",
				AdminBindPort:        "19000",
				PrometheusScrapePath: "/metrics",
			},
			omitDeprecatedTags: true,
			wantArgs: BootstrapTplArgs{
				AdminBindAddress:   "127.0.0.1",
				AdminBindPort:      "19000",
				StaticClustersJSON: expectedSelfAdminCluster,
				StaticListenersJSON: strings.Join(
					[]string{expectedPromListener, expectedStatsListener, expectedReadyListener},
					", ",
				),
				// Should not have default stats config JSON when deprecated tags are omitted
				StatsConfigJSON:      updatedStatsConfigJSON,
				PrometheusScrapePath: "/metrics",
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			args := tt.baseArgs

			defer testSetAndResetEnv(t, tt.env)()

			err := tt.input.ConfigureArgs(&args, tt.omitDeprecatedTags)
			if tt.wantErr {
				require.Error(t, err)
			} else {
				require.NoError(t, err)

				// Want to compare JSON fields with JSONEq
				argV := reflect.ValueOf(args)
				wantV := reflect.ValueOf(tt.wantArgs)
				argT := reflect.TypeOf(args)
				for i := 0; i < argT.NumField(); i++ {
					f := argT.Field(i)
					if strings.HasSuffix(f.Name, "JSON") && wantV.Field(i).String() != "" {
						// Some of our JSON strings are comma separated objects to be
						// insertedinto an array which is not valid JSON on it's own so wrap
						// them all in an array. For simple values this is still valid JSON
						// too.
						want := "[" + wantV.Field(i).String() + "]"
						got := "[" + argV.Field(i).String() + "]"
						require.JSONEq(t, want, got, "field %s should be equivalent JSON", f.Name)
					} else {
						require.Equalf(t, wantV.Field(i).Interface(),
							argV.Field(i).Interface(), "field %s should be equal", f.Name)
					}
				}
			}
		})
	}
}

func TestConsulTagSpecifiers(t *testing.T) {
	// Conveniently both envoy and Go use the re2 dialect of regular
	// expressions, so we can actually test the stats tag extraction regular
	// expressions right here!

	specs, err := resourceTagSpecifiers(false)
	require.NoError(t, err)

	specsNoDeprecated, err := resourceTagSpecifiers(true)
	require.NoError(t, err)

	type testPattern struct {
		name string
		r    *regexp.Regexp
	}

	parseSpecs := func(specs []string) []testPattern {
		var patterns []testPattern
		for _, spec := range specs {
			var m struct {
				TagName string `json:"tag_name"`
				Regex   string `json:"regex"`
			}
			require.NoError(t, json.Unmarshal([]byte(spec), &m))

			patterns = append(patterns, testPattern{
				name: m.TagName,
				r:    regexp.MustCompile(m.Regex),
			})
		}
		return patterns
	}

	var (
		patterns             = parseSpecs(specs)
		patternsNoDeprecated = parseSpecs(specsNoDeprecated)
	)

	type testcase struct {
		name               string
		stat               string
		expect             map[string][]string // this is the m[1:] of the match
		expectNoDeprecated map[string][]string // this is the m[1:] of the match
	}

	cases := []testcase{
		{
			name: "cluster service",
			stat: "cluster.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.datacenter":                 {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2"},
				"consul.destination.trust_domain":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.target":                     {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2"},
				"consul.trust_domain":               {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong.default.dc2"},
				"consul.destination.trust_domain":   {"pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "cluster custom service",
			stat: "cluster.f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.datacenter":                 {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.target":                     {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2"},
				"consul.trust_domain":               {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.target":         {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "cluster service subset",
			stat: "cluster.v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.datacenter":                 {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.target":                     {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2"},
				"consul.trust_domain":               {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", ""},
				"consul.destination.datacenter":     {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "cluster custom service subset",
			stat: "cluster.f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.bind_errors",
			expect: map[string][]string{
				"consul.custom_hash":                {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.datacenter":                 {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.custom_hash":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.full_target":                {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.namespace":                  {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.routing_type":               {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.service":                    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.service_subset":             {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.target":                     {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2"},
				"consul.trust_domain":               {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
			expectNoDeprecated: map[string][]string{
				"consul.destination.custom_hash":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8"},
				"consul.destination.datacenter":     {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "dc2"},
				"consul.destination.full_target":    {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648"},
				"consul.destination.namespace":      {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "default"},
				"consul.destination.routing_type":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "internal"},
				"consul.destination.service":        {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "pong"},
				"consul.destination.service_subset": {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "v2"},
				"consul.destination.target":         {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "f8f8f8f8~v2.pong.default.dc2"},
				"consul.destination.trust_domain":   {"f8f8f8f8~v2.pong.default.dc2.internal.e5b08d03-bfc3-c870-1833-baddb116e648.consul.", "e5b08d03-bfc3-c870-1833-baddb116e648"},
			},
		},
		{
			name: "tcp listener no namespace",
			stat: "tcp.upstream.db.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"db.dc1.", "dc1"},
				"consul.upstream.namespace":  {"db.dc1.", ""},
				"consul.upstream.service":    {"db.dc1.", "db"},
			},
		},
		{
			name: "tcp listener with namespace",
			stat: "tcp.upstream.db.default.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"db.default.dc1.", "dc1"},
				"consul.upstream.namespace":  {"db.default.dc1.", "default"},
				"consul.upstream.service":    {"db.default.dc1.", "db"},
			},
		},
		{
			name: "http listener no namespace",
			stat: "http.upstream.web.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"web.dc1.", "dc1"},
				"consul.upstream.namespace":  {"web.dc1.", ""},
				"consul.upstream.service":    {"web.dc1.", "web"},
			},
		},
		{
			name: "http listener with namespace",
			stat: "http.upstream.web.default.dc1.downstream_cx_total",
			expect: map[string][]string{
				"consul.upstream.datacenter": {"web.default.dc1.", "dc1"},
				"consul.upstream.namespace":  {"web.default.dc1.", "default"},
				"consul.upstream.service":    {"web.default.dc1.", "web"},
			},
		},
	}

	for _, tc := range cases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			var (
				got             = make(map[string][]string)
				gotNoDeprecated = make(map[string][]string)
			)
			for _, p := range patterns {
				m := p.r.FindStringSubmatch(tc.stat)
				if len(m) > 1 {
					m = m[1:]
					got[p.name] = m
				}
			}
			for _, p := range patternsNoDeprecated {
				m := p.r.FindStringSubmatch(tc.stat)
				if len(m) > 1 {
					m = m[1:]
					gotNoDeprecated[p.name] = m
				}
			}

			if tc.expectNoDeprecated == nil {
				tc.expectNoDeprecated = tc.expect
			}

			assert.Equal(t, tc.expect, got)
			assert.Equal(t, tc.expectNoDeprecated, gotNoDeprecated)
		})
	}
}
