github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/cmd/dosa/schema_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package main 22 23 import ( 24 "context" 25 "errors" 26 "os" 27 "testing" 28 "time" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 "github.com/uber-go/dosa" 33 "github.com/uber-go/dosa/mocks" 34 35 _ "github.com/uber-go/dosa/connectors/devnull" 36 ) 37 38 func getTestEntityNameMap() map[string]bool { 39 return map[string]bool{ 40 "awesome_test_entity": true, 41 "testnullableentity": true, 42 "named_import_entity": true, 43 "testnullablenamedimportentity": true, 44 } 45 } 46 47 func TestScopeFlag_String(t *testing.T) { 48 f := scopeFlag("") 49 f.setString("foo.bar.baz") 50 assert.Equal(t, "foo_bar_baz", f.String()) 51 52 err := f.UnmarshalFlag("qux.quux.corge") 53 assert.NoError(t, err) 54 assert.Equal(t, "qux_quux_corge", f.String()) 55 } 56 57 func TestSchema_ExpandDirectories(t *testing.T) { 58 assert := assert.New(t) 59 const tmpdir = ".testexpanddirectories" 60 os.RemoveAll(tmpdir) 61 defer os.RemoveAll(tmpdir) 62 63 if err := os.Mkdir(tmpdir, 0770); err != nil { 64 t.Fatalf("can't create %s: %s", tmpdir, err) 65 } 66 // note: these must be in lexical order :( 67 dirs := []string{"a", "a/b", "c", "c/d", "c/e"} 68 69 os.Chdir(tmpdir) 70 for _, dirToCreate := range dirs { 71 os.Mkdir(dirToCreate, 0770) 72 } 73 os.Create("a/b/file") 74 75 cases := []struct { 76 args []string 77 dirs []string 78 err error 79 }{ 80 { 81 args: []string{}, 82 dirs: []string{"."}, 83 }, 84 { 85 args: []string{"."}, 86 dirs: []string{"."}, 87 }, 88 { 89 args: []string{"./..."}, 90 dirs: append([]string{"."}, dirs...), 91 }, 92 { 93 args: []string{"bogus"}, 94 err: errors.New("no such file or directory"), 95 }, 96 { 97 args: []string{"a/b/file"}, 98 err: errors.New("not a directory"), 99 }, 100 } 101 102 for _, c := range cases { 103 dirs, err := expandDirectories(c.args) 104 if c.err != nil { 105 assert.Contains(err.Error(), c.err.Error()) 106 } else { 107 assert.Nil(err) 108 assert.Equal(c.dirs, dirs) 109 } 110 } 111 os.Chdir("..") 112 } 113 114 func TestSchema_ServiceInference(t *testing.T) { 115 tcs := []struct { 116 serviceName string 117 scope string 118 expected string 119 }{ 120 // service = "", scope = "" -> default 121 { 122 expected: _defServiceName, 123 }, 124 // service = "", scope != prod -> default 125 { 126 scope: "not-production", 127 expected: _defServiceName, 128 }, 129 // service = "", scope = prod -> prod 130 { 131 scope: _prodScope, 132 expected: _prodServiceName, 133 }, 134 // service = "foo", scope = "" -> "foo" 135 { 136 serviceName: "foo", 137 expected: "foo", 138 }, 139 // service = "bar", scope != prod -> "bar" 140 { 141 serviceName: "bar", 142 scope: "bar", 143 expected: "bar", 144 }, 145 // service = "baz", scope = prod -> "baz" 146 { 147 serviceName: "baz", 148 scope: _prodScope, 149 expected: "baz", 150 }, 151 } 152 153 for _, tc := range tcs { 154 for _, cmd := range []string{"check", "upsert", "status"} { 155 os.Args = []string{ 156 "dosa", 157 "--service", tc.serviceName, 158 "--connector", "devnull", 159 "schema", 160 cmd, 161 "--prefix", "foo", 162 "--scope", tc.scope, 163 "../../testentity", 164 } 165 main() 166 assert.Equal(t, options.ServiceName, tc.expected) 167 } 168 } 169 } 170 171 func TestSchema_PrefixRequired(t *testing.T) { 172 for _, cmd := range []string{"check", "upsert"} { 173 c := StartCapture() 174 exit = func(r int) {} 175 os.Args = []string{ 176 "dosa", 177 "schema", 178 cmd, 179 "../../testentity", 180 } 181 main() 182 assert.Contains(t, c.stop(true), "--prefix' was not specified") 183 } 184 } 185 186 func TestSchema_InvalidDirectory(t *testing.T) { 187 // dump is a special snowflake 188 prefixMap := map[string]bool{ 189 "check": true, 190 "upsert": true, 191 "dump": false, 192 } 193 for cmd, hasPrefix := range prefixMap { 194 c := StartCapture() 195 exit = func(r int) {} 196 os.Args = []string{ 197 "dosa", 198 "schema", 199 cmd, 200 } 201 if hasPrefix { 202 os.Args = append(os.Args, "--prefix", "foo") 203 } 204 os.Args = append(os.Args, []string{ 205 "-e", "testentity.go", 206 "../../testentity", 207 "/dev/null", 208 }...) 209 main() 210 assert.Contains(t, c.stop(true), "\"/dev/null\" is not a directory") 211 } 212 } 213 214 func TestSchema_NoEntitiesFound(t *testing.T) { 215 // dump is a special snowflake 216 prefixMap := map[string]bool{ 217 "check": true, 218 "upsert": true, 219 "dump": false, 220 } 221 for cmd, hasPrefix := range prefixMap { 222 c := StartCapture() 223 exit = func(r int) {} 224 os.Args = []string{ 225 "dosa", 226 "schema", 227 cmd, 228 } 229 if hasPrefix { 230 os.Args = append(os.Args, "--prefix", "foo") 231 } 232 os.Args = append(os.Args, []string{ 233 "-e", "testentity.go", 234 "-e", "keyvalue.go", 235 "-e", "named_import_testentity.go", 236 "../../testentity", 237 }...) 238 main() 239 assert.Contains(t, c.stop(true), "no entities found") 240 } 241 } 242 243 // There are 4 tests to perform against each operation 244 // 1 - success case, displays scope 245 // 2 - failure case, couldn't initialize the connector 246 // 3 - failure case, the connector API call fails 247 // 4 - failure case, problems with the directories on the command line or the entities 248 249 func TestSchema_Check_Happy(t *testing.T) { 250 ctrl := gomock.NewController(t) 251 defer ctrl.Finish() 252 253 exit = func(r int) { 254 assert.Equal(t, 0, r) 255 } 256 dosa.RegisterConnector("mock", func(dosa.CreationArgs) (dosa.Connector, error) { 257 mc := mocks.NewMockConnector(ctrl) 258 mc.EXPECT().CheckSchema(gomock.Any(), "scope", "foo", gomock.Any()). 259 Do(func(ctx context.Context, scope string, namePrefix string, ed []*dosa.EntityDefinition) { 260 dl, ok := ctx.Deadline() 261 assert.True(t, ok) 262 assert.True(t, dl.After(time.Now())) 263 assert.Equal(t, 6, len(ed)) 264 nameMap := getTestEntityNameMap() 265 for _, e := range ed { 266 assert.True(t, nameMap[e.Name]) 267 } 268 }).Return(int32(1), nil) 269 return mc, nil 270 }) 271 os.Args = []string{"dosa", "--connector", "mock", "schema", "check", "--prefix", "foo", "-e", "_test.go", "-e", "excludeme.go", "-s", "scope", "-v", "../../testentity"} 272 main() 273 } 274 275 func TestSchema_Status_Happy(t *testing.T) { 276 ctrl := gomock.NewController(t) 277 defer ctrl.Finish() 278 279 exit = func(r int) { 280 assert.Equal(t, 0, r) 281 } 282 dosa.RegisterConnector("mock", func(dosa.CreationArgs) (dosa.Connector, error) { 283 mc := mocks.NewMockConnector(ctrl) 284 mc.EXPECT().CheckSchemaStatus(gomock.Any(), "scope", "foo", gomock.Any()). 285 Do(func(ctx context.Context, scope string, namePrefix string, version int32) { 286 dl, ok := ctx.Deadline() 287 assert.True(t, ok) 288 assert.True(t, dl.After(time.Now())) 289 assert.Equal(t, int32(12), version) 290 }).Return(&dosa.SchemaStatus{Version: int32(12)}, nil) 291 return mc, nil 292 }) 293 os.Args = []string{"dosa", "--connector", "mock", "schema", "status", "--prefix", "foo", "-s", "scope", "-v", "--version", "12"} 294 main() 295 } 296 297 func TestSchema_Upsert_Happy(t *testing.T) { 298 ctrl := gomock.NewController(t) 299 defer ctrl.Finish() 300 301 exit = func(r int) { 302 assert.Equal(t, 0, r) 303 } 304 305 dosa.RegisterConnector("mock", func(dosa.CreationArgs) (dosa.Connector, error) { 306 mc := mocks.NewMockConnector(ctrl) 307 mc.EXPECT().UpsertSchema(gomock.Any(), "scope", "foo", gomock.Any()). 308 Do(func(ctx context.Context, scope string, namePrefix string, ed []*dosa.EntityDefinition) { 309 dl, ok := ctx.Deadline() 310 assert.True(t, ok) 311 assert.True(t, dl.After(time.Now())) 312 assert.Equal(t, 6, len(ed)) 313 314 nameMap := getTestEntityNameMap() 315 for _, e := range ed { 316 assert.True(t, nameMap[e.Name]) 317 } 318 }).Return(&dosa.SchemaStatus{Version: int32(1)}, nil) 319 return mc, nil 320 }) 321 os.Args = []string{"dosa", "--connector", "mock", "schema", "upsert", "--prefix", "foo", "-e", "_test.go", "-e", "excludeme.go", "-s", "scope", "-v", "../../testentity"} 322 main() 323 } 324 325 func TestSchema_Dump_InvalidFormat(t *testing.T) { 326 c := StartCapture() 327 exit = func(r int) {} 328 os.Args = []string{"dosa", "schema", "dump", "-f", "invalid", "../../testentity"} 329 main() 330 assert.Contains(t, c.stop(true), "Invalid value") 331 } 332 333 func TestSchema_Dump_CQL(t *testing.T) { 334 c := StartCapture() 335 exit = func(r int) {} 336 os.Args = []string{"dosa", "schema", "dump", "-v", "../../testentity"} 337 main() 338 output := c.stop(false) 339 assert.Contains(t, output, "executing schema dump") 340 assert.Contains(t, output, "create table \"awesome_test_entity\" (\"an_uuid_key\" uuid, \"strkey\" text, \"int64key\" bigint") 341 } 342 343 func TestSchema_Dump_UQL(t *testing.T) { 344 c := StartCapture() 345 exit = func(r int) {} 346 os.Args = []string{"dosa", "schema", "dump", "-f", "uql", "-v", "../../testentity"} 347 main() 348 output := c.stop(false) 349 assert.Contains(t, output, "executing schema dump") 350 assert.Contains(t, output, "CREATE TABLE awesome_test_entity") 351 assert.Contains(t, output, "an_int64_value int64;") 352 assert.Contains(t, output, "PRIMARY KEY (an_uuid_key, strkey ASC, int64key DESC);") 353 } 354 355 func TestSchema_Dump_Avro(t *testing.T) { 356 c := StartCapture() 357 exit = func(r int) {} 358 os.Args = []string{"dosa", "schema", "dump", "-f", "avro", "-v", "../../testentity"} 359 main() 360 output := c.stop(false) 361 assert.Contains(t, output, "executing schema dump") 362 }