github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/appfile/compile_test.go (about) 1 package appfile 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "reflect" 11 "strings" 12 "testing" 13 14 "github.com/hashicorp/terraform/dag" 15 ) 16 17 var testHasGit bool 18 19 func init() { 20 if _, err := exec.LookPath("git"); err == nil { 21 testHasGit = true 22 } 23 } 24 25 func TestCompiled_impl(t *testing.T) { 26 var _ json.Marshaler = new(Compiled) 27 var _ json.Unmarshaler = new(Compiled) 28 } 29 30 func TestCompile(t *testing.T) { 31 cases := []struct { 32 Dir string 33 String string 34 Err bool 35 }{ 36 { 37 "compile-basic", 38 testCompileBasicStr, 39 false, 40 }, 41 42 { 43 "compile-deps", 44 testCompileDepsStr, 45 false, 46 }, 47 48 { 49 "compile-multi-dep", 50 testCompileMultiDepStr, 51 false, 52 }, 53 54 { 55 "compile-invalid", 56 "", 57 true, 58 }, 59 60 { 61 "compile-cycle", 62 "", 63 true, 64 }, 65 66 { 67 "compile-deps-no-id", 68 "", 69 true, 70 }, 71 72 /* 73 TODO: uncomment once we can enforce this 74 { 75 "compile-diff-project", 76 "", 77 true, 78 }, 79 */ 80 } 81 82 for _, tc := range cases { 83 t.Logf("Testing: %s", tc.Dir) 84 85 // We wrap this in a function just so we can use defers 86 func() { 87 opts := testCompileOpts(t) 88 defer os.RemoveAll(opts.Dir) 89 f := testFile(t, tc.Dir) 90 defer f.resetID() 91 92 c, err := testCompiler(t, opts).Compile(f) 93 if (err != nil) != tc.Err { 94 t.Fatalf("err: %s\n\n%s", tc.Dir, err) 95 } 96 97 if err == nil { 98 if tc.String != "" { 99 testCompileCompare(t, c, tc.String) 100 } 101 testCompileMarshal(t, c, opts.Dir) 102 } 103 }() 104 } 105 } 106 107 // This is a really important test case that verifies that ".ottoid" 108 // is not ignored from dependencies. We had this happen with 0.1 109 func TestCompile_dotOttoId(t *testing.T) { 110 if !testHasGit { 111 t.Log("git not found, skipping") 112 t.Skip() 113 } 114 115 opts := testCompileOpts(t) 116 defer os.RemoveAll(opts.Dir) 117 f := testFile(t, "compile-deps-git") 118 defer f.resetID() 119 120 // Rename DOTgit to .git since Git doesn't allow nested .git 121 dir := filepath.Join(filepath.Dir(f.Path), "child") 122 oldName := filepath.Join(dir, "DOTgit") 123 newName := filepath.Join(dir, ".git") 124 if err := os.Rename(oldName, newName); err != nil { 125 t.Fatalf("err: %s", err) 126 } 127 defer os.Rename(newName, oldName) 128 129 c, err := testCompiler(t, opts).Compile(f) 130 if err != nil { 131 t.Fatalf("err:\n\n%s", err) 132 } 133 134 testCompileCompare(t, c, testCompileDepGitStr) 135 testCompileMarshal(t, c, opts.Dir) 136 } 137 138 func TestCompile_structure(t *testing.T) { 139 cases := []struct { 140 Dir string 141 Name string 142 File *File 143 Err bool 144 }{ 145 { 146 "import-basic", 147 "", 148 &File{ 149 Application: &Application{ 150 Name: "foo", 151 Type: "bar", 152 Detect: true, 153 }, 154 Project: &Project{ 155 Name: "foo", 156 Infrastructure: "aws", 157 }, 158 Infrastructure: []*Infrastructure{ 159 &Infrastructure{ 160 Name: "aws", 161 Type: "aws", 162 }, 163 }, 164 }, 165 false, 166 }, 167 168 { 169 "import-nested", 170 "", 171 &File{ 172 Application: &Application{ 173 Name: "bar", 174 Type: "bar", 175 Detect: true, 176 }, 177 Project: &Project{ 178 Name: "foo", 179 Infrastructure: "aws", 180 }, 181 Infrastructure: []*Infrastructure{ 182 &Infrastructure{ 183 Name: "aws", 184 Type: "aws", 185 }, 186 }, 187 }, 188 false, 189 }, 190 191 { 192 "import-cycle", 193 "", 194 nil, 195 true, 196 }, 197 198 { 199 "import-dep", 200 "child", 201 &File{ 202 Application: &Application{ 203 Name: "child", 204 Type: "bar", 205 Detect: true, 206 }, 207 Project: &Project{ 208 Name: "bar", 209 Infrastructure: "aws", 210 }, 211 Infrastructure: []*Infrastructure{ 212 &Infrastructure{ 213 Name: "aws", 214 Type: "aws", 215 }, 216 }, 217 }, 218 false, 219 }, 220 221 { 222 "compile-dep-infra", 223 "child", 224 &File{ 225 Application: &Application{ 226 Name: "child", 227 Type: "bar", 228 Detect: true, 229 }, 230 Project: &Project{ 231 Name: "bar", 232 Infrastructure: "google", 233 }, 234 Infrastructure: []*Infrastructure{ 235 &Infrastructure{ 236 Name: "google", 237 Type: "google", 238 }, 239 }, 240 }, 241 false, 242 }, 243 } 244 245 for _, tc := range cases { 246 t.Logf("Testing: %s", tc.Dir) 247 248 // We wrap this in a function just so we can use defers 249 func() { 250 opts := testCompileOpts(t) 251 defer os.RemoveAll(opts.Dir) 252 f := testFile(t, tc.Dir) 253 f.initID() 254 f.loadID() 255 defer f.resetID() 256 257 if tc.File != nil { 258 tc.File.ID = f.ID 259 tc.File.Path = f.Path 260 tc.File.Imports = f.Imports 261 } 262 263 c, err := testCompiler(t, opts).Compile(f) 264 if (err != nil) != tc.Err { 265 t.Fatalf("err: %s\n\n%s", tc.Dir, err) 266 } 267 if err == nil && c == nil { 268 t.Fatalf("bad: compiled is nil\n\n%s", tc.Dir) 269 } 270 if err != nil { 271 return 272 } 273 274 // Get the root file. If we specified a name, then find 275 // the dependent application with that name since that is 276 // what we're comparing against. 277 actual := c.File 278 if tc.Name != "" { 279 actual = nil 280 c.Graph.Walk(func(raw dag.Vertex) error { 281 v := raw.(*CompiledGraphVertex) 282 if v.File.Application == nil { 283 return nil 284 } 285 if v.File.Application.Name == tc.Name { 286 actual = v.File 287 } 288 return nil 289 }) 290 291 if actual == nil { 292 t.Fatalf("err: %s\n\n%s not found in graph", tc.Dir, tc.Name) 293 } 294 295 // For child files, we just clear these out. 296 actual.ID = "" 297 actual.Path = "" 298 actual.Source = "" 299 actual.Imports = nil 300 tc.File.ID = actual.ID 301 tc.File.Path = actual.Path 302 tc.File.Imports = actual.Imports 303 tc.File.Source = actual.Source 304 } 305 306 if !reflect.DeepEqual(actual, tc.File) { 307 t.Fatalf("err: %s\n\n%#v\n\n%#v", tc.Dir, actual, tc.File) 308 } 309 }() 310 } 311 } 312 313 func TestCompileID(t *testing.T) { 314 opts := testCompileOpts(t) 315 defer os.RemoveAll(opts.Dir) 316 f := testFile(t, "compile-id") 317 defer f.resetID() 318 319 c, err := testCompiler(t, opts).Compile(f) 320 if err != nil { 321 t.Fatalf("err: %s", err) 322 } 323 324 if f.ID == "" { 325 t.Fatalf("ID should not be blank") 326 } 327 if f.ID != c.File.ID { 328 t.Fatalf("%s != %s", f.ID, c.File.ID) 329 } 330 } 331 332 func TestCompileID_existing(t *testing.T) { 333 opts := testCompileOpts(t) 334 defer os.RemoveAll(opts.Dir) 335 f := testFile(t, "compile-id-exists") 336 if f.ID == "" { 337 t.Fatalf("ID should not be blank") 338 } 339 340 copyId := f.ID 341 c, err := testCompiler(t, opts).Compile(f) 342 if err != nil { 343 t.Fatalf("err: %s", err) 344 } 345 346 if copyId != c.File.ID { 347 t.Fatalf("%s != %s", f.ID, c.File.ID) 348 } 349 } 350 351 func TestLoadCompile_new(t *testing.T) { 352 path := filepath.Join("./test-fixtures", "load-new") 353 _, err := LoadCompiled(path) 354 if err == nil { 355 t.Fatal("should error") 356 } 357 } 358 359 func testCompileCompare(t *testing.T, c *Compiled, expected string) { 360 actual := strings.TrimSpace(c.String()) 361 expected = strings.TrimSpace(fmt.Sprintf(expected, c.File.Path)) 362 if actual != expected { 363 t.Fatalf("bad:\n\n%s\n\n%s", actual, expected) 364 } 365 } 366 367 func testCompileMarshal(t *testing.T, original *Compiled, dir string) { 368 c, err := LoadCompiled(dir) 369 if err != nil { 370 t.Fatalf("err loading compiled: %s", err) 371 } 372 373 if c.String() != original.String() { 374 t.Fatalf("bad:\n\n%s\n\n%s", c, original) 375 } 376 } 377 378 func testCompileOpts(t *testing.T) *CompileOpts { 379 dir, err := ioutil.TempDir("", "otto-") 380 if err != nil { 381 t.Fatalf("err: %s", err) 382 } 383 384 return &CompileOpts{ 385 Dir: dir, 386 Loader: nil, 387 } 388 } 389 390 func testCompiler(t *testing.T, opts *CompileOpts) *Compiler { 391 c, err := NewCompiler(opts) 392 if err != nil { 393 t.Fatalf("err: %s", err) 394 } 395 396 return c 397 } 398 399 func testFile(t *testing.T, dir string) *File { 400 path := filepath.Join("./test-fixtures", dir, "Appfile") 401 f, err := ParseFile(path) 402 if err != nil { 403 t.Fatalf("err: %s\n\n%s", path, err) 404 } 405 406 return f 407 } 408 409 const testCompileBasicStr = ` 410 Compiled Appfile: %s 411 412 Dep Graph: 413 foo 414 ` 415 416 const testCompileDepsStr = ` 417 Compiled Appfile: %s 418 419 Dep Graph: 420 bar 421 foo 422 bar 423 ` 424 425 const testCompileDepGitStr = ` 426 Compiled Appfile: %s 427 428 Dep Graph: 429 bar 430 foo 431 bar 432 ` 433 434 const testCompileMultiDepStr = ` 435 Compiled Appfile: %s 436 437 Dep Graph: 438 bar 439 baz 440 foo 441 bar 442 baz 443 `