// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package consul

import (
	"testing"

	"github.com/hashicorp/consul/agent/structs"
	"github.com/hashicorp/consul/api"
	"github.com/stretchr/testify/require"
)

func TestCatalogOverview(t *testing.T) {
	cases := []struct {
		name     string
		nodes    []*structs.Node
		services []*structs.ServiceNode
		checks   []*structs.HealthCheck
		expected structs.CatalogSummary
	}{
		{
			name:     "empty",
			expected: structs.CatalogSummary{},
		},
		{
			name: "one node with no checks",
			nodes: []*structs.Node{
				{Node: "node1"},
			},
			expected: structs.CatalogSummary{
				Nodes: []structs.HealthSummary{
					{Total: 1, Passing: 1},
				},
			},
		},
		{
			name: "one service with no checks",
			services: []*structs.ServiceNode{
				{Node: "node1", ServiceName: "service1"},
			},
			expected: structs.CatalogSummary{
				Services: []structs.HealthSummary{
					{Name: "service1", Total: 1, Passing: 1},
				},
			},
		},
		{
			name: "three nodes with node checks",
			nodes: []*structs.Node{
				{Node: "node1"},
				{Node: "node2"},
				{Node: "node3"},
			},
			checks: []*structs.HealthCheck{
				{Node: "node1", Name: "check1", CheckID: "check1", Status: api.HealthPassing},
				{Node: "node2", Name: "check1", CheckID: "check1", Status: api.HealthWarning},
				{Node: "node3", Name: "check1", CheckID: "check1", Status: api.HealthCritical},
			},
			expected: structs.CatalogSummary{
				Nodes: []structs.HealthSummary{
					{Total: 3, Passing: 1, Warning: 1, Critical: 1},
				},
				Checks: []structs.HealthSummary{
					{Name: "check1", Total: 3, Passing: 1, Warning: 1, Critical: 1},
				},
			},
		},
		{
			name: "three instances of one service with checks",
			nodes: []*structs.Node{
				{Node: "node1"},
			},
			services: []*structs.ServiceNode{
				{Node: "node1", ServiceName: "service1", ServiceID: "id1"},
				{Node: "node1", ServiceName: "service1", ServiceID: "id2"},
				{Node: "node1", ServiceName: "service1", ServiceID: "id3"},
			},
			checks: []*structs.HealthCheck{
				{Node: "node1", Name: "check1", CheckID: "check1", ServiceID: "id1", Status: api.HealthPassing},
				{Node: "node1", Name: "check1", CheckID: "check2", ServiceID: "id2", Status: api.HealthWarning},
				{Node: "node1", Name: "check1", CheckID: "check3", ServiceID: "id3", Status: api.HealthCritical},
			},
			expected: structs.CatalogSummary{
				Nodes: []structs.HealthSummary{
					{Total: 1, Passing: 1},
				},
				Services: []structs.HealthSummary{
					{Name: "service1", Total: 3, Passing: 1, Warning: 1, Critical: 1},
				},
				Checks: []structs.HealthSummary{
					{Name: "check1", Total: 3, Passing: 1, Warning: 1, Critical: 1},
				},
			},
		},
		{
			name: "three instances of different services with checks",
			nodes: []*structs.Node{
				{Node: "node1"},
			},
			services: []*structs.ServiceNode{
				{Node: "node1", ServiceName: "service1", ServiceID: "id1"},
				{Node: "node1", ServiceName: "service2", ServiceID: "id2"},
				{Node: "node1", ServiceName: "service3", ServiceID: "id3"},
			},
			checks: []*structs.HealthCheck{
				{Node: "node1", Name: "check1", CheckID: "check1", ServiceID: "id1", Status: api.HealthPassing},
				{Node: "node1", Name: "check1", CheckID: "check2", ServiceID: "id2", Status: api.HealthWarning},
				{Node: "node1", Name: "check1", CheckID: "check3", ServiceID: "id3", Status: api.HealthCritical},
			},
			expected: structs.CatalogSummary{
				Nodes: []structs.HealthSummary{
					{Total: 1, Passing: 1},
				},
				Services: []structs.HealthSummary{
					{Name: "service1", Total: 1, Passing: 1},
					{Name: "service2", Total: 1, Warning: 1},
					{Name: "service3", Total: 1, Critical: 1},
				},
				Checks: []structs.HealthSummary{
					{Name: "check1", Total: 3, Passing: 1, Warning: 1, Critical: 1},
				},
			},
		},
		{
			name: "many instances of the same check",
			checks: []*structs.HealthCheck{
				{Name: "check1", CheckID: "check1", Status: api.HealthPassing},
				{Name: "check1", CheckID: "check2", Status: api.HealthWarning},
				{Name: "check1", CheckID: "check3", Status: api.HealthCritical},
				{Name: "check1", CheckID: "check4", Status: api.HealthPassing},
				{Name: "check1", CheckID: "check5", Status: api.HealthCritical},
			},
			expected: structs.CatalogSummary{
				Checks: []structs.HealthSummary{
					{Name: "check1", Total: 5, Passing: 2, Warning: 1, Critical: 2},
				},
			},
		},
		{
			name: "three different checks",
			checks: []*structs.HealthCheck{
				{Name: "check1", CheckID: "check1", Status: api.HealthPassing},
				{Name: "check2", CheckID: "check2", Status: api.HealthWarning},
				{Name: "check3", CheckID: "check3", Status: api.HealthCritical},
			},
			expected: structs.CatalogSummary{
				Checks: []structs.HealthSummary{
					{Name: "check1", Total: 1, Passing: 1},
					{Name: "check2", Total: 1, Warning: 1},
					{Name: "check3", Total: 1, Critical: 1},
				},
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			summary := getCatalogOverview(&structs.CatalogContents{
				Nodes:    tc.nodes,
				Services: tc.services,
				Checks:   tc.checks,
			})
			require.ElementsMatch(t, tc.expected.Nodes, summary.Nodes)
			require.ElementsMatch(t, tc.expected.Services, summary.Services)
			require.ElementsMatch(t, tc.expected.Checks, summary.Checks)
		})
	}
}
