endtoend_test.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. package main
  2. import (
  3. "bytes"
  4. "context"
  5. "os"
  6. osexec "os/exec"
  7. "path/filepath"
  8. "slices"
  9. "strings"
  10. "testing"
  11. "github.com/google/go-cmp/cmp"
  12. "github.com/google/go-cmp/cmp/cmpopts"
  13. "github.com/sqlc-dev/sqlc/internal/cmd"
  14. "github.com/sqlc-dev/sqlc/internal/config"
  15. "github.com/sqlc-dev/sqlc/internal/opts"
  16. )
  17. func TestExamples(t *testing.T) {
  18. t.Parallel()
  19. ctx := context.Background()
  20. examples, err := filepath.Abs(filepath.Join("..", "..", "examples"))
  21. if err != nil {
  22. t.Fatal(err)
  23. }
  24. files, err := os.ReadDir(examples)
  25. if err != nil {
  26. t.Fatal(err)
  27. }
  28. for _, replay := range files {
  29. if !replay.IsDir() {
  30. continue
  31. }
  32. tc := replay.Name()
  33. t.Run(tc, func(t *testing.T) {
  34. t.Parallel()
  35. path := filepath.Join(examples, tc)
  36. var stderr bytes.Buffer
  37. opts := &cmd.Options{
  38. Env: cmd.Env{},
  39. Stderr: &stderr,
  40. }
  41. output, err := cmd.Generate(ctx, path, "", opts)
  42. if err != nil {
  43. t.Fatalf("sqlc generate failed: %s", stderr.String())
  44. }
  45. cmpDirectory(t, path, output)
  46. })
  47. }
  48. }
  49. func BenchmarkExamples(b *testing.B) {
  50. ctx := context.Background()
  51. examples, err := filepath.Abs(filepath.Join("..", "..", "examples"))
  52. if err != nil {
  53. b.Fatal(err)
  54. }
  55. files, err := os.ReadDir(examples)
  56. if err != nil {
  57. b.Fatal(err)
  58. }
  59. for _, replay := range files {
  60. if !replay.IsDir() {
  61. continue
  62. }
  63. tc := replay.Name()
  64. b.Run(tc, func(b *testing.B) {
  65. path := filepath.Join(examples, tc)
  66. for i := 0; i < b.N; i++ {
  67. var stderr bytes.Buffer
  68. opts := &cmd.Options{
  69. Env: cmd.Env{},
  70. Stderr: &stderr,
  71. }
  72. cmd.Generate(ctx, path, "", opts)
  73. }
  74. })
  75. }
  76. }
  77. type textContext struct {
  78. Mutate func(*config.Config)
  79. Enabled func() bool
  80. }
  81. func TestReplay(t *testing.T) {
  82. // Ensure that this environment variable is always set to true when running
  83. // end-to-end tests
  84. os.Setenv("SQLC_DUMMY_VALUE", "true")
  85. // t.Parallel()
  86. ctx := context.Background()
  87. contexts := map[string]textContext{
  88. "base": {
  89. Mutate: func(c *config.Config) {},
  90. Enabled: func() bool { return true },
  91. },
  92. "managed-db": {
  93. Mutate: func(c *config.Config) {
  94. c.Cloud.Project = "01HAQMMECEYQYKFJN8MP16QC41" // TODO: Read from environment
  95. for i := range c.SQL {
  96. c.SQL[i].Database = &config.Database{
  97. Managed: true,
  98. }
  99. }
  100. },
  101. Enabled: func() bool {
  102. return len(os.Getenv("SQLC_AUTH_TOKEN")) > 0
  103. },
  104. },
  105. }
  106. for name, testctx := range contexts {
  107. name := name
  108. testctx := testctx
  109. if !testctx.Enabled() {
  110. continue
  111. }
  112. for _, replay := range FindTests(t, "testdata", name) {
  113. tc := replay
  114. t.Run(filepath.Join(name, tc.Name), func(t *testing.T) {
  115. t.Parallel()
  116. var stderr bytes.Buffer
  117. var output map[string]string
  118. var err error
  119. path, _ := filepath.Abs(tc.Path)
  120. args := tc.Exec
  121. if args == nil {
  122. args = &Exec{Command: "generate"}
  123. }
  124. expected := string(tc.Stderr)
  125. if args.Process != "" {
  126. _, err := osexec.LookPath(args.Process)
  127. if err != nil {
  128. t.Skipf("executable not found: %s %s", args.Process, err)
  129. }
  130. }
  131. if len(args.Contexts) > 0 {
  132. if !slices.Contains(args.Contexts, name) {
  133. t.Skipf("unsupported context: %s", name)
  134. }
  135. }
  136. opts := cmd.Options{
  137. Env: cmd.Env{
  138. Debug: opts.DebugFromString(args.Env["SQLCDEBUG"]),
  139. NoRemote: true,
  140. },
  141. Stderr: &stderr,
  142. MutateConfig: testctx.Mutate,
  143. }
  144. switch args.Command {
  145. case "diff":
  146. err = cmd.Diff(ctx, path, "", &opts)
  147. case "generate":
  148. output, err = cmd.Generate(ctx, path, "", &opts)
  149. if err == nil {
  150. cmpDirectory(t, path, output)
  151. }
  152. case "vet":
  153. err = cmd.Vet(ctx, path, "", &opts)
  154. default:
  155. t.Fatalf("unknown command")
  156. }
  157. if len(expected) == 0 && err != nil {
  158. t.Fatalf("sqlc %s failed: %s", args.Command, stderr.String())
  159. }
  160. diff := cmp.Diff(strings.TrimSpace(expected), strings.TrimSpace(stderr.String()))
  161. if diff != "" {
  162. t.Fatalf("stderr differed (-want +got):\n%s", diff)
  163. }
  164. })
  165. }
  166. }
  167. }
  168. func cmpDirectory(t *testing.T, dir string, actual map[string]string) {
  169. expected := map[string]string{}
  170. var ff = func(path string, file os.FileInfo, err error) error {
  171. if err != nil {
  172. return err
  173. }
  174. if file.IsDir() {
  175. return nil
  176. }
  177. if !strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, ".kt") && !strings.HasSuffix(path, ".py") && !strings.HasSuffix(path, ".json") && !strings.HasSuffix(path, ".txt") {
  178. return nil
  179. }
  180. // TODO: Figure out a better way to ignore certain files
  181. if strings.HasSuffix(path, ".txt") && filepath.Base(path) != "hello.txt" {
  182. return nil
  183. }
  184. if filepath.Base(path) == "sqlc.json" {
  185. return nil
  186. }
  187. if filepath.Base(path) == "exec.json" {
  188. return nil
  189. }
  190. if strings.Contains(path, "/kotlin/build") {
  191. return nil
  192. }
  193. if strings.HasSuffix(path, "_test.go") || strings.Contains(path, "src/test/") {
  194. return nil
  195. }
  196. if strings.Contains(path, "/python/.venv") || strings.Contains(path, "/python/src/tests/") ||
  197. strings.HasSuffix(path, "__init__.py") || strings.Contains(path, "/python/src/dbtest/") ||
  198. strings.Contains(path, "/python/.mypy_cache") {
  199. return nil
  200. }
  201. blob, err := os.ReadFile(path)
  202. if err != nil {
  203. return err
  204. }
  205. expected[path] = string(blob)
  206. return nil
  207. }
  208. if err := filepath.Walk(dir, ff); err != nil {
  209. t.Fatal(err)
  210. }
  211. if !cmp.Equal(expected, actual, cmpopts.EquateEmpty()) {
  212. t.Errorf("%s contents differ", dir)
  213. for name, contents := range expected {
  214. name := name
  215. if actual[name] == "" {
  216. t.Errorf("%s is empty", name)
  217. return
  218. }
  219. if diff := cmp.Diff(contents, actual[name]); diff != "" {
  220. t.Errorf("%s differed (-want +got):\n%s", name, diff)
  221. }
  222. }
  223. }
  224. }
  225. func BenchmarkReplay(b *testing.B) {
  226. ctx := context.Background()
  227. var dirs []string
  228. err := filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
  229. if err != nil {
  230. return err
  231. }
  232. if info.Name() == "sqlc.json" || info.Name() == "sqlc.yaml" || info.Name() == "sqlc.yml" {
  233. dirs = append(dirs, filepath.Dir(path))
  234. return filepath.SkipDir
  235. }
  236. return nil
  237. })
  238. if err != nil {
  239. b.Fatal(err)
  240. }
  241. for _, replay := range dirs {
  242. tc := replay
  243. b.Run(tc, func(b *testing.B) {
  244. path, _ := filepath.Abs(tc)
  245. for i := 0; i < b.N; i++ {
  246. var stderr bytes.Buffer
  247. opts := &cmd.Options{
  248. Env: cmd.Env{},
  249. Stderr: &stderr,
  250. }
  251. cmd.Generate(ctx, path, "", opts)
  252. }
  253. })
  254. }
  255. }