github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/sqx/dot_test.go (about)

     1  package sqx_test
     2  
     3  import (
     4  	"bufio"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/bingoohuang/gg/pkg/sqx"
    10  	_ "github.com/mattn/go-sqlite3"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  type mss = map[string]string
    15  
    16  func TestGetTag(t *testing.T) {
    17  	tests := []struct {
    18  		line, want string
    19  		attrs      mss
    20  	}{
    21  		{"SELECT 1+1", "", nil},
    22  		{"-- Some Comment", "", mss{"Some": "", "Comment": ""}},
    23  		{"-- name:  ", "", mss{"name": ""}},
    24  		{
    25  			"-- name: find-users-by-name dbtype: mysql", "find-users-by-name",
    26  			mss{"name": "find-users-by-name", "dbtype": "mysql"},
    27  		},
    28  		{"  --  name:  save-user ", "save-user", mss{"name": "save-user"}},
    29  	}
    30  
    31  	for _, c := range tests {
    32  		attrs, name := sqx.ParseDotTag(c.line, "--", "name")
    33  		if name != c.want {
    34  			t.Errorf("isTag('%s') == %s, expect %s", c.line, name, c.want)
    35  		}
    36  
    37  		if !reflect.DeepEqual(attrs, c.attrs) {
    38  			t.Errorf("attrsOfTag('%s') == %v, expect %v", c.line, attrs, c.attrs)
    39  		}
    40  	}
    41  }
    42  
    43  func TestScannerRun(t *testing.T) {
    44  	sqlFile := `
    45  	-- name: all-users
    46  	-- Finds all users
    47  	SELECT * from USER
    48  	-- name: empty-query-should-not-be-stored
    49  	-- name: save-user
    50  	INSERT INTO users (?, ?, ?)
    51  	`
    52  
    53  	scanner := &sqx.DotScanner{}
    54  	queries := scanner.Run(bufio.NewScanner(strings.NewReader(sqlFile)))
    55  
    56  	numberOfQueries := len(queries)
    57  	expectedQueries := 2
    58  
    59  	if numberOfQueries != expectedQueries {
    60  		t.Errorf("Scanner/Run() has %d queries instead of %d",
    61  			numberOfQueries, expectedQueries)
    62  	}
    63  }
    64  
    65  func TestLoad(t *testing.T) {
    66  	_, err := sqx.DotSQLLoad(strings.NewReader(""))
    67  	assert.Nil(t, err)
    68  }
    69  
    70  func TestLoadFromFile(t *testing.T) {
    71  	dot, err := sqx.DotSQLLoadFile("./non-existent.sql")
    72  	assert.NotNil(t, err, "error expected to be non-nil, got nil")
    73  	assert.Nil(t, dot, "dotsql instance expected to be nil, got non-nil")
    74  
    75  	dot, err = sqx.DotSQLLoadFile("testdata/test_schema.sql")
    76  	assert.Nil(t, err)
    77  
    78  	assert.NotNil(t, dot, "dotsql instance expected to be non-nil, got nil")
    79  }
    80  
    81  func TestLoadFromString(t *testing.T) {
    82  	_, err := sqx.DotSQLLoadString("")
    83  	assert.Nil(t, err)
    84  }
    85  
    86  func TestRaw(t *testing.T) {
    87  	expectedQuery := "SELECT 1+1"
    88  
    89  	dot, err := sqx.DotSQLLoadString("--name: my-query\n" + expectedQuery)
    90  	assert.Nil(t, err)
    91  
    92  	eval, err := dot.Raw("my-query")
    93  	assert.Nil(t, err)
    94  
    95  	got, _ := eval.Eval(map[string]interface{}{})
    96  	got = strings.TrimSpace(got)
    97  	assert.Equal(t, expectedQuery, got, "Raw() == '%s', expected '%s'", got, expectedQuery)
    98  }
    99  
   100  func TestQueries(t *testing.T) {
   101  	expectedQueryMap := map[string]string{
   102  		"select": "SELECT *\nfrom users",
   103  		"insert": "INSERT INTO users (?, ?, ?)",
   104  	}
   105  
   106  	dot, err := sqx.DotSQLLoadString(`
   107  -- name: select
   108  SELECT *
   109  from users;
   110  
   111  -- name: insert
   112  INSERT INTO users (?, ?, ?)
   113  	`)
   114  	assert.Nil(t, err)
   115  
   116  	got := dot.Dots
   117  
   118  	if len(got) != len(expectedQueryMap) {
   119  		t.Errorf("QueryMap() len (%d) differ from expected (%d)", len(got), len(expectedQueryMap))
   120  	}
   121  
   122  	for name, query := range got {
   123  		if query.RawSQL() != expectedQueryMap[name] {
   124  			t.Errorf("QueryMap()[%s] == '%s', expected '%s'", name, query, expectedQueryMap[name])
   125  		}
   126  	}
   127  }
   128  
   129  func TestParseSQL(t *testing.T) {
   130  	parsed, err := sqx.ParseSQL("", "insert into person(name, age) values(:name, :age)")
   131  	assert.Nil(t, err)
   132  	assert.Equal(t, &sqx.SQLParsed{
   133  		RawStmt: "insert into person(name, age) values(?, ?)",
   134  		BindBy:  sqx.ByName,
   135  		Vars:    []string{"name", "age"},
   136  		MaxSeq:  2,
   137  	}, parsed)
   138  
   139  	parsed, err = sqx.ParseSQL("", "insert into person(name, age) values(:1, :2)")
   140  	assert.Nil(t, err)
   141  	assert.Equal(t, &sqx.SQLParsed{
   142  		RawStmt: "insert into person(name, age) values(?, ?)",
   143  		BindBy:  sqx.BySeq,
   144  		Vars:    []string{"1", "2"},
   145  		MaxSeq:  2,
   146  	}, parsed)
   147  
   148  	parsed, err = sqx.ParseSQL("", "insert into person(name, age) values(:, :)")
   149  	assert.Nil(t, err)
   150  	assert.Equal(t, &sqx.SQLParsed{
   151  		RawStmt: "insert into person(name, age) values(?, ?)",
   152  		BindBy:  sqx.ByAuto,
   153  		Vars:    []string{"", ""},
   154  		MaxSeq:  2,
   155  	}, parsed)
   156  
   157  	parsed, err = sqx.ParseSQL("", "insert into person(name, age) values('a', 'b')")
   158  	assert.Nil(t, err)
   159  	assert.Equal(t, &sqx.SQLParsed{
   160  		RawStmt: "insert into person(name, age) values('a', 'b')",
   161  		BindBy:  sqx.ByNone,
   162  		Vars:    []string{},
   163  	}, parsed)
   164  
   165  	parsed, err = sqx.ParseSQL("auto", "insert into person(name, age) values(:, :age)")
   166  	assert.Nil(t, parsed)
   167  	assert.NotNil(t, err)
   168  }
   169  
   170  func TestConvertSQLLines(t *testing.T) {
   171  	that := assert.New(t)
   172  
   173  	that.Equal([]string{"a\nb\nc"}, sqx.ConvertSQLLines([]string{"a", "b", "c"}))
   174  	that.Equal([]string{"--a", "b\nc"}, sqx.ConvertSQLLines([]string{"--a", "b", "c"}))
   175  	that.Equal([]string{"-- if", "b", "-- end"}, sqx.ConvertSQLLines([]string{"-- if", "b", "-- end"}))
   176  	that.Equal([]string{"-- if", "b", "-- end"}, sqx.ConvertSQLLines([]string{"/* if */ b /* end */"}))
   177  	that.Equal([]string{"-- if", "b", "-- end"}, sqx.ConvertSQLLines([]string{"/* if */ ", "b", " /* end */"}))
   178  	that.Equal([]string{"-- if", "b\nc", "-- end"}, sqx.ConvertSQLLines([]string{"/* if */ ", "b", "c", " /* end */"}))
   179  	that.Equal([]string{"-- if", "b\nc", "-- end"}, sqx.ConvertSQLLines([]string{"/* if  ", "*/b", "c/*", " end */"}))
   180  }
   181  
   182  func TestParseDynamicSQL(t *testing.T) {
   183  	that := assert.New(t)
   184  
   185  	{
   186  		lines, part, err := sqx.ParseDynamicSQL([]string{"-- if a", "b", "-- end"})
   187  		that.Nil(err)
   188  		that.Equal(3, lines)
   189  		that.Equal(&sqx.MultiPart{Parts: []sqx.SQLPart{
   190  			&sqx.IfPart{
   191  				Conditions: []sqx.IfCondition{
   192  					{
   193  						Expr: "a",
   194  						Part: &sqx.MultiPart{
   195  							Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "b"}},
   196  						},
   197  					},
   198  				},
   199  				Else: nil,
   200  			},
   201  		}}, part)
   202  	}
   203  
   204  	{
   205  		lines, part, err := sqx.ParseDynamicSQL([]string{"-- if a", "b", "-- else ", "c", "-- end"})
   206  		that.Nil(err)
   207  		that.Equal(5, lines)
   208  		that.Equal(&sqx.MultiPart{Parts: []sqx.SQLPart{
   209  			&sqx.IfPart{
   210  				Conditions: []sqx.IfCondition{
   211  					{
   212  						Expr: "a",
   213  						Part: &sqx.MultiPart{
   214  							Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "b"}},
   215  						},
   216  					},
   217  				},
   218  				Else: &sqx.MultiPart{
   219  					Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "c"}},
   220  				},
   221  			},
   222  		}}, part)
   223  	}
   224  
   225  	{
   226  		// nolint:lll
   227  		lines, part, err := sqx.ParseDynamicSQL([]string{"-- if a", "-- if b", "b", "-- end", "-- else ", "-- if c", "c", "-- end", "-- end"})
   228  		that.Nil(err)
   229  		that.Equal(9, lines)
   230  		that.Equal(&sqx.MultiPart{Parts: []sqx.SQLPart{
   231  			&sqx.IfPart{
   232  				Conditions: []sqx.IfCondition{
   233  					{
   234  						Expr: "a",
   235  						Part: &sqx.MultiPart{
   236  							Parts: []sqx.SQLPart{
   237  								&sqx.IfPart{
   238  									Conditions: []sqx.IfCondition{
   239  										{
   240  											Expr: "b",
   241  											Part: &sqx.MultiPart{
   242  												Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "b"}},
   243  											},
   244  										},
   245  									},
   246  								},
   247  							},
   248  						},
   249  					},
   250  				},
   251  				Else: &sqx.MultiPart{
   252  					Parts: []sqx.SQLPart{
   253  						&sqx.IfPart{
   254  							Conditions: []sqx.IfCondition{
   255  								{
   256  									Expr: "c",
   257  									Part: &sqx.MultiPart{
   258  										Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "c"}},
   259  									},
   260  								},
   261  							},
   262  						},
   263  					},
   264  				},
   265  			},
   266  		}}, part)
   267  	}
   268  
   269  	{
   270  		lines, part, err := sqx.ParseDynamicSQL([]string{"-- if a", "a", "-- elseif b ", "b", "-- end"})
   271  		that.Nil(err)
   272  		that.Equal(5, lines)
   273  		that.Equal(&sqx.MultiPart{Parts: []sqx.SQLPart{
   274  			&sqx.IfPart{
   275  				Conditions: []sqx.IfCondition{
   276  					{
   277  						Expr: "a",
   278  						Part: &sqx.MultiPart{
   279  							Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "a"}},
   280  						},
   281  					}, {
   282  						Expr: "b",
   283  						Part: &sqx.MultiPart{
   284  							Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "b"}},
   285  						},
   286  					},
   287  				},
   288  			},
   289  		}}, part)
   290  	}
   291  
   292  	{
   293  		// nolint:lll
   294  		lines, part, err := sqx.ParseDynamicSQL([]string{"-- if a", "-- if b", "ab", "-- elseif c ", "ac", "-- end", "-- end"})
   295  		that.Nil(err)
   296  		that.Equal(7, lines)
   297  		that.Equal(&sqx.MultiPart{Parts: []sqx.SQLPart{
   298  			&sqx.IfPart{
   299  				Conditions: []sqx.IfCondition{
   300  					{
   301  						Expr: "a",
   302  						Part: &sqx.MultiPart{
   303  							Parts: []sqx.SQLPart{
   304  								&sqx.IfPart{
   305  									Conditions: []sqx.IfCondition{
   306  										{
   307  											Expr: "b",
   308  											Part: &sqx.MultiPart{
   309  												Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "ab"}},
   310  											},
   311  										}, {
   312  											Expr: "c",
   313  											Part: &sqx.MultiPart{
   314  												Parts: []sqx.SQLPart{&sqx.LiteralPart{Literal: "ac"}},
   315  											},
   316  										},
   317  									},
   318  								},
   319  							},
   320  						},
   321  					},
   322  				},
   323  			},
   324  		}}, part)
   325  	}
   326  
   327  	{
   328  		lines, part, err := sqx.ParseDynamicSQL([]string{
   329  			"-- if a", "-- if b", "ab", "-- elseif c ", "ac", "-- end",
   330  			"-- else ", "x", "-- end",
   331  		})
   332  		that.Nil(err)
   333  		that.Equal(9, lines)
   334  		that.Equal(&sqx.MultiPart{Parts: []sqx.SQLPart{
   335  			&sqx.IfPart{
   336  				Conditions: []sqx.IfCondition{
   337  					{
   338  						Expr: "a",
   339  						Part: &sqx.MultiPart{
   340  							Parts: []sqx.SQLPart{
   341  								&sqx.IfPart{
   342  									Conditions: []sqx.IfCondition{
   343  										{
   344  											Expr: "b",
   345  											Part: &sqx.MultiPart{Parts: []sqx.SQLPart{
   346  												&sqx.LiteralPart{Literal: "ab"},
   347  											}},
   348  										}, {
   349  											Expr: "c",
   350  											Part: &sqx.MultiPart{Parts: []sqx.SQLPart{
   351  												&sqx.LiteralPart{Literal: "ac"},
   352  											}},
   353  										},
   354  									},
   355  								},
   356  							},
   357  						},
   358  					},
   359  				},
   360  				Else: &sqx.MultiPart{Parts: []sqx.SQLPart{&sqx.LiteralPart{
   361  					Literal: "x",
   362  				}}},
   363  			},
   364  		}}, part)
   365  	}
   366  }