<commit_before>package cmd

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	"github.com/stormforger/cli/api"
	"github.com/stormforger/cli/api/testrun"
)

var (
	// testRunLaunchCmd represents the test run launch command
	testRunLaunchCmd = &cobra.Command{
		Use:   "launch <test-case-ref>",
		Short: "Create and launch a new test run",
		Long: fmt.Sprintf(`Create and launch a new test run based on given test case

<test-case-ref> can be 'organisation-name/test-case-name' or 'test-case-uid'.

Examples
--------
* Launch by organisation and test case name

  forge test-case launch acme-inc/checkout

* Alternatively the test case UID can also be provided

  forge test-case launch xPSX5KXM


Configuration
-------------
You can specify configuration for a test run that will overwrite what is defined
in your JavaScript definition.

* Available cluster sizings:
  * %s

Available cluster regions are available at https://docs.stormforger.com/reference/test-cluster/#cluster-region
`,
			strings.Join(validSizings, "\n  * ")),
		Run: testRunLaunch,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if len(args) < 1 {
				log.Fatal("Missing argument: test case reference")
			}

			if len(args) > 1 {
				log.Fatal("Too many arguments")
			}

			if testRunLaunchOpts.DumpTraffic && testRunLaunchOpts.CheckNFR != "" {
				log.Fatal("--dump-traffic and --nfr-check-file are mutual exclusive")
			}

			if testRunLaunchOpts.ClusterRegion != "" && !stringInSlice(testRunLaunchOpts.ClusterRegion, validRegions) {
				log.Fatalf("%s is not a valid region", testRunLaunchOpts.ClusterRegion)
			}

			if testRunLaunchOpts.ClusterSizing != "" && !stringInSlice(testRunLaunchOpts.ClusterSizing, validSizings) {
				log.Fatalf("%s is not a valid sizing", testRunLaunchOpts.ClusterSizing)
			}
		},
	}

	testRunLaunchOpts struct {
		OpenInBrowser         bool
		Title                 string
		Notes                 string
		ClusterRegion         string
		ClusterSizing         string
		Watch                 bool
		MaxWatchTime          time.Duration
		CheckNFR              string
		DisableGzip           bool
		SkipWait              bool
		DumpTraffic           bool
		SessionValidationMode bool
		Validate              bool
	}

	validRegions = []string{
		"ap-east-1",
		"ap-northeast-1",
		"ap-northeast-2",
		"ap-south-1",
		"ap-southeast-1",
		"ap-southeast-2",
		"ca-central-1",
		"eu-central-1",
		"eu-north-1",
		"eu-west-1",
		"eu-west-2",
		"eu-west-3",
		"sa-east-1",
		"us-east-1",
		"us-east-2",
		"us-west-1",
		"us-west-2",
	}

	validSizings = []string{
		"preflight",
		"tiny",
		"small",
		"medium",
		"large",
		"xlarge",
		"2xlarge",
	}
)

func init() {
	TestCaseCmd.AddCommand(testRunLaunchCmd)

	testRunLaunchCmd.Flags().BoolVar(&testRunLaunchOpts.OpenInBrowser, "open", false, "Open test run in browser")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Title, "title", "t", "", "Descriptive title of test run")
	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Notes, "notes", "n", "", "Longer description (Markdown supported)")

	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterRegion, "region", "", "Region to start test in")
	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterSizing, "sizing", "", "Cluster sizing to use")

	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Watch, "watch", "w", false, "Automatically watch newly launched test run")
	testRunLaunchCmd.Flags().DurationVar(&testRunLaunchOpts.MaxWatchTime, "watch-timeout", 0, "Maximum duration in seconds to watch")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.CheckNFR, "nfr-check-file", "", "", "Check test result against NFR definition (implies --watch)")

	// options for debugging
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DisableGzip, "disable-gzip", "", false, "Globally disable gzip")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SkipWait, "skip-wait", "", false, "Ignore defined waits")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DumpTraffic, "dump-traffic", "", false, "Create traffic dump")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SessionValidationMode, "session-validation-mode", "", false, "Enable session validation mode")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Validate, "validate", "", false, "Perform validation run")
}

