//go:build unit
// +build unit

/*
Copyright 2023 The Flux authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"testing"
)

func Test_GetCmd(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/objects.yaml", tmpl, t)

	tests := []struct {
		name     string
		args     string
		expected string
	}{
		{
			name:     "no label selector",
			expected: "testdata/get/get.golden",
		},
		{
			name:     "equal label selector",
			args:     "-l sharding.fluxcd.io/key=shard1",
			expected: "testdata/get/get_label_one.golden",
		},
		{
			name:     "notin label selector",
			args:     `-l "sharding.fluxcd.io/key notin (shard1, shard2)"`,
			expected: "testdata/get/get_label_two.golden",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmd := cmdTestCase{
				args:   "get sources git " + tt.args + " -n " + tmpl["fluxns"],
				assert: assertGoldenTemplateFile(tt.expected, nil),
			}

			cmd.runTestCmd(t)
		})
	}
}

func Test_GetCmdStatusSelector(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/status_objects.yaml", tmpl, t)

	tests := []struct {
		name     string
		args     string
		expected string
	}{
		{
			name:     "equal status selector matches one",
			args:     "--status-selector Ready=True",
			expected: "testdata/get/get_status_ready_true.golden",
		},
		{
			name:     "equal status selector matches false",
			args:     "--status-selector Ready=False",
			expected: "testdata/get/get_status_ready_false.golden",
		},
		{
			name:     "not-equal status selector matches all not-true",
			args:     "--status-selector Ready!=True",
			expected: "testdata/get/get_status_ready_not_true.golden",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmd := cmdTestCase{
				args:   "get sources git " + tt.args + " -n " + tmpl["fluxns"],
				assert: assertGoldenTemplateFile(tt.expected, nil),
			}

			cmd.runTestCmd(t)
		})
	}
}

func Test_GetCmdStatusSelectorSyntheticReady(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/notification_objects.yaml", tmpl, t)

	commands := []string{
		"get alerts",
		"get alert-providers",
		"get all",
	}
	for _, command := range commands {
		t.Run(command, func(t *testing.T) {
			unfilteredOutput, err := executeCommand(command + " -n " + tmpl["fluxns"])
			if err != nil {
				t.Fatalf("%s failed: %v", command, err)
			}
			if unfilteredOutput == "" {
				t.Fatalf("expected %s output for namespace with notification objects", command)
			}

			filteredOutput, err := executeCommand(command + " --status-selector Ready=True -n " + tmpl["fluxns"])
			if err != nil {
				t.Fatalf("%s with Ready=True status selector failed: %v", command, err)
			}

			if filteredOutput != unfilteredOutput {
				t.Fatalf("expected Ready=True filtered output to match unfiltered output:\nfiltered:\n%s\nunfiltered:\n%s", filteredOutput, unfilteredOutput)
			}
		})
	}
}

func Test_GetAllCmdStatusSelectorNoMatches(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/status_objects.yaml", tmpl, t)

	cmd := cmdTestCase{
		args:   "get all --status-selector foo=bar -n " + tmpl["fluxns"],
		assert: assertGoldenValue(""),
	}

	cmd.runTestCmd(t)
}

func Test_GetAllCmdStatusSelectorKustomizationOnlyMatches(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/kustomization_only.yaml", tmpl, t)

	unfilteredOutput, err := executeCommand("get all -n " + tmpl["fluxns"])
	if err != nil {
		t.Fatalf("get all failed: %v", err)
	}
	if unfilteredOutput == "" {
		t.Fatal("expected get all output for namespace with one Kustomization")
	}

	filteredOutput, err := executeCommand("get all --status-selector Ready=True -n " + tmpl["fluxns"])
	if err != nil {
		t.Fatalf("get all with matching status selector failed: %v", err)
	}

	if filteredOutput != unfilteredOutput {
		t.Fatalf("expected filtered output to match unfiltered output:\nfiltered:\n%s\nunfiltered:\n%s", filteredOutput, unfilteredOutput)
	}
}

func Test_GetAllCmdStatusSelectorKustomizationOnlyNoMatch(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/kustomization_only.yaml", tmpl, t)

	emptyNamespace := allocateNamespace("empty")
	setupTestNamespace(emptyNamespace, t)

	emptyOutput, err := executeCommand("get all -n " + emptyNamespace)
	if err != nil {
		t.Fatalf("get all in empty namespace failed: %v", err)
	}

	filteredOutput, err := executeCommand("get all --status-selector Ready=False -n " + tmpl["fluxns"])
	if err != nil {
		t.Fatalf("get all with non-matching status selector failed: %v", err)
	}

	if filteredOutput != emptyOutput {
		t.Fatalf("expected filtered output to match empty namespace output:\nfiltered:\n%s\nempty namespace:\n%s", filteredOutput, emptyOutput)
	}
}

func Test_GetCmdErrors(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/objects.yaml", tmpl, t)

	tests := []struct {
		name   string
		args   string
		assert assertFunc
	}{
		{
			name:   "specific object not found",
			args:   "get kustomization non-existent-resource -n " + tmpl["fluxns"],
			assert: assertError(fmt.Sprintf("Kustomization object 'non-existent-resource' not found in \"%s\" namespace", tmpl["fluxns"])),
		},
		{
			name:   "no objects found in namespace",
			args:   "get helmrelease -n " + tmpl["fluxns"],
			assert: assertError(fmt.Sprintf("no HelmRelease objects found in \"%s\" namespace", tmpl["fluxns"])),
		},
		{
			name:   "no artifact generators found in namespace",
			args:   "get artifact generators -n " + tmpl["fluxns"],
			assert: assertError(fmt.Sprintf("no ArtifactGenerator objects found in \"%s\" namespace", tmpl["fluxns"])),
		},
		{
			name:   "malformed status selector",
			args:   "get sources git --status-selector Ready -n " + tmpl["fluxns"],
			assert: assertError("expected status selector in type=status or type!=status format, but found: Ready"),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmd := cmdTestCase{
				args:   tt.args,
				assert: tt.assert,
			}
			cmd.runTestCmd(t)
		})
	}
}

func Test_GetCmdSuccess(t *testing.T) {
	tmpl := map[string]string{
		"fluxns": allocateNamespace("flux-system"),
	}
	testEnv.CreateObjectFile("./testdata/get/objects.yaml", tmpl, t)

	tests := []struct {
		name   string
		args   string
		assert assertFunc
	}{
		{
			name:   "list sources git",
			args:   "get sources git -n " + tmpl["fluxns"],
			assert: assertSuccess(),
		},
		{
			name:   "get help",
			args:   "get --help",
			assert: assertSuccess(),
		},
		{
			name:   "get with all namespaces flag",
			args:   "get sources git -A",
			assert: assertSuccess(),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmd := cmdTestCase{
				args:   tt.args,
				assert: tt.assert,
			}
			cmd.runTestCmd(t)
		})
	}
}
