package restart_target_test

import (
	"fmt"
	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"

	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s"

	. "github.com/pingcap/endless/pkg/util"
	"github.com/pingcap/endless/testcase/conprof"
)

var (
	suiteTestCtx core.TestContext
)

func init() {
	suiteTestCtx = AutoConf()
}

func TestConprof(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Conprof Restart Target Suite")
}

var _ = Describe("Conprof Restart Target Test", func() {
	var deployErr error
	var b *conprof.ClusterBundle

	BeforeSuite(func() {
		tiupNode := suiteTestCtx.Resource("tiup").(resource.WorkloadNode)
		if deployErr = conprof.CheckTiUPAvailable(tiupNode); deployErr != nil {
			return
		}

		sysbenchNode := suiteTestCtx.Resource("sysbench").(resource.WorkloadNode)
		b, deployErr = conprof.NewClusterBundle(tiupNode, sysbenchNode, conprof.ClusterConfig{
			Version:        suiteTestCtx.Param("target_version"),
			Name:           "conprof-restart-target",
			PDClientPort:   2379,
			PDPeerPort:     2380,
			TiDBPort:       4000,
			TiDBStatusPort: 10080,
			TiKVPort:       20160,
			TiKVStatusPort: 20180,
			PrometheusPort: 9090,
			NgmPort:        12020,
		})
	})

	AfterSuite(func() {
		if b != nil {
			b.Close()
			b = nil
		}
	})

	TestCase("Conprof Restart Target Test", "TEST_RESTART_TARGET", func() {
		if deployErr != nil {
			Skip(fmt.Sprintf("tiup is unavailable: %s", deployErr.Error()))
		}
		Expect(b.TurnOffTopSQL()).Should(Succeed())

		Expect(b.ScaleOut("tidb", 4100, 10180))
		Expect(b.ScaleOut("tikv", 20260, 20280))

		By(`
1. Turn on Conprof, start ngm, run sysbench
	* Targets begin to profile
	* Data are available`)
		// =======================================
		// Turn on Conprof, start ngm, run sysbench
		// =======================================
		Expect(b.TurnOnConprof(b.NgmURL(12020))).Should(Succeed())
		Expect(b.PrepareSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4100)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4100, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20260, StatusPort: 20280},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
		2. Stop TiDB:4100, restart after a while
			* Data are available for all targets except TiDB:4100 during downtime
			* Data are available for all targets after TiDB:4100 restarted`)
		// ==============
		// Stop TiDB:4100
		// ==============
		b.StopSysbench(4100)
		Expect(b.StopComponent("tidb", 4100)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20260, StatusPort: 20280},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		// =====================
		// restart after a while
		// =====================
		Expect(b.RestartComponent("tidb", 4100)).Should(Succeed())
		Expect(b.StartSysbench(4100)).Should(Succeed())

		// ------------------------------------------------------------
		// Data are available for all targets after TiDB:4100 restarted
		// ------------------------------------------------------------
		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4100, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20260, StatusPort: 20280},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		By(`
		3. Stop TiKV:20260, restart after a while
			* Data are available for all targets except TiKV:20260 during downtime
			* Data are available for all targets after TiKV:20260 restarted`)
		// ======================================
		// Stop TiKV:20260, restart after a while
		// ======================================
		Expect(b.StopComponent("tikv", 20260)).Should(Succeed())

		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4100, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20260, StatusPort: 20280},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())

		// =====================
		// Restart after a while
		// =====================
		Expect(b.RestartComponent("tikv", 20260)).Should(Succeed())
		b.StopAllSysbench()
		Expect(b.DestroySysbench()).Should(Succeed())
		Expect(b.PrepareSysbench(4000)).Should(Succeed())
		Expect(b.StartSysbench(4000))
		Expect(b.StartSysbench(4100))

		// -------------------------------------------------------------
		// Data are available for all targets after TiKV:20260 restarted
		// -------------------------------------------------------------
		Eventually(func(g Gomega) {
			// ---------------------
			// * Targets are profiling
			// * Data are available
			// ---------------------
			ngmURL := b.NgmURL(12020)
			b.ExpectConprofIsRunning(g, ngmURL)
			b.ExpectComponentsProfileData(g, ngmURL, []conprof.Component{
				{Name: "tidb", IP: "tidb-peer", Port: 4000, StatusPort: 10080},
				{Name: "tidb", IP: "tidb-peer", Port: 4100, StatusPort: 10080},
				{Name: "tikv", IP: "tikv-peer", Port: 20160, StatusPort: 20180},
				{Name: "tikv", IP: "tikv-peer", Port: 20260, StatusPort: 20280},
				{Name: "pd", IP: "pd-peer", Port: 2379, StatusPort: 2380},
			})
		}, "5m", "10s").Should(Succeed())
	})
})