func testRunLaunch(cmd *cobra.Command, args []string) {
	client := NewClient()

	testCaseUID := lookupTestCase(*client, args[0])

	launchOptions := api.TestRunLaunchOptions{
		Title:                 testRunLaunchOpts.Title,
		Notes:                 testRunLaunchOpts.Notes,
		ClusterRegion:         testRunLaunchOpts.ClusterRegion,
		ClusterSizing:         testRunLaunchOpts.ClusterSizing,
		DisableGzip:           testRunLaunchOpts.DisableGzip,
		SkipWait:              testRunLaunchOpts.SkipWait,
		DumpTraffic:           testRunLaunchOpts.DumpTraffic,
		SessionValidationMode: testRunLaunchOpts.SessionValidationMode,
	}

	if testRunLaunchOpts.Validate {
		launchOptions.SessionValidationMode = true
		launchOptions.ClusterSizing = "preflight"
		launchOptions.ClusterRegion = "eu-west-1"
	}

	status, response, err := client.TestRunCreate(testCaseUID, launchOptions)
	if err != nil {
		log.Fatal(err)
	}
<commit_msg>Allow any region for preflight (#89)<commit_after>package cmd

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	"github.com/stormforger/cli/api"
	"github.com/stormforger/cli/api/testrun"
)

var (
	// testRunLaunchCmd represents the test run launch command
	testRunLaunchCmd = &cobra.Command{
		Use:   "launch <test-case-ref>",
		Short: "Create and launch a new test run",
		Long: fmt.Sprintf(`Create and launch a new test run based on given test case

<test-case-ref> can be 'organisation-name/test-case-name' or 'test-case-uid'.

Examples
--------
* Launch by organisation and test case name

  forge test-case launch acme-inc/checkout

* Alternatively the test case UID can also be provided

  forge test-case launch xPSX5KXM


Configuration
-------------
You can specify configuration for a test run that will overwrite what is defined
in your JavaScript definition.

* Available cluster sizings:
  * %s

Available cluster regions are available at https://docs.stormforger.com/reference/test-cluster/#cluster-region
`,
			strings.Join(validSizings, "\n  * ")),
		Run: testRunLaunch,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if len(args) < 1 {
				log.Fatal("Missing argument: test case reference")
			}

			if len(args) > 1 {
				log.Fatal("Too many arguments")
			}

			if testRunLaunchOpts.DumpTraffic && testRunLaunchOpts.CheckNFR != "" {
				log.Fatal("--dump-traffic and --nfr-check-file are mutual exclusive")
			}

			if testRunLaunchOpts.ClusterRegion != "" && !stringInSlice(testRunLaunchOpts.ClusterRegion, validRegions) {
				log.Fatalf("%s is not a valid region", testRunLaunchOpts.ClusterRegion)
			}

			if testRunLaunchOpts.ClusterSizing != "" && !stringInSlice(testRunLaunchOpts.ClusterSizing, validSizings) {
				log.Fatalf("%s is not a valid sizing", testRunLaunchOpts.ClusterSizing)
			}
		},
	}

	testRunLaunchOpts struct {
		OpenInBrowser         bool
		Title                 string
		Notes                 string
		ClusterRegion         string
		ClusterSizing         string
		Watch                 bool
		MaxWatchTime          time.Duration
		CheckNFR              string
		DisableGzip           bool
		SkipWait              bool
		DumpTraffic           bool
		SessionValidationMode bool
		Validate              bool
	}

	validRegions = []string{
		"ap-east-1",
		"ap-northeast-1",
		"ap-northeast-2",
		"ap-south-1",
		"ap-southeast-1",
		"ap-southeast-2",
		"ca-central-1",
		"eu-central-1",
		"eu-north-1",
		"eu-west-1",
		"eu-west-2",
		"eu-west-3",
		"sa-east-1",
		"us-east-1",
		"us-east-2",
		"us-west-1",
		"us-west-2",
	}

	validSizings = []string{
		"preflight",
		"tiny",
		"small",
		"medium",
		"large",
		"xlarge",
		"2xlarge",
	}
)

func init() {
	TestCaseCmd.AddCommand(testRunLaunchCmd)

	testRunLaunchCmd.Flags().BoolVar(&testRunLaunchOpts.OpenInBrowser, "open", false, "Open test run in browser")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Title, "title", "t", "", "Descriptive title of test run")
	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.Notes, "notes", "n", "", "Longer description (Markdown supported)")

	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterRegion, "region", "", "Region to start test in")
	testRunLaunchCmd.Flags().StringVar(&testRunLaunchOpts.ClusterSizing, "sizing", "", "Cluster sizing to use")

	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Watch, "watch", "w", false, "Automatically watch newly launched test run")
	testRunLaunchCmd.Flags().DurationVar(&testRunLaunchOpts.MaxWatchTime, "watch-timeout", 0, "Maximum duration in seconds to watch")

	testRunLaunchCmd.Flags().StringVarP(&testRunLaunchOpts.CheckNFR, "nfr-check-file", "", "", "Check test result against NFR definition (implies --watch)")

	// options for debugging
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DisableGzip, "disable-gzip", "", false, "Globally disable gzip")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SkipWait, "skip-wait", "", false, "Ignore defined waits")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.DumpTraffic, "dump-traffic", "", false, "Create traffic dump")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.SessionValidationMode, "session-validation-mode", "", false, "Enable session validation mode")
	testRunLaunchCmd.Flags().BoolVarP(&testRunLaunchOpts.Validate, "validate", "", false, "Perform validation run")
}

func testRunLaunch(cmd *cobra.Command, args []string) {
	client := NewClient()

	testCaseUID := lookupTestCase(*client, args[0])

	launchOptions := api.TestRunLaunchOptions{
		Title:                 testRunLaunchOpts.Title,
		Notes:                 testRunLaunchOpts.Notes,
		ClusterRegion:         testRunLaunchOpts.ClusterRegion,
		ClusterSizing:         testRunLaunchOpts.ClusterSizing,
		DisableGzip:           testRunLaunchOpts.DisableGzip,
		SkipWait:              testRunLaunchOpts.SkipWait,
		DumpTraffic:           testRunLaunchOpts.DumpTraffic,
		SessionValidationMode: testRunLaunchOpts.SessionValidationMode,
	}

	if testRunLaunchOpts.Validate {
		launchOptions.SessionValidationMode = true
		launchOptions.ClusterSizing = "preflight"
	}

	status, response, err := client.TestRunCreate(testCaseUID, launchOptions)
	if err != nil {
		log.Fatal(err)
	}

{'fraction_non_alphanumeric': 0.12943378568086103, 'fraction_numerical': 0.004960224613944782, 'mean_word_length': 3.8484573502722323, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1166432', 'n_tokens_mistral': 3683, 'n_tokens_neox': 3458, 'n_words': 737}