1
1
package cli
2
2
3
3
import (
4
+ "errors"
4
5
"io/ioutil"
5
6
"os"
6
7
"path/filepath"
7
8
"strconv"
9
+ "strings"
8
10
"testing"
9
11
"time"
10
12
11
13
"github.com/stretchr/testify/suite"
12
14
)
13
15
16
+ var noErrorExpected error = nil
17
+
14
18
type CreateCmdSuite struct {
15
19
suite.Suite
16
20
}
@@ -20,7 +24,7 @@ func TestCreateCmdSuite(t *testing.T) {
20
24
}
21
25
22
26
func (s * CreateCmdSuite ) mustCreateTempDir () string {
23
- tmpDir , err := ioutil .TempDir ("" , "" )
27
+ tmpDir , err := ioutil .TempDir ("" , "migrate_ " )
24
28
25
29
if err != nil {
26
30
s .FailNow (err .Error ())
@@ -79,34 +83,34 @@ func (s *CreateCmdSuite) TestNextSeqVersion() {
79
83
matches []string
80
84
seqDigits int
81
85
expected string
82
- expectedErr string
86
+ expectedErr error
83
87
}{
84
- {"Bad digits" , []string {}, 0 , "" , errInvalidSequenceWidth . Error () },
85
- {"Single digit initialize" , []string {}, 1 , "1" , "" },
86
- {"Single digit malformed" , []string {"bad" }, 1 , "" , "Malformed migration filename: bad" },
87
- {"Single digit no int" , []string {"bad_bad" }, 1 , "" , `strconv.ParseUint: parsing "bad": invalid syntax` },
88
- {"Single digit negative seq" , []string {"-5_test" }, 1 , "" , `strconv.ParseUint: parsing "-5": invalid syntax` },
89
- {"Single digit increment" , []string {"3_test" , "4_test" }, 1 , "5" , "" },
90
- {"Single digit overflow" , []string {"9_test" }, 1 , "" , "Next sequence number 10 too large. At most 1 digits are allowed" },
91
- {"Zero-pad initialize" , []string {}, 6 , "000001" , "" },
92
- {"Zero-pad malformed" , []string {"bad" }, 6 , "" , "Malformed migration filename: bad" },
93
- {"Zero-pad no int" , []string {"bad_bad" }, 6 , "" , `strconv.ParseUint: parsing "bad": invalid syntax` },
94
- {"Zero-pad negative seq" , []string {"-000005_test" }, 6 , "" , `strconv.ParseUint: parsing "-000005": invalid syntax` },
95
- {"Zero-pad increment" , []string {"000003_test" , "000004_test" }, 6 , "000005" , "" },
96
- {"Zero-pad overflow" , []string {"999999_test" }, 6 , "" , "Next sequence number 1000000 too large. At most 6 digits are allowed" },
97
- {"dir absolute path" , []string {"/migrationDir/000001_test" }, 6 , "000002" , "" },
98
- {"dir relative path" , []string {"migrationDir/000001_test" }, 6 , "000002" , "" },
99
- {"dir dot prefix" , []string {"./migrationDir/000001_test" }, 6 , "000002" , "" },
100
- {"dir parent prefix" , []string {"../migrationDir/000001_test" }, 6 , "000002" , "" },
101
- {"dir no prefix" , []string {"000001_test" }, 6 , "000002" , "" },
88
+ {"Bad digits" , []string {}, 0 , "" , errInvalidSequenceWidth },
89
+ {"Single digit initialize" , []string {}, 1 , "1" , noErrorExpected },
90
+ {"Single digit malformed" , []string {"bad" }, 1 , "" , errors . New ( "Malformed migration filename: bad" ) },
91
+ {"Single digit no int" , []string {"bad_bad" }, 1 , "" , errors . New ( `strconv.ParseUint: parsing "bad": invalid syntax` ) },
92
+ {"Single digit negative seq" , []string {"-5_test" }, 1 , "" , errors . New ( `strconv.ParseUint: parsing "-5": invalid syntax` ) },
93
+ {"Single digit increment" , []string {"3_test" , "4_test" }, 1 , "5" , noErrorExpected },
94
+ {"Single digit overflow" , []string {"9_test" }, 1 , "" , errors . New ( "Next sequence number 10 too large. At most 1 digits are allowed" ) },
95
+ {"Zero-pad initialize" , []string {}, 6 , "000001" , noErrorExpected },
96
+ {"Zero-pad malformed" , []string {"bad" }, 6 , "" , errors . New ( "Malformed migration filename: bad" ) },
97
+ {"Zero-pad no int" , []string {"bad_bad" }, 6 , "" , errors . New ( `strconv.ParseUint: parsing "bad": invalid syntax` ) },
98
+ {"Zero-pad negative seq" , []string {"-000005_test" }, 6 , "" , errors . New ( `strconv.ParseUint: parsing "-000005": invalid syntax` ) },
99
+ {"Zero-pad increment" , []string {"000003_test" , "000004_test" }, 6 , "000005" , noErrorExpected },
100
+ {"Zero-pad overflow" , []string {"999999_test" }, 6 , "" , errors . New ( "Next sequence number 1000000 too large. At most 6 digits are allowed" ) },
101
+ {"dir absolute path" , []string {"/migrationDir/000001_test" }, 6 , "000002" , noErrorExpected },
102
+ {"dir relative path" , []string {"migrationDir/000001_test" }, 6 , "000002" , noErrorExpected },
103
+ {"dir dot prefix" , []string {"./migrationDir/000001_test" }, 6 , "000002" , noErrorExpected },
104
+ {"dir parent prefix" , []string {"../migrationDir/000001_test" }, 6 , "000002" , noErrorExpected },
105
+ {"dir no prefix" , []string {"000001_test" }, 6 , "000002" , noErrorExpected },
102
106
}
103
107
104
108
for _ , c := range cases {
105
109
s .Run (c .tid , func () {
106
110
v , err := nextSeqVersion (c .matches , c .seqDigits )
107
111
108
- if c .expectedErr != "" {
109
- s .EqualError (err , c .expectedErr )
112
+ if c .expectedErr != nil {
113
+ s .EqualError (err , c .expectedErr . Error () )
110
114
} else {
111
115
s .NoError (err )
112
116
s .Equal (c .expected , v )
@@ -125,20 +129,20 @@ func (s *CreateCmdSuite) TestTimeVersion() {
125
129
time time.Time
126
130
format string
127
131
expected string
128
- expectedErr string
132
+ expectedErr error
129
133
}{
130
- {"Bad format" , ts , "" , "" , errInvalidTimeFormat . Error () },
131
- {"unix" , ts , "unix" , tsUnixStr , "" },
132
- {"unixNano" , ts , "unixNano" , tsUnixNanoStr , "" },
133
- {"custom ymthms" , ts , "20060102150405" , "20001225000102" , "" },
134
+ {"Bad format" , ts , "" , "" , errInvalidTimeFormat },
135
+ {"unix" , ts , "unix" , tsUnixStr , noErrorExpected },
136
+ {"unixNano" , ts , "unixNano" , tsUnixNanoStr , noErrorExpected },
137
+ {"custom ymthms" , ts , "20060102150405" , "20001225000102" , noErrorExpected },
134
138
}
135
139
136
140
for _ , c := range cases {
137
141
s .Run (c .tid , func () {
138
142
v , err := timeVersion (c .time , c .format )
139
143
140
- if c .expectedErr != "" {
141
- s .EqualError (err , c .expectedErr )
144
+ if c .expectedErr != nil {
145
+ s .EqualError (err , c .expectedErr . Error () )
142
146
} else {
143
147
s .NoError (err )
144
148
s .Equal (c .expected , v )
@@ -163,7 +167,7 @@ func (s *CreateCmdSuite) TestCreateCmd() {
163
167
cwd string // path to chdir to before test. relative to baseDir.
164
168
existingFiles []string // file paths created before test. relative to baseDir.
165
169
expectedFiles []string // file paths expected to exist after test. paths relative to baseDir.
166
- expectedErr string
170
+ expectedErr error
167
171
dir string // `dir` parameter. if absolute path, will be converted to baseDir/dir.
168
172
startTime time.Time
169
173
format string
@@ -172,32 +176,32 @@ func (s *CreateCmdSuite) TestCreateCmd() {
172
176
ext string
173
177
name string
174
178
}{
175
- {"seq and format" , nil , "" , nil , nil , errIncompatibleSeqAndFormat . Error () , "." , ts , "unix" , true , 4 , "sql" , "name" },
176
- {"seq init dir dot" , nil , "" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, "" , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
177
- {"seq init dir dot trailing slash" , nil , "" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, "" , "./" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
178
- {"seq init dir double dot" , []string {"subdir" }, "subdir" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, "" , ".." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
179
- {"seq init dir double dot trailing slash" , []string {"subdir" }, "subdir" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, "" , "../" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
180
- {"seq init dir absolute" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "/subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
181
- {"seq init dir absolute trailing slash" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "/subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
182
- {"seq init dir relative" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
183
- {"seq init dir relative trailing slash" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
184
- {"seq init dir dot relative" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "./subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
185
- {"seq init dir dot relative trailing slash" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "./subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
186
- {"seq init dir double dot relative" , []string {"subdir" }, "subdir" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "../subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
187
- {"seq init dir double dot relative trailing slash" , []string {"subdir" }, "subdir" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, "" , "../subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
188
- {"seq init dir maze" , []string {"subdir" }, "subdir" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, "" , "..//subdir/./.././/subdir/.." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
189
- {"seq width invalid" , nil , "" , nil , nil , errInvalidSequenceWidth . Error () , "." , ts , defaultTimeFormat , true , 0 , "sql" , "name" },
190
- {"seq malformed" , nil , "" , []string {"bad.sql" }, []string {"bad.sql" }, "Malformed migration filename: bad.sql" , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
191
- {"seq not int" , nil , "" , []string {"bad_bad.sql" }, []string {"bad_bad.sql" }, `strconv.ParseUint: parsing "bad": invalid syntax` , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
192
- {"seq negative" , nil , "" , []string {"-5_negative.sql" }, []string {"-5_negative.sql" }, `strconv.ParseUint: parsing "-5": invalid syntax` , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
193
- {"seq increment" , nil , "" , []string {"3_three.sql" , "4_four.sql" }, []string {"3_three.sql" , "4_four.sql" , "0005_five.up.sql" , "0005_five.down.sql" }, "" , "." , ts , defaultTimeFormat , true , 4 , "sql" , "five" },
194
- {"seq overflow" , nil , "" , []string {"9_nine.sql" }, []string {"9_nine.sql" }, `Next sequence number 10 too large. At most 1 digits are allowed` , "." , ts , defaultTimeFormat , true , 1 , "sql" , "ten" },
195
- {"time empty format" , nil , "" , nil , nil , errInvalidTimeFormat . Error () , "." , ts , "" , false , 0 , "sql" , "name" },
196
- {"time unix" , nil , "" , nil , []string {tsUnixStr + "_name.up.sql" , tsUnixStr + "_name.down.sql" }, "" , "." , ts , "unix" , false , 0 , "sql" , "name" },
197
- {"time unixNano" , nil , "" , nil , []string {tsUnixNanoStr + "_name.up.sql" , tsUnixNanoStr + "_name.down.sql" }, "" , "." , ts , "unixNano" , false , 0 , "sql" , "name" },
198
- {"time custom format" , nil , "" , nil , []string {"20001225000102_name.up.sql" , "20001225000102_name.down.sql" }, "" , "." , ts , "20060102150405" , false , 0 , "sql" , "name" },
199
- {"time version collision" , nil , "" , []string {"20001225_name.up.sql" , "20001225_name.down.sql" }, []string {"20001225_name.up.sql" , "20001225_name.down.sql" }, "duplicate migration version: 20001225" , "." , ts , "20060102" , false , 0 , "sql" , "name" },
200
- {"dir invalid" , nil , "" , []string {"file" }, []string {"file" }, "" , " mkdir file: not a directory" , ts , "unix" , false , 0 , "sql" , "name" },
179
+ {"seq and format" , nil , "" , nil , nil , errIncompatibleSeqAndFormat , "." , ts , "unix" , true , 4 , "sql" , "name" },
180
+ {"seq init dir dot" , nil , "" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, noErrorExpected , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
181
+ {"seq init dir dot trailing slash" , nil , "" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, noErrorExpected , "./" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
182
+ {"seq init dir double dot" , []string {"subdir" }, "subdir" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, noErrorExpected , ".." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
183
+ {"seq init dir double dot trailing slash" , []string {"subdir" }, "subdir" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, noErrorExpected , "../" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
184
+ {"seq init dir absolute" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "/subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
185
+ {"seq init dir absolute trailing slash" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "/subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
186
+ {"seq init dir relative" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
187
+ {"seq init dir relative trailing slash" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
188
+ {"seq init dir dot relative" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "./subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
189
+ {"seq init dir dot relative trailing slash" , []string {"subdir" }, "" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "./subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
190
+ {"seq init dir double dot relative" , []string {"subdir" }, "subdir" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "../subdir" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
191
+ {"seq init dir double dot relative trailing slash" , []string {"subdir" }, "subdir" , nil , []string {"subdir/0001_name.up.sql" , "subdir/0001_name.down.sql" }, noErrorExpected , "../subdir/" , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
192
+ {"seq init dir maze" , []string {"subdir" }, "subdir" , nil , []string {"0001_name.up.sql" , "0001_name.down.sql" }, noErrorExpected , "..//subdir/./.././/subdir/.." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
193
+ {"seq width invalid" , nil , "" , nil , nil , errInvalidSequenceWidth , "." , ts , defaultTimeFormat , true , 0 , "sql" , "name" },
194
+ {"seq malformed" , nil , "" , []string {"bad.sql" }, []string {"bad.sql" }, errors . New ( "Malformed migration filename: bad.sql" ) , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
195
+ {"seq not int" , nil , "" , []string {"bad_bad.sql" }, []string {"bad_bad.sql" }, errors . New ( `strconv.ParseUint: parsing "bad": invalid syntax` ) , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
196
+ {"seq negative" , nil , "" , []string {"-5_negative.sql" }, []string {"-5_negative.sql" }, errors . New ( `strconv.ParseUint: parsing "-5": invalid syntax` ) , "." , ts , defaultTimeFormat , true , 4 , "sql" , "name" },
197
+ {"seq increment" , nil , "" , []string {"3_three.sql" , "4_four.sql" }, []string {"3_three.sql" , "4_four.sql" , "0005_five.up.sql" , "0005_five.down.sql" }, noErrorExpected , "." , ts , defaultTimeFormat , true , 4 , "sql" , "five" },
198
+ {"seq overflow" , nil , "" , []string {"9_nine.sql" }, []string {"9_nine.sql" }, errors . New ( `Next sequence number 10 too large. At most 1 digits are allowed` ) , "." , ts , defaultTimeFormat , true , 1 , "sql" , "ten" },
199
+ {"time empty format" , nil , "" , nil , nil , errInvalidTimeFormat , "." , ts , "" , false , 0 , "sql" , "name" },
200
+ {"time unix" , nil , "" , nil , []string {tsUnixStr + "_name.up.sql" , tsUnixStr + "_name.down.sql" }, noErrorExpected , "." , ts , "unix" , false , 0 , "sql" , "name" },
201
+ {"time unixNano" , nil , "" , nil , []string {tsUnixNanoStr + "_name.up.sql" , tsUnixNanoStr + "_name.down.sql" }, noErrorExpected , "." , ts , "unixNano" , false , 0 , "sql" , "name" },
202
+ {"time custom format" , nil , "" , nil , []string {"20001225000102_name.up.sql" , "20001225000102_name.down.sql" }, noErrorExpected , "." , ts , "20060102150405" , false , 0 , "sql" , "name" },
203
+ {"time version collision" , nil , "" , []string {"20001225_name.up.sql" , "20001225_name.down.sql" }, []string {"20001225_name.up.sql" , "20001225_name.down.sql" }, errors . New ( "duplicate migration version: 20001225" ) , "." , ts , "20060102" , false , 0 , "sql" , "name" },
204
+ {"dir invalid" , nil , "" , []string {"file" }, []string {"file" }, errors . New ( " mkdir 'test: this is invalid dir name': The directory name is invalid." ), `'test: this is invalid dir name'` , ts , "unix" , false , 0 , "sql" , "name" },
201
205
}
202
206
203
207
for _ , c := range cases {
@@ -221,15 +225,19 @@ func (s *CreateCmdSuite) TestCreateCmd() {
221
225
}
222
226
223
227
dir := c .dir
224
-
225
- if filepath .IsAbs (dir ) {
228
+ dir = filepath .ToSlash (dir )
229
+ volName := filepath .VolumeName (baseDir )
230
+ // Windows specific, can not recognize \subdir as abs path
231
+ isWindowsAbsPathNoLetter := strings .HasPrefix (dir , "/" ) && volName != ""
232
+ isRealAbsPath := filepath .IsAbs (dir )
233
+ if isWindowsAbsPathNoLetter || isRealAbsPath {
226
234
dir = filepath .Join (baseDir , dir )
227
235
}
228
236
229
237
err := createCmd (dir , c .startTime , c .format , c .name , c .ext , c .seq , c .seqDigits , false )
230
238
231
- if c .expectedErr != "" {
232
- s .EqualError (err , c .expectedErr )
239
+ if c .expectedErr != nil {
240
+ s .EqualError (err , c .expectedErr . Error () )
233
241
} else {
234
242
s .NoError (err )
235
243
}
0 commit comments