func forceOnlyRulesWarning(opts Options, names ...string) {
	wantNames := make(map[string]bool, len(names))
	for _, name := range names {
		wantNames[name] = true
	}
	for key := range opts.RuleSeverity {
		if wantNames[key] {
			opts.RuleSeverity[key] = SeverityWarning
		} else {
			opts.RuleSeverity[key] = SeverityIgnore
		}
	}
}
forceOnlyRulesWarning sets the specific named linter rule(s) to
SeverityWarning, and sets all other rules to SeverityIgnore.
package linter
import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"testing"
	"github.com/skeema/mybase"
	"github.com/skeema/skeema/internal/fs"
	"github.com/skeema/skeema/internal/util"
	"github.com/skeema/skeema/internal/workspace"
	"github.com/skeema/tengo"
)
func TestMain(m *testing.M) {
	tengo.UseFilteredDriverLogger()
	os.Exit(m.Run())
}
func TestIntegration(t *testing.T) {
	images := tengo.SplitEnv("SKEEMA_TEST_IMAGES")
	if len(images) == 0 {
		fmt.Println("SKEEMA_TEST_IMAGES env var is not set, so integration tests will be skipped!")
		fmt.Println("To run integration tests, you may set SKEEMA_TEST_IMAGES to a comma-separated")
		fmt.Println("list of Docker images. Example:\n# SKEEMA_TEST_IMAGES=\"mysql:5.6,mysql:5.7\" go test")
	}
	manager, err := tengo.NewDockerClient(tengo.DockerClientOptions{})
	if err != nil {
		t.Errorf("Unable to create sandbox manager: %s", err)
	}
	suite := &IntegrationSuite{manager: manager}
	tengo.RunSuite(suite, t, images)
}
type IntegrationSuite struct {
	manager       *tengo.DockerClient
	d             *tengo.DockerizedInstance
	schema        *tengo.Schema
	logicalSchema *fs.LogicalSchema
}
func (s IntegrationSuite) TestCheckSchema(t *testing.T) {
	dir := getDir(t, "testdata/validcfg")
	opts, err := OptionsForDir(dir)
	if err != nil {
		t.Fatalf("Unexpected error from OptionsForDir: %v", err)
	}
	forceRulesWarning(opts)
	opts.Flavor = s.d.Flavor()
	expectFailures := 3
	if opts.Flavor.VendorMinVersion(tengo.VendorMariaDB, 10, 6) {
		expectFailures += 2  
	}
	logicalSchema := dir.LogicalSchemas[0]
	wsOpts, err := workspace.OptionsForDir(dir, s.d.Instance)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.OptionsForDir: %v", err)
	}
	wsSchema, err := workspace.ExecLogicalSchema(logicalSchema, wsOpts)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.ExecLogicalSchema: %v", err)
	} else if len(wsSchema.Failures) != expectFailures {
		for _, err := range wsSchema.Failures {
			t.Errorf(err.Error())
		}
		t.Fatalf("Expected %d creation failures from %s/*.sql, instead found %d", expectFailures, dir, len(wsSchema.Failures))
	}
	result := CheckSchema(wsSchema, opts)
	expected := expectedAnnotations(logicalSchema, s.d.Flavor())
	compareAnnotations(t, expected, result)
}
func (s IntegrationSuite) TestCheckSchemaHidden(t *testing.T) {
	dir := getDir(t, "testdata/hidden")
	opts, err := OptionsForDir(dir)
	if err != nil {
		t.Fatalf("Unexpected error from OptionsForDir: %v", err)
	}
	forceOnlyRulesWarning(opts, "nullable", "ids")
	opts.Flavor = s.d.Flavor()
	logicalSchema := dir.LogicalSchemas[0]
	wsOpts, err := workspace.OptionsForDir(dir, s.d.Instance)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.OptionsForDir: %v", err)
	}
	wsSchema, err := workspace.ExecLogicalSchema(logicalSchema, wsOpts)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.ExecLogicalSchema: %v", err)
	} else if len(wsSchema.Failures) > 0 {
		t.Fatalf("Unexpectedly found %d failing CREATE statements in %s/*.sql", len(wsSchema.Failures), dir)
	}
	result := CheckSchema(wsSchema, opts)
	expected := expectedAnnotations(logicalSchema, s.d.Flavor())
	compareAnnotations(t, expected, result)
}
func (s IntegrationSuite) TestCheckSchemaCompression(t *testing.T) {
	dir := getDir(t, "testdata/validcfg")
	opts, err := OptionsForDir(dir)
	if err != nil {
		t.Fatalf("Unexpected error from OptionsForDir: %v", err)
	}
	logicalSchema := dir.LogicalSchemas[0]
	wsOpts, err := workspace.OptionsForDir(dir, s.d.Instance)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.OptionsForDir: %v", err)
	}
	wsSchema, err := workspace.ExecLogicalSchema(logicalSchema, wsOpts)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.ExecLogicalSchema: %v", err)
	}
	forceOnlyRulesWarning(opts, "compression")
	var innoTableCount int
	for _, tbl := range wsSchema.Tables {
		if tbl.Engine == "InnoDB" {
			innoTableCount++
		}
	}
	if !s.d.Flavor().VendorMinVersion(tengo.VendorMariaDB, 10, 6) {
		cases := []struct {
			allowList            []string
			flavor               tengo.Flavor
			expectedWarningCount int
		}{
			{[]string{"8kb"}, s.d.Flavor(), innoTableCount - 1},
			{[]string{"page", "8kb"}, tengo.FlavorMySQL57, innoTableCount - 1},
			{[]string{"page"}, tengo.FlavorMariaDB103, innoTableCount},
			{[]string{"none"}, s.d.Flavor(), 2},
			{[]string{"none", "4kb"}, s.d.Flavor(), 2},
			{[]string{"none", "4kb", "page"}, s.d.Flavor(), 2},
			{[]string{"none", "invalid-value"}, s.d.Flavor(), 2},
			{[]string{"invalid-value"}, s.d.Flavor(), innoTableCount},
		}
		for n, c := range cases {
			opts.RuleConfig["compression"] = c.allowList
			opts.Flavor = c.flavor
			result := CheckSchema(wsSchema, opts)
			if result.WarningCount != c.expectedWarningCount {
				t.Errorf("cases[%d] expected warning count %d, instead found %d", n, c.expectedWarningCount, result.WarningCount)
			}
		}
	}
	var tableExpectedClause map[string]string
	if s.d.Flavor().MySQLishMinVersion(5, 7) {
		dir = getDir(t, "testdata/pagecomprmysql")
		tableExpectedClause = map[string]string{
			"page_comp_zlib": "COMPRESSION='zlib'",
			"page_comp_lz4":  "COMPRESSION='lz4'",
			"page_comp_none": "",
		}
	} else if s.d.Flavor().VendorMinVersion(tengo.VendorMariaDB, 10, 2) {
		dir = getDir(t, "testdata/pagecomprmaria")
		tableExpectedClause = map[string]string{
			"page_comp_1":   "`PAGE_COMPRESSED`=1",
			"page_comp_on":  "`PAGE_COMPRESSED`='on'",
			"page_comp_0":   "",
			"page_comp_off": "",
		}
	}
	if tableExpectedClause != nil {
		logicalSchema := dir.LogicalSchemas[0]
		wsOpts, err := workspace.OptionsForDir(dir, s.d.Instance)
		if err != nil {
			t.Fatalf("Unexpected error from workspace.OptionsForDir: %v", err)
		}
		wsSchema, err := workspace.ExecLogicalSchema(logicalSchema, wsOpts)
		if err != nil {
			t.Fatalf("Unexpected error from workspace.ExecLogicalSchema: %v", err)
		}
		if len(wsSchema.Failures) > 0 {
			t.Fatalf("%d of the CREATEs in %s unexpectedly failed: %+v", len(wsSchema.Failures), dir, wsSchema.Failures)
		}
		for _, tbl := range wsSchema.Tables {
			expectedClause, ok := tableExpectedClause[tbl.Name]
			if !ok {
				t.Fatalf("Unexpectedly found table %s in dir %s, not present in tableExpectedClause mapping for flavor %s", tbl.Name, dir, s.d.Flavor())
			}
			var expectedMode string
			if expectedClause == "" {
				expectedMode = "none"
			} else {
				expectedMode = "page"
			}
			actualMode, actualClause := tableCompressionMode(tbl)
			if actualMode != expectedMode || actualClause != expectedClause {
				t.Errorf("Unexpected return value from tableCompressionMode(%s): got %q,%q; expected %q,%q", tbl.Name, actualMode, actualClause, expectedMode, expectedClause)
			}
		}
	}
}
func (s IntegrationSuite) TestCheckSchemaUTF8MB3(t *testing.T) {
	dir := getDir(t, "testdata/utf8mb3")
	opts, err := OptionsForDir(dir)
	if err != nil {
		t.Fatalf("Unexpected error from OptionsForDir: %v", err)
	}
	logicalSchema := dir.LogicalSchemas[0]
	wsOpts, err := workspace.OptionsForDir(dir, s.d.Instance)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.OptionsForDir: %v", err)
	}
	wsSchema, err := workspace.ExecLogicalSchema(logicalSchema, wsOpts)
	if err != nil {
		t.Fatalf("Unexpected error from workspace.ExecLogicalSchema: %v", err)
	} else if len(wsSchema.Failures) != 0 {
		t.Fatalf("Unexpectedly found %d workspace failures", len(wsSchema.Failures))
	}
	forceOnlyRulesWarning(opts, "charset")
	opts.Flavor = s.d.Flavor()
	result := CheckSchema(wsSchema, opts)
	expected := expectedAnnotations(logicalSchema, s.d.Flavor())
	compareAnnotations(t, expected, result)
}
func (s *IntegrationSuite) Setup(backend string) (err error) {
	s.d, err = s.manager.GetOrCreateInstance(tengo.DockerizedInstanceOptions{
		Name:              fmt.Sprintf("skeema-test-%s", strings.Replace(backend, ":", "-", -1)),
		Image:             backend,
		RootPassword:      "fakepw",
		DefaultConnParams: "foreign_key_checks=0&sql_mode=%27NO_ENGINE_SUBSTITUTION%27",  
	})
	return err
}
func (s *IntegrationSuite) Teardown(backend string) error {
	return s.d.Stop()
}
func (s *IntegrationSuite) BeforeTest(backend string) error {
	return s.d.NukeData()
}
func getDir(t *testing.T, dirPath string, cliArgs ...string) *fs.Dir {
	t.Helper()
	cmd := mybase.NewCommand("lintertest", "", "", nil)
	util.AddGlobalOptions(cmd)
	workspace.AddCommandOptions(cmd)
	AddCommandOptions(cmd)
	cmd.AddArg("environment", "production", false)
	commandLine := "lintertest"
	if len(cliArgs) > 0 {
		commandLine = fmt.Sprintf("lintertest %s", strings.Join(cliArgs, " "))
	}
	cfg := mybase.ParseFakeCLI(t, cmd, commandLine)
	dir, err := fs.ParseDir(dirPath, cfg)
	if err != nil {
		t.Fatalf("Unexpected error parsing dir %s: %s", dirPath, err)
	}
	return dir
}
func expectedAnnotations(logicalSchema *fs.LogicalSchema, flavor tengo.Flavor) (annotations []*Annotation) {
	re := regexp.MustCompile(`/\*[^*]*annotations:\s*([^*]+)\*/`)
	for _, stmt := range logicalSchema.Creates {
		for offset, line := range strings.Split(stmt.Text, "\n") {
			matches := re.FindStringSubmatch(line)
			if matches == nil {
				continue
			}
			for _, ruleName := range strings.Split(matches[1], ",") {
				ruleName := strings.TrimSpace(ruleName)
				if ruleName == "display-width" && flavor.OmitIntDisplayWidth() {
					continue
				}
				if ruleName == "compression" && flavor.VendorMinVersion(tengo.VendorMariaDB, 10, 6) && strings.Contains(stmt.File, "compressed.sql") {
					continue
				}
				annotations = append(annotations, &Annotation{
					RuleName:  ruleName,
					Statement: stmt,
					Note:      Note{LineOffset: offset},
				})
			}
		}
	}
	return
}
func compareAnnotations(t *testing.T, expected []*Annotation, actualResult *Result) {
	t.Helper()
	if len(expected) != len(actualResult.Annotations) {
		t.Errorf("Expected %d total annotations, instead found %d", len(expected), len(actualResult.Annotations))
	}
	seen := make(map[string]bool)  
	for _, a := range expected {
		key := fmt.Sprintf("%s:%s", a.RuleName, a.Location())
		seen[key] = false
	}
	for _, a := range actualResult.Annotations {
		key := fmt.Sprintf("%s:%s", a.RuleName, a.Location())
		if already, ok := seen[key]; !ok {
			t.Errorf("Found unexpected annotation: %s", key)
		} else if already {
			t.Errorf("Found duplicate annotation: %s", key)
		} else {
			seen[key] = true
		}
	}
	for key, didSee := range seen {
		if !didSee {
			t.Errorf("Expected to find annotation %s, but it was not present in the result", key)
		}
	}
}
func forceRulesWarning(opts Options) {
	for key := range opts.RuleSeverity {
		if rule := rulesByName[key]; !rule.hidden() {
			opts.RuleSeverity[key] = SeverityWarning
		}
	}
}