Skip to content

Commit 046cb81

Browse files
cmoogkyleconroy
authored andcommitted
improves test coverage of mysql (#280)
1 parent fbb5816 commit 046cb81

File tree

4 files changed

+227
-21
lines changed

4 files changed

+227
-21
lines changed

internal/mysql/errors_test.go

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,86 @@ import (
77
"vitess.io/vitess/go/vt/sqlparser"
88
)
99

10-
func TestSyntaxErr(t *testing.T) {
11-
tokenizer := sqlparser.NewStringTokenizer("SELEC T id FROM users;")
12-
expectedLocation := 6
13-
expectedNear := "SELEC"
10+
func TestCustomArgErr(t *testing.T) {
11+
tests := [...]struct {
12+
input string
13+
output sqlparser.PositionedErr
14+
}{
15+
{
16+
input: "/* name: GetUser :one */\nselect id, first_name from users where id = sqlc.argh(target_id)",
17+
output: sqlparser.PositionedErr{
18+
Err: `invalid function call "sqlc.argh", did you mean "sqlc.arg"?`,
19+
Pos: 0,
20+
Near: nil,
21+
},
22+
},
23+
{
24+
input: "/* name: GetUser :one */\nselect id, first_name from users where id = sqlc.arg(sqlc.arg(target_id))",
25+
output: sqlparser.PositionedErr{
26+
Err: `invalid custom argument value "sqlc.arg(sqlc.arg(target_id))"`,
27+
Pos: 0,
28+
Near: nil,
29+
},
30+
},
31+
{
32+
input: "/* name: GetUser :one */\nselect id, first_name from users where id = sqlc.arg(?)",
33+
output: sqlparser.PositionedErr{
34+
Err: `invalid custom argument value "sqlc.arg(?)"`,
35+
Pos: 0,
36+
Near: nil,
37+
},
38+
},
39+
}
40+
41+
for _, tcase := range tests {
42+
q, err := parseContents(mockFileName, tcase.input, mockSchema, mockSettings)
43+
if err == nil && len(q) > 0 {
44+
t.Errorf("parse contents succeeded on an invalid query")
45+
}
46+
if diff := cmp.Diff(tcase.output, err); diff != "" {
47+
t.Errorf(diff)
48+
}
49+
}
50+
}
51+
52+
func TestPositionedErr(t *testing.T) {
53+
tests := [...]struct {
54+
input string
55+
output sqlparser.PositionedErr
56+
}{
57+
{
58+
input: "/* name: GetUser :one */\nselect id, first_name from users from where id = ?",
59+
output: sqlparser.PositionedErr{
60+
Err: `syntax error`,
61+
Pos: 63,
62+
Near: []byte("from"),
63+
},
64+
},
65+
{
66+
input: "/* name: GetUser :one */\nselectt id, first_name from users",
67+
output: sqlparser.PositionedErr{
68+
Err: `syntax error`,
69+
Pos: 33,
70+
Near: []byte("selectt"),
71+
},
72+
},
73+
{
74+
input: "/* name: GetUser :one */\nselect id from users where select id",
75+
output: sqlparser.PositionedErr{
76+
Err: `syntax error`,
77+
Pos: 59,
78+
Near: []byte("select"),
79+
},
80+
},
81+
}
1482

15-
_, parseErr := sqlparser.ParseNextStrictDDL(tokenizer)
16-
if parseErr == nil {
17-
t.Errorf("Tokenizer failed to error on invalid MySQL syntax")
18-
} else if posErr, ok := parseErr.(sqlparser.PositionedErr); ok {
19-
if posErr.Pos != expectedLocation {
20-
t.Errorf(cmp.Diff(posErr.Pos, expectedLocation))
83+
for _, tcase := range tests {
84+
q, err := parseContents(mockFileName, tcase.input, mockSchema, mockSettings)
85+
if err == nil && len(q) > 0 {
86+
t.Errorf("parse contents succeeded on an invalid query")
2187
}
22-
if string(posErr.Near) != expectedNear {
23-
t.Errorf(cmp.Diff(string(posErr.Near), string(expectedNear)))
88+
if diff := cmp.Diff(tcase.output, err); diff != "" {
89+
t.Errorf(diff)
2490
}
25-
} else {
26-
t.Errorf("failed to return sqlparser.PositionedErr error for invalid mysql expression")
2791
}
2892
}

internal/mysql/gen_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package mysql
2+
3+
import (
4+
"testing"
5+
6+
"github.com/google/go-cmp/cmp"
7+
"github.com/kyleconroy/sqlc/internal/dinosql"
8+
"github.com/kyleconroy/sqlc/internal/pg"
9+
"vitess.io/vitess/go/vt/sqlparser"
10+
)
11+
12+
func TestArgName(t *testing.T) {
13+
tcase := [...]struct {
14+
input string
15+
output string
16+
}{
17+
{
18+
input: "get_users",
19+
output: "getUsers",
20+
},
21+
{
22+
input: "get_users_by_id",
23+
output: "getUsersByID",
24+
},
25+
{
26+
input: "get_all_",
27+
output: "getAll",
28+
},
29+
}
30+
31+
for _, tc := range tcase {
32+
name := argName(tc.input)
33+
if diff := cmp.Diff(name, tc.output); diff != "" {
34+
t.Errorf(diff)
35+
}
36+
}
37+
}
38+
func TestEnumName(t *testing.T) {
39+
tcase := [...]struct {
40+
input sqlparser.ColumnDefinition
41+
output string
42+
}{
43+
{
44+
input: sqlparser.ColumnDefinition{Name: sqlparser.NewColIdent("first_name")},
45+
output: "FirstNameType",
46+
},
47+
{
48+
input: sqlparser.ColumnDefinition{Name: sqlparser.NewColIdent("user_id")},
49+
output: "UserIDType",
50+
},
51+
{
52+
input: sqlparser.ColumnDefinition{Name: sqlparser.NewColIdent("last_name")},
53+
output: "LastNameType",
54+
},
55+
}
56+
57+
for _, tc := range tcase {
58+
enumName := enumNameFromColDef(&tc.input, mockSettings)
59+
if diff := cmp.Diff(enumName, tc.output); diff != "" {
60+
t.Errorf(diff)
61+
}
62+
}
63+
}
64+
65+
func TestEnums(t *testing.T) {
66+
tcase := [...]struct {
67+
input Result
68+
output []dinosql.GoEnum
69+
}{
70+
{
71+
input: Result{Schema: mockSchema},
72+
output: []dinosql.GoEnum{
73+
{
74+
Name: "JobStatusType",
75+
Constants: []dinosql.GoConstant{
76+
{Name: "applied", Type: "JobStatusType", Value: "applied"},
77+
{Name: "pending", Type: "JobStatusType", Value: "pending"},
78+
{Name: "accepted", Type: "JobStatusType", Value: "accepted"},
79+
{Name: "rejected", Type: "JobStatusType", Value: "rejected"},
80+
},
81+
},
82+
},
83+
},
84+
}
85+
for _, tc := range tcase {
86+
enums := tc.input.Enums(mockSettings)
87+
if diff := cmp.Diff(enums, tc.output); diff != "" {
88+
t.Errorf(diff)
89+
}
90+
}
91+
}
92+
93+
func TestStructs(t *testing.T) {
94+
tcase := [...]struct {
95+
input Result
96+
output []dinosql.GoStruct
97+
}{
98+
{
99+
input: Result{Schema: mockSchema},
100+
output: []dinosql.GoStruct{
101+
{
102+
Table: pg.FQN{Catalog: "orders"},
103+
Name: "Order",
104+
Fields: []dinosql.GoField{
105+
{Name: "ID", Type: "int", Tags: map[string]string{"json:": "id"}},
106+
{Name: "Price", Type: "float64", Tags: map[string]string{"json:": "price"}},
107+
{Name: "UserID", Type: "int", Tags: map[string]string{"json:": "user_id"}},
108+
},
109+
},
110+
{
111+
Table: pg.FQN{Catalog: "users"},
112+
Name: "User",
113+
Fields: []dinosql.GoField{
114+
{Name: "FirstName", Type: "string", Tags: map[string]string{"json:": "first_name"}},
115+
{Name: "LastName", Type: "sql.NullString", Tags: map[string]string{"json:": "last_name"}},
116+
{Name: "ID", Type: "int", Tags: map[string]string{"json:": "id"}},
117+
{Name: "Age", Type: "int", Tags: map[string]string{"json:": "age"}},
118+
{Name: "JobStatus", Type: "JobStatusType", Tags: map[string]string{"json:": "job_status"}},
119+
}},
120+
},
121+
},
122+
}
123+
124+
for _, tc := range tcase {
125+
structs := tc.input.Structs(mockSettings)
126+
if diff := cmp.Diff(structs, tc.output); diff != "" {
127+
t.Errorf(diff)
128+
}
129+
}
130+
}

internal/mysql/parse.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ func parsePath(sqlPath string, inPkg string, s *Schema, settings dinosql.Generat
5151
queries, err := parseContents(filename, contents, s, settings)
5252
if err != nil {
5353
if posErr, ok := err.(sqlparser.PositionedErr); ok {
54-
parseErrors.Add(filename, contents, posErr.Pos, fmt.Errorf("%s at or near \"%s\"", posErr.Err, posErr.Near))
54+
message := fmt.Errorf(posErr.Err)
55+
if posErr.Near != nil {
56+
message = fmt.Errorf("%s at or near \"%s\"", posErr.Err, posErr.Near)
57+
}
58+
parseErrors.Add(filename, contents, posErr.Pos, message)
5559
} else {
5660
parseErrors.Add(filename, contents, 0, err)
5761
}

internal/mysql/parse_test.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ func init() {
1515
initMockSchema()
1616
}
1717

18-
const filename = "test_data/queries.sql"
19-
const configPath = "test_data/sqlc.json"
18+
const mockFileName = "test_data/queries.sql"
19+
const mockConfigPath = "test_data/sqlc.json"
2020

2121
var mockSettings = dinosql.GenerateSettings{
2222
Version: "1",
@@ -29,7 +29,7 @@ var mockSettings = dinosql.GenerateSettings{
2929
}
3030

3131
func TestParseConfig(t *testing.T) {
32-
blob, err := ioutil.ReadFile(configPath)
32+
blob, err := ioutil.ReadFile(mockConfigPath)
3333
if err != nil {
3434
t.Fatal(err)
3535
}
@@ -41,7 +41,7 @@ func TestParseConfig(t *testing.T) {
4141
}
4242

4343
func TestGeneratePkg(t *testing.T) {
44-
_, err := GeneratePkg(mockSettings.Packages[0].Name, filename, filename, mockSettings)
44+
_, err := GeneratePkg(mockSettings.Packages[0].Name, mockFileName, mockFileName, mockSettings)
4545
if err != nil {
4646
if pErr, ok := err.(*dinosql.ParserErr); ok {
4747
for _, fileErr := range pErr.Errs {
@@ -92,6 +92,14 @@ func initMockSchema() {
9292
NotNull: true,
9393
},
9494
},
95+
&sqlparser.ColumnDefinition{
96+
Name: sqlparser.NewColIdent("job_status"),
97+
Type: sqlparser.ColumnType{
98+
Type: "enum",
99+
NotNull: true,
100+
EnumValues: []string{"applied", "pending", "accepted", "rejected"},
101+
},
102+
},
95103
}
96104
schemaMap["orders"] = []*sqlparser.ColumnDefinition{
97105
&sqlparser.ColumnDefinition{
@@ -213,8 +221,8 @@ func TestParseSelect(t *testing.T) {
213221
schema: mockSchema,
214222
},
215223
output: &Query{
216-
SQL: "select first_name, last_name, id, age from users",
217-
Columns: filterCols(mockSchema.tables["users"], map[string]string{"first_name": "users", "last_name": "users", "id": "users", "age": "users"}),
224+
SQL: "select first_name, last_name, id, age, job_status from users",
225+
Columns: filterCols(mockSchema.tables["users"], map[string]string{"first_name": "users", "last_name": "users", "id": "users", "age": "users", "job_status": "users"}),
218226
Params: []*Param{},
219227
Name: "GetAll",
220228
Cmd: ":many",

0 commit comments

Comments
 (0)