Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions internal/enginetest/mysql/coverage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package mysql

import (
"os"
"path/filepath"
"testing"

"github.com/sqlc-dev/sqlc/internal/enginetest/testcases"
)

// TestCoverage verifies that all required test cases are implemented
// for the MySQL engine.
func TestCoverage(t *testing.T) {
engine := Engine()
registry := testcases.DefaultRegistry

// Get all tests this engine should implement
requiredTests := registry.RequiredTestsForEngine(engine)

testdataDir, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}

// Find all implemented tests
implemented := make(map[string]bool)
err = filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Name() == "sqlc.yaml" || info.Name() == "sqlc.json" {
dir := filepath.Dir(path)
testName := filepath.Base(dir)
implemented[testName] = true
return filepath.SkipDir
}
return nil
})
if err != nil && !os.IsNotExist(err) {
t.Fatal(err)
}

// Check for missing tests
var missing []*testcases.TestCase
for _, tc := range requiredTests {
if !implemented[tc.Name] {
missing = append(missing, tc)
}
}

// Report missing tests (informational, not a failure)
if len(missing) > 0 {
t.Logf("MySQL engine is missing %d required test cases (this is informational):", len(missing))
for _, tc := range missing {
t.Logf(" - %s (%s): %s", tc.ID, tc.Name, tc.Description)
}
}

// Report coverage statistics
total := len(requiredTests)
covered := total - len(missing)
percentage := float64(covered) / float64(total) * 100

t.Logf("MySQL test coverage: %d/%d (%.1f%%)", covered, total, percentage)
}

// TestCoverageByCategory reports coverage broken down by category
func TestCoverageByCategory(t *testing.T) {
engine := Engine()
registry := testcases.DefaultRegistry
caps := testcases.DefaultCapabilities(engine)

testdataDir, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}

// Find all implemented tests
implemented := make(map[string]bool)
_ = filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Name() == "sqlc.yaml" || info.Name() == "sqlc.json" {
dir := filepath.Dir(path)
testName := filepath.Base(dir)
implemented[testName] = true
return filepath.SkipDir
}
return nil
})

// Report by category
categories := testcases.RequiredCategories()
if caps.SupportsEnum {
categories = append(categories, testcases.CategoryEnum)
}
if caps.SupportsSchema {
categories = append(categories, testcases.CategorySchema)
}
if caps.SupportsArray {
categories = append(categories, testcases.CategoryArray)
}
if caps.SupportsJSON {
categories = append(categories, testcases.CategoryJSON)
}

for _, cat := range categories {
tests := registry.GetByCategory(cat)
var covered, total int
for _, tc := range tests {
total++
if implemented[tc.Name] {
covered++
}
}
if total > 0 {
percentage := float64(covered) / float64(total) * 100
t.Logf(" %s: %d/%d (%.1f%%)", cat, covered, total, percentage)
}
}
}
174 changes: 174 additions & 0 deletions internal/enginetest/mysql/endtoend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Package mysql contains end-to-end tests for the MySQL engine.
package mysql

import (
"bytes"
"context"
"os"
"path/filepath"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

"github.com/sqlc-dev/sqlc/internal/cmd"
"github.com/sqlc-dev/sqlc/internal/enginetest/testcases"
)

func TestEndToEnd(t *testing.T) {
t.Parallel()
ctx := context.Background()

testdataDir, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}

// Walk through all test directories
err = filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

// Look for sqlc config files
if info.Name() != "sqlc.yaml" && info.Name() != "sqlc.json" {
return nil
}

dir := filepath.Dir(path)
testName := strings.TrimPrefix(dir, testdataDir+string(filepath.Separator))

t.Run(testName, func(t *testing.T) {
t.Parallel()
runTest(ctx, t, dir)
})

return filepath.SkipDir
})

if err != nil {
t.Fatal(err)
}
}

func runTest(ctx context.Context, t *testing.T, dir string) {
t.Helper()

var stderr bytes.Buffer
opts := &cmd.Options{
Env: cmd.Env{},
Stderr: &stderr,
}

// Check for expected stderr
expectedStderr := readExpectedStderr(t, dir)

output, err := cmd.Generate(ctx, dir, "", opts)

// If we expect an error, check stderr matches
if len(expectedStderr) > 0 {
if err == nil {
t.Fatalf("expected error but got none")
}
diff := cmp.Diff(
strings.TrimSpace(expectedStderr),
strings.TrimSpace(stderr.String()),
stderrTransformer(),
)
if diff != "" {
t.Fatalf("stderr differed (-want +got):\n%s", diff)
}
return
}

if err != nil {
t.Fatalf("sqlc generate failed: %s", stderr.String())
}

cmpDirectory(t, dir, output)
}

func readExpectedStderr(t *testing.T, dir string) string {
t.Helper()

paths := []string{
filepath.Join(dir, "stderr.txt"),
}

for _, path := range paths {
if _, err := os.Stat(path); !os.IsNotExist(err) {
blob, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
return string(blob)
}
}
return ""
}

func stderrTransformer() cmp.Option {
return cmp.Transformer("Stderr", func(in string) string {
s := strings.Replace(in, "\r", "", -1)
return strings.Replace(s, "\\", "/", -1)
})
}

func lineEndings() cmp.Option {
return cmp.Transformer("LineEndings", func(in string) string {
return strings.Replace(in, "\r\n", "\n", -1)
})
}

func cmpDirectory(t *testing.T, dir string, actual map[string]string) {
t.Helper()

expected := map[string]string{}
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if !strings.HasSuffix(path, ".go") {
return nil
}
if strings.HasSuffix(path, "_test.go") {
return nil
}
blob, err := os.ReadFile(path)
if err != nil {
return err
}
expected[path] = string(blob)
return nil
})
if err != nil {
t.Fatal(err)
}

opts := []cmp.Option{
cmpopts.EquateEmpty(),
lineEndings(),
}

if !cmp.Equal(expected, actual, opts...) {
t.Errorf("%s contents differ", dir)
for name, contents := range expected {
if actual[name] == "" {
t.Errorf("%s is empty", name)
continue
}
if diff := cmp.Diff(contents, actual[name], opts...); diff != "" {
t.Errorf("%s differed (-want +got):\n%s", name, diff)
}
}
}
}

// Engine returns the engine type for this package
func Engine() testcases.Engine {
return testcases.EngineMySQL
}
31 changes: 31 additions & 0 deletions internal/enginetest/mysql/testdata/join_inner/go/db.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading