github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/extension/otto/otto_test.go (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package otto_test 17 18 import ( 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "regexp" 23 "strings" 24 "time" 25 26 . "github.com/onsi/ginkgo" 27 . "github.com/onsi/gomega" 28 "github.com/onsi/gomega/ghttp" 29 ottopkg "github.com/robertkrimen/otto" 30 31 "github.com/cloudwan/gohan/db/transaction" 32 "github.com/cloudwan/gohan/extension" 33 "github.com/cloudwan/gohan/extension/otto" 34 "github.com/cloudwan/gohan/schema" 35 "github.com/cloudwan/gohan/server/middleware" 36 "github.com/cloudwan/gohan/server/resources" 37 "github.com/cloudwan/gohan/util" 38 ) 39 40 func newEnvironment() *otto.Environment { 41 timelimit := time.Duration(1) * time.Second 42 43 return otto.NewEnvironment("otto_test", 44 testDB, &middleware.FakeIdentity{}, timelimit, testSync) 45 } 46 47 var _ = Describe("Otto extension manager", func() { 48 var ( 49 manager *schema.Manager 50 environmentManager *extension.Manager 51 ) 52 53 BeforeEach(func() { 54 manager = schema.GetManager() 55 environmentManager = extension.GetManager() 56 }) 57 58 AfterEach(func() { 59 tx, err := testDB.Begin() 60 Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.") 61 defer tx.Close() 62 for _, schema := range schema.GetManager().Schemas() { 63 if whitelist[schema.ID] { 64 continue 65 } 66 err = clearTable(tx, schema) 67 Expect(err).ToNot(HaveOccurred(), "Failed to clear table.") 68 } 69 err = tx.Commit() 70 Expect(err).ToNot(HaveOccurred(), "Failed to commite transaction.") 71 72 extension.ClearManager() 73 }) 74 75 Describe("Loading an extension", func() { 76 Context("When extension is not a valid JavaScript", func() { 77 It("returns a meaningful compilation error", func() { 78 goodExtension, err := schema.NewExtension(map[string]interface{}{ 79 "id": "good_extension", 80 "code": `gohan_register_handler("test_event", function(context) {});`, 81 "path": ".*", 82 }) 83 Expect(err).ToNot(HaveOccurred()) 84 goodExtension.URL = "good_extension.js" 85 86 badExtension, err := schema.NewExtension(map[string]interface{}{ 87 "id": "bad_extension", 88 "code": `gohan_register_handler("test_event", function(context {});`, 89 "path": ".*", 90 }) 91 Expect(err).ToNot(HaveOccurred()) 92 badExtension.URL = "bad_extension.js" 93 94 extensions := []*schema.Extension{goodExtension, badExtension} 95 env := newEnvironment() 96 err = env.LoadExtensionsForPath(extensions, "test_path") 97 Expect(err).To(HaveOccurred(), "Expected compilation errors.") 98 99 pattern := regexp.MustCompile(`^(?P<file>[^:]+).*Line\s(?P<line>\d+).*`) 100 match := pattern.FindStringSubmatch(err.Error()) 101 Expect(len(match)).To(Equal(3)) 102 103 groups := make(map[string]string) 104 for i, name := range pattern.SubexpNames() { 105 groups[name] = match[i] 106 } 107 108 Expect(groups).To(HaveKeyWithValue("file", "bad_extension.js")) 109 Expect(groups).To(HaveKeyWithValue("line", "1")) 110 }) 111 }) 112 113 Context("When extension URL uses file:// protocol", func() { 114 It("should read the file and run the extension", func() { 115 extension, err := schema.NewExtension(map[string]interface{}{ 116 "id": "test_extension", 117 "url": "file://../tests/sample_extension.js", 118 "path": ".*", 119 }) 120 Expect(err).ToNot(HaveOccurred()) 121 122 extensions := []*schema.Extension{extension} 123 env := newEnvironment() 124 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 125 126 context := map[string]interface{}{ 127 "id": "test", 128 } 129 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 130 Expect(context["resp"]).ToNot(BeNil()) 131 }) 132 }) 133 134 Context("When extension URL uses http:// protocol", func() { 135 It("should download and run the extension", func() { 136 server := ghttp.NewServer() 137 code := ` 138 gohan_register_handler("test_event", function(context){ 139 context.resp = "Hello"; 140 }); 141 ` 142 server.AppendHandlers(ghttp.CombineHandlers( 143 ghttp.VerifyRequest("GET", "/extension.js"), 144 ghttp.RespondWith(200, code), 145 )) 146 147 extension, err := schema.NewExtension(map[string]interface{}{ 148 "id": "test_extension", 149 "url": server.URL() + "/extension.js", 150 "path": ".*", 151 }) 152 Expect(err).ToNot(HaveOccurred()) 153 extensions := []*schema.Extension{extension} 154 env := newEnvironment() 155 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 156 157 context := map[string]interface{}{ 158 "id": "test", 159 } 160 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 161 Expect(context["resp"]).ToNot(BeNil()) 162 server.Close() 163 }) 164 }) 165 }) 166 167 Describe("Running an extension", func() { 168 Context("When a runtime error occurs", func() { 169 It("should return a meaningful error", func() { 170 goodExtension, err := schema.NewExtension(map[string]interface{}{ 171 "id": "good_extension", 172 "code": `gohan_register_handler("test_event", function(context) {});`, 173 "path": ".*", 174 }) 175 Expect(err).ToNot(HaveOccurred()) 176 goodExtension.URL = "good_extension.js" 177 178 badExtension, err := schema.NewExtension(map[string]interface{}{ 179 "id": "bad_extension", 180 "code": `gohan_register_handler("test_event", function foo(context) { 181 var a = 5; 182 console.log(b); 183 });`, 184 "path": ".*", 185 }) 186 Expect(err).ToNot(HaveOccurred()) 187 badExtension.URL = "bad_extension.js" 188 189 extensions := []*schema.Extension{goodExtension, badExtension} 190 env := newEnvironment() 191 err = env.LoadExtensionsForPath(extensions, "test_path") 192 193 context := map[string]interface{}{ 194 "id": "test", 195 } 196 err = env.HandleEvent("test_event", context) 197 Expect(err).To(HaveOccurred()) 198 199 Expect(regexp.MatchString(`ReferenceError:\s'b'`, err.Error())).To(BeTrue()) 200 201 pattern := regexp.MustCompile(`at\s(?P<function>\w+)\s\((?P<file>.*?):(?P<line>\d+).*?\)`) 202 match := pattern.FindStringSubmatch(err.Error()) 203 Expect(len(match)).To(Equal(4)) 204 //FIXME(timorl): because of a throw bug in otto we cannot expect more meaningful errors 205 }) 206 }) 207 208 Context("When a nil value is passed", func() { 209 It("should be represented as null in the virtual machine", func() { 210 goodExtension, err := schema.NewExtension(map[string]interface{}{ 211 "id": "good_extension", 212 "code": `gohan_register_handler("test_event", function(context) { 213 if (context.nulo === null) { 214 context.respondo = "verdo" 215 } else { 216 context.respondo = "ne verdo" 217 } 218 });`, 219 "path": ".*", 220 }) 221 Expect(err).ToNot(HaveOccurred()) 222 goodExtension.URL = "good_extension.js" 223 224 extensions := []*schema.Extension{goodExtension} 225 env := newEnvironment() 226 env.LoadExtensionsForPath(extensions, "test_path") 227 228 context := map[string]interface{}{ 229 "id": "test", 230 "nulo": nil, 231 } 232 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 233 Expect(context).To(HaveKeyWithValue("respondo", "verdo")) 234 }) 235 }) 236 237 Context("When extension is running too long", func() { 238 It("should be aborted in the middle of extension", func() { 239 timeoutExtension, err := schema.NewExtension(map[string]interface{}{ 240 "id": "timeout_extension", 241 "code": `gohan_register_handler("test_event", function(context) { 242 while(true) { 243 // busy loop, but ok for this test 244 var i = 1; 245 }; 246 });`, 247 "path": ".*", 248 }) 249 Expect(err).ToNot(HaveOccurred()) 250 timeoutExtension.URL = "timeout_extension.js" 251 252 extensions := []*schema.Extension{timeoutExtension} 253 env := newEnvironment() 254 env.LoadExtensionsForPath(extensions, "test_path") 255 256 context := map[string]interface{}{ 257 "id": "test", 258 } 259 err = env.HandleEvent("test_event", context) 260 Expect(err).To(MatchError(ContainSubstring("exceed timeout for extension execution"))) 261 }) 262 }) 263 }) 264 265 Describe("Using gohan_http builtin", func() { 266 Context("When the destination is reachable", func() { 267 It("Should return the contents", func() { 268 server := ghttp.NewServer() 269 server.AppendHandlers(ghttp.CombineHandlers( 270 ghttp.VerifyRequest("GET", "/contents"), 271 ghttp.RespondWith(200, "HELLO"), 272 )) 273 274 extension, err := schema.NewExtension(map[string]interface{}{ 275 "id": "test_extension", 276 "code": ` 277 gohan_register_handler("test_event", function(context){ 278 context.resp = gohan_http('GET', '` + server.URL() + `/contents', {}, {}); 279 });`, 280 "path": ".*", 281 }) 282 Expect(err).ToNot(HaveOccurred()) 283 extensions := []*schema.Extension{extension} 284 env := newEnvironment() 285 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 286 287 context := map[string]interface{}{ 288 "id": "test", 289 } 290 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 291 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("status_code", "200"))) 292 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("body", "HELLO"))) 293 server.Close() 294 }) 295 }) 296 297 Context("When the destination is not reachable", func() { 298 It("Should return the error", func() { 299 extension, err := schema.NewExtension(map[string]interface{}{ 300 "id": "test_extension", 301 "code": ` 302 gohan_register_handler("test_event", function(context){ 303 context.resp = gohan_http('GET', 'http://localhost:38000/contents', {}, {}); 304 });`, 305 "path": ".*", 306 }) 307 Expect(err).ToNot(HaveOccurred()) 308 extensions := []*schema.Extension{extension} 309 env := newEnvironment() 310 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 311 312 context := map[string]interface{}{ 313 "id": "test", 314 } 315 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 316 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("status", "err"))) 317 Expect(context).To(HaveKeyWithValue("resp", HaveKey("error"))) 318 }) 319 }) 320 321 Context("When the content type is not specified", func() { 322 It("Should post the data as a JSON document", func() { 323 server := ghttp.NewServer() 324 server.AppendHandlers(ghttp.CombineHandlers( 325 ghttp.VerifyRequest("POST", "/contents"), 326 ghttp.RespondWith(200, "HELLO"), 327 ghttp.VerifyJSON("{\"data\": \"posted_data\"}"), 328 )) 329 330 extension, err := schema.NewExtension(map[string]interface{}{ 331 "id": "test_extension", 332 "code": ` 333 gohan_register_handler("test_event", function(context){ 334 var d = { data: 'posted_data' } 335 context.resp = gohan_http('POST', '` + server.URL() + `/contents', {}, d); 336 });`, 337 "path": ".*", 338 }) 339 340 Expect(err).ToNot(HaveOccurred()) 341 extensions := []*schema.Extension{extension} 342 env := newEnvironment() 343 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 344 345 context := map[string]interface{}{ 346 "id": "test", 347 } 348 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 349 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("status_code", "200"))) 350 server.Close() 351 }) 352 }) 353 354 Context("When the content type is text/plain", func() { 355 It("Should post the data as plain text", func() { 356 357 verifyPosted := func(expected string) http.HandlerFunc { 358 return func(w http.ResponseWriter, req *http.Request) { 359 body, _ := ioutil.ReadAll(req.Body) 360 req.Body.Close() 361 actual := strings.Trim(string(body), "\"") 362 Ω(actual).Should(Equal(expected), "Post data Mismatch") 363 } 364 } 365 366 postedString := "posted_string" 367 server := ghttp.NewServer() 368 server.AppendHandlers(ghttp.CombineHandlers( 369 ghttp.VerifyRequest("POST", "/contents"), 370 ghttp.RespondWith(200, "HELLO"), 371 verifyPosted(postedString), 372 )) 373 374 extension, err := schema.NewExtension(map[string]interface{}{ 375 "id": "test_extension", 376 "code": ` 377 gohan_register_handler("test_event", function(context){ 378 var d = "` + postedString + `" 379 context.resp = gohan_http('POST', '` + server.URL() + `/contents', {'Content-Type': 'text/plain' }, d); 380 });`, 381 "path": ".*", 382 }) 383 Expect(err).ToNot(HaveOccurred()) 384 extensions := []*schema.Extension{extension} 385 env := newEnvironment() 386 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 387 388 context := map[string]interface{}{ 389 "id": "test", 390 } 391 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 392 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("status_code", "200"))) 393 server.Close() 394 }) 395 }) 396 }) 397 398 Describe("Using gohan_raw_http builtin", func() { 399 Context("When the destination is reachable", func() { 400 It("Should return the contents", func() { 401 server := ghttp.NewServer() 402 server.AppendHandlers(ghttp.CombineHandlers( 403 ghttp.VerifyRequest("POST", "/contents"), 404 ghttp.VerifyHeader(http.Header{"Content-Type": []string{"application/json"}}), 405 ghttp.VerifyBody([]byte("{\"input\":\"value\"}")), 406 ghttp.RespondWith(200, []byte("{\"output\":\"value\"}")), 407 )) 408 409 extension, err := schema.NewExtension(map[string]interface{}{ 410 "id": "test_extension", 411 "code": ` 412 gohan_register_handler("test_event", function(context){ 413 var headers = { 414 "content-type": "application/json" 415 }; 416 var body = JSON.stringify({ 417 input: "value" 418 }); 419 context.response = gohan_raw_http('POST', '` + server.URL() + `/contents', headers, body); 420 421 });`, 422 "path": ".*", 423 }) 424 Expect(err).ToNot(HaveOccurred()) 425 extensions := []*schema.Extension{extension} 426 env := newEnvironment() 427 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 428 429 context := map[string]interface{}{ 430 "id": "test", 431 } 432 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 433 Expect(context).To(HaveKeyWithValue("response", HaveKeyWithValue("status_code", 200))) 434 Expect(context).To(HaveKeyWithValue("response", HaveKeyWithValue("body", "{\"output\":\"value\"}"))) 435 server.Close() 436 }) 437 438 It("Should not follow redirect", func() { 439 server := ghttp.NewServer() 440 server.AppendHandlers(ghttp.CombineHandlers( 441 ghttp.VerifyRequest("POST", "/contents"), 442 ghttp.VerifyHeader(http.Header{ 443 "Content-Type": []string{"application/json"}, 444 }), 445 ghttp.VerifyBody([]byte("{\"input\":\"value\"}")), 446 ghttp.RespondWith(302, []byte(""), http.Header{ 447 "Location": []string{server.URL() + "/redirect"}, 448 }), 449 )) 450 451 extension, err := schema.NewExtension(map[string]interface{}{ 452 "id": "test_extension", 453 "code": ` 454 gohan_register_handler("test_event", function(context){ 455 var headers = { 456 "content-type": "application/json" 457 }; 458 var body = JSON.stringify({ 459 input: "value" 460 }); 461 context.response = gohan_raw_http('POST', '` + server.URL() + `/contents', headers, body); 462 });`, 463 "path": ".*", 464 }) 465 Expect(err).ToNot(HaveOccurred()) 466 extensions := []*schema.Extension{extension} 467 env := newEnvironment() 468 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 469 470 context := map[string]interface{}{ 471 "id": "test", 472 } 473 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 474 Expect(context).To(HaveKeyWithValue("response", HaveKeyWithValue("status_code", 302))) 475 Expect(context).To(HaveKeyWithValue("response", HaveKeyWithValue("body", ""))) 476 server.Close() 477 }) 478 }) 479 }) 480 481 Describe("Using gohan_config builtin", func() { 482 It("Should return correct value", func() { 483 extension, err := schema.NewExtension(map[string]interface{}{ 484 "id": "test_extension", 485 "code": ` 486 gohan_register_handler("test_event", function(context){ 487 context.resp = gohan_config("database", ""); 488 });`, 489 "path": ".*", 490 }) 491 Expect(err).ToNot(HaveOccurred()) 492 extensions := []*schema.Extension{extension} 493 env := newEnvironment() 494 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 495 496 context := map[string]interface{}{} 497 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 498 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("connection", "test.db"))) 499 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("type", "sqlite3"))) 500 }) 501 502 It("Should return correct value - key not found", func() { 503 extension, err := schema.NewExtension(map[string]interface{}{ 504 "id": "test_extension", 505 "code": ` 506 gohan_register_handler("test_event", function(context){ 507 context.resp = gohan_config("does not exist", false); 508 });`, 509 "path": ".*", 510 }) 511 Expect(err).ToNot(HaveOccurred()) 512 extensions := []*schema.Extension{extension} 513 env := newEnvironment() 514 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 515 516 context := map[string]interface{}{} 517 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 518 Expect(context).To(HaveKeyWithValue("resp", BeFalse())) 519 }) 520 }) 521 522 Describe("Using gohan database manipulation builtins", func() { 523 var ( 524 adminAuth schema.Authorization 525 auth schema.Authorization 526 context middleware.Context 527 schemaID string 528 path string 529 action string 530 currentSchema *schema.Schema 531 extensions []*schema.Extension 532 env extension.Environment 533 events map[string]string 534 535 network1 map[string]interface{} 536 network2 map[string]interface{} 537 subnet1 map[string]interface{} 538 ) 539 540 BeforeEach(func() { 541 adminAuth = schema.NewAuthorization(adminTenantID, "admin", adminTokenID, []string{"admin"}, nil) 542 auth = adminAuth 543 544 context = middleware.Context{} 545 546 events = map[string]string{} 547 548 network1 = map[string]interface{}{ 549 "id": "test1", 550 "name": "Rohan", 551 "description": "The proud horsemasters", 552 "tenant_id": adminTenantID, 553 "providor_networks": map[string]interface{}{}, 554 "route_targets": []interface{}{}, 555 "shared": false, 556 } 557 network2 = map[string]interface{}{ 558 "id": "test2", 559 "name": "Gondor", 560 "description": "Once glorious empire", 561 "tenant_id": adminTenantID, 562 "providor_networks": map[string]interface{}{}, 563 "route_targets": []interface{}{}, 564 "shared": false, 565 } 566 subnet1 = map[string]interface{}{ 567 "id": "test3", 568 "name": "Minas Tirith", 569 "tenant_id": adminTenantID, 570 "cidr": "10.10.0.0/16", 571 } 572 }) 573 574 JustBeforeEach(func() { 575 var ok bool 576 currentSchema, ok = manager.Schema(schemaID) 577 Expect(ok).To(BeTrue()) 578 579 path = currentSchema.GetPluralURL() 580 581 policy, role := manager.PolicyValidate(action, path, auth) 582 Expect(policy).NotTo(BeNil()) 583 context["policy"] = policy 584 context["role"] = role 585 context["tenant_id"] = auth.TenantID() 586 context["tenant_name"] = auth.TenantName() 587 context["auth_token"] = auth.AuthToken() 588 context["catalog"] = auth.Catalog() 589 context["auth"] = auth 590 context["identity_service"] = &middleware.FakeIdentity{} 591 592 env = newEnvironment() 593 environmentManager.RegisterEnvironment(schemaID, env) 594 extensions = []*schema.Extension{} 595 for event, javascript := range events { 596 extension, err := schema.NewExtension(map[string]interface{}{ 597 "id": event + "_extension", 598 "code": `gohan_register_handler("` + event + `", function(context) {` + javascript + `});`, 599 "path": path, 600 }) 601 Expect(err).ToNot(HaveOccurred()) 602 extensions = append(extensions, extension) 603 } 604 Expect(env.LoadExtensionsForPath(extensions, path)).To(Succeed()) 605 }) 606 607 AfterEach(func() { 608 tx, err := testDB.Begin() 609 Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.") 610 environmentManager.UnRegisterEnvironment(schemaID) 611 defer tx.Close() 612 for _, schema := range schema.GetManager().Schemas() { 613 if whitelist[schema.ID] { 614 continue 615 } 616 err = clearTable(tx, schema) 617 Expect(err).ToNot(HaveOccurred(), "Failed to clear table.") 618 } 619 err = tx.Commit() 620 Expect(err).ToNot(HaveOccurred(), "Failed to commite transaction.") 621 }) 622 623 Describe("Using gohan_db_* builtins", func() { 624 BeforeEach(func() { 625 schemaID = "network" 626 action = "read" 627 }) 628 629 Context("When given a transaction", func() { 630 It("Correctly handles CRUD operations", func() { 631 tx, err := testDB.Begin() 632 Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.") 633 defer tx.Commit() 634 635 extension, err := schema.NewExtension(map[string]interface{}{ 636 "id": "test_extension", 637 "code": ` 638 gohan_register_handler("test_event", function(context){ 639 gohan_db_create(context.transaction, 640 'network', { 641 'id':'test1', 642 'name': 'name', 643 'description': 'description', 644 'providor_networks': {}, 645 'route_targets': [], 646 'shared': false, 647 'tenant_id': 'admin'}); 648 context.network = gohan_db_fetch(context.transaction, 'network', 'test1', 'admin'); 649 gohan_db_update(context.transaction, 650 'network', {'id':'test1', 'name': 'name_updated', 'tenant_id': 'admin'}); 651 context.networks = gohan_db_list(context.transaction, 'network', {}); 652 context.networks2 = gohan_db_list(context.transaction, 'network', {'shared': false}); 653 gohan_db_delete(context.transaction, 'network', 'test1'); 654 context.networks3 = gohan_db_list(context.transaction, 'network', {}); 655 });`, 656 "path": ".*", 657 }) 658 Expect(err).ToNot(HaveOccurred()) 659 extensions := []*schema.Extension{extension} 660 env := newEnvironment() 661 env.LoadExtensionsForPath(extensions, "test_path") 662 663 context := map[string]interface{}{ 664 "id": "test", 665 "transaction": tx, 666 } 667 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 668 Expect(context["network"]).ToNot(BeNil()) 669 Expect(context["networks"]).ToNot(BeNil()) 670 Expect(context["networks2"]).ToNot(BeNil()) 671 }) 672 }) 673 674 Context("When given no transaction", func() { 675 It("Correctly handles CRUD operations", func() { 676 tx, err := testDB.Begin() 677 Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.") 678 defer tx.Commit() 679 680 extension, err := schema.NewExtension(map[string]interface{}{ 681 "id": "test_extension", 682 "code": ` 683 gohan_register_handler("test_event", function(context){ 684 gohan_db_create(context.transaction, 685 'network', { 686 'id':'test1', 687 'name': 'name', 688 'description': 'description', 689 'providor_networks': {}, 690 'route_targets': [], 691 'shared': false, 692 'tenant_id': 'admin'}); 693 context.network = gohan_db_fetch(context.transaction, 'network', 'test1', 'admin'); 694 gohan_db_update(context.transaction, 695 'network', {'id':'test1', 'name': 'name_updated', 'tenant_id': 'admin'}); 696 context.networks = gohan_db_list(context.transaction, 'network', {}); 697 gohan_db_delete(context.transaction, 'network', 'test1'); 698 context.networks2 = gohan_db_list(context.transaction, 'network', {}); 699 });`, 700 "path": ".*", 701 }) 702 Expect(err).ToNot(HaveOccurred()) 703 extensions := []*schema.Extension{extension} 704 env := newEnvironment() 705 env.LoadExtensionsForPath(extensions, "test_path") 706 707 context := map[string]interface{}{ 708 "id": "test", 709 "transaction": nil, 710 } 711 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 712 Expect(context["network"]).ToNot(BeNil()) 713 Expect(context["networks"]).ToNot(BeNil()) 714 }) 715 }) 716 }) 717 718 Describe("Using gohan_db_transaction", func() { 719 BeforeEach(func() { 720 schemaID = "network" 721 action = "read" 722 }) 723 724 Context("When given a transaction", func() { 725 It("Correctly handles CRUD operations", func() { 726 extension, err := schema.NewExtension(map[string]interface{}{ 727 "id": "test_extension", 728 "code": ` 729 gohan_register_handler("test_event", function(context){ 730 var tx = gohan_db_transaction(); 731 gohan_db_create(tx, 732 'network', { 733 'id':'test1', 734 'name': 'name', 735 'description': 'description', 736 'providor_networks': {}, 737 'route_targets': [], 738 'shared': false, 739 'tenant_id': 'admin'}); 740 context.network = gohan_db_fetch(tx, 'network', 'test1', 'admin'); 741 gohan_db_update(tx, 742 'network', {'id':'test1', 'name': 'name_updated', 'tenant_id': 'admin'}); 743 context.networks = gohan_db_list(tx, 'network', {}); 744 gohan_db_delete(tx, 'network', 'test1'); 745 context.networks2 = gohan_db_list(tx, 'network', {}); 746 tx.Commit(); 747 });`, 748 "path": ".*", 749 }) 750 Expect(err).ToNot(HaveOccurred()) 751 extensions := []*schema.Extension{extension} 752 env := newEnvironment() 753 env.LoadExtensionsForPath(extensions, "test_path") 754 755 context := map[string]interface{}{ 756 "id": "test", 757 } 758 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 759 Expect(context["network"]).ToNot(BeNil()) 760 Expect(context["networks"]).ToNot(BeNil()) 761 }) 762 }) 763 }) 764 765 Describe("Using chaining builtins", func() { 766 BeforeEach(func() { 767 schemaID = "network" 768 }) 769 770 Describe("Using gohan_model_list", func() { 771 var ( 772 tx transaction.Transaction 773 ) 774 775 BeforeEach(func() { 776 resource, err := manager.LoadResource(schemaID, network1) 777 Expect(err).NotTo(HaveOccurred()) 778 tx, err = testDB.Begin() 779 Expect(err).NotTo(HaveOccurred()) 780 defer tx.Close() 781 Expect(tx.Create(resource)).To(Succeed()) 782 Expect(tx.Commit()).To(Succeed()) 783 784 action = "read" 785 }) 786 787 Describe("When invoked correctly", func() { 788 Context("With transaction", func() { 789 BeforeEach(func() { 790 events["test"] = ` 791 context.networks = gohan_model_list(context, 'network', {});` 792 }) 793 794 It("Correctly lists elements", func() { 795 tx, err := testDB.Begin() 796 Expect(err).NotTo(HaveOccurred()) 797 defer tx.Close() 798 context["transaction"] = tx 799 800 Expect(env.HandleEvent("test", context)).To(Succeed()) 801 Expect(tx.Commit()).To(Succeed()) 802 Expect(context).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network1)))) 803 }) 804 }) 805 806 Context("With a string filter", func() { 807 BeforeEach(func() { 808 events["test"] = ` 809 console.log(context) 810 context.networks = gohan_model_list(context, 'network', {'id': 'test1'});` 811 }) 812 813 It("Correctly lists elements", func() { 814 tx, err := testDB.Begin() 815 Expect(err).NotTo(HaveOccurred()) 816 defer tx.Close() 817 context["transaction"] = tx 818 Expect(env.HandleEvent("test", context)).To(Succeed()) 819 Expect(context).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network1)))) 820 }) 821 }) 822 823 Context("With an array filter", func() { 824 BeforeEach(func() { 825 events["test"] = ` 826 context.networks = gohan_model_list(context, 'network', {'id': ['test1']});` 827 }) 828 829 It("Correctly lists elements", func() { 830 tx, err := testDB.Begin() 831 Expect(err).NotTo(HaveOccurred()) 832 defer tx.Close() 833 context["transaction"] = tx 834 Expect(env.HandleEvent("test", context)).To(Succeed()) 835 Expect(context).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network1)))) 836 }) 837 }) 838 839 Context("With chained exception", func() { 840 BeforeEach(func() { 841 events["test"] = ` 842 context.networks = gohan_model_list(context, 'network', {});` 843 events["post_list_in_transaction"] = ` 844 throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);` 845 }) 846 847 It("Returns the proper error", func() { 848 tx, err := testDB.Begin() 849 Expect(err).NotTo(HaveOccurred()) 850 defer tx.Close() 851 context["transaction"] = tx 852 Expect(env.HandleEvent("test", context)).To(Succeed()) 853 Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente"))) 854 Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente")) 855 }) 856 }) 857 858 Context("With internal resource manipulation error", func() { 859 BeforeEach(func() { 860 events["test"] = ` 861 context.networks = gohan_model_list(context, 'network', {});` 862 events["post_list_in_transaction"] = ` 863 delete context.response;` 864 }) 865 866 It("Returns the proper error", func() { 867 tx, err := testDB.Begin() 868 Expect(err).NotTo(HaveOccurred()) 869 defer tx.Close() 870 context["transaction"] = tx 871 err = env.HandleEvent("test", context) 872 Expect(err).To(MatchError(ContainSubstring("No response"))) 873 }) 874 }) 875 }) 876 877 Describe("When invoked incorrectly", func() { 878 Context("With wrong number of arguments", func() { 879 BeforeEach(func() { 880 events["test"] = ` 881 context.networks = gohan_model_list();` 882 }) 883 884 It("Returns the proper error", func() { 885 err := env.HandleEvent("test", context) 886 Expect(err).To(MatchError(ContainSubstring("arguments"))) 887 }) 888 }) 889 890 Context("With wrong schema ID", func() { 891 BeforeEach(func() { 892 events["test"] = ` 893 context.networks = gohan_model_list(context, 'netwerk', {});` 894 }) 895 896 It("Returns the proper error", func() { 897 err := env.HandleEvent("test", context) 898 Expect(err).To(MatchError(ContainSubstring("Unknown schema"))) 899 }) 900 }) 901 902 Context("With wrong filter", func() { 903 BeforeEach(func() { 904 events["test"] = ` 905 context.networks = gohan_model_list(context, 'network', {'id': context.policy});` 906 }) 907 908 It("Returns the proper error", func() { 909 err := env.HandleEvent("test", context) 910 Expect(err).To(MatchError(ContainSubstring("not a string"))) 911 }) 912 }) 913 914 Context("With wrong filter but array", func() { 915 BeforeEach(func() { 916 events["test"] = ` 917 context.networks = gohan_model_list(context, 'network', {'id': [context.policy]});` 918 }) 919 920 It("Returns the proper error", func() { 921 err := env.HandleEvent("test", context) 922 Expect(err).To(MatchError(ContainSubstring("not a string"))) 923 }) 924 }) 925 }) 926 }) 927 928 Describe("Using gohan_model_fetch", func() { 929 var ( 930 tx transaction.Transaction 931 ) 932 933 BeforeEach(func() { 934 resource, err := manager.LoadResource(schemaID, network1) 935 Expect(err).NotTo(HaveOccurred()) 936 tx, err = testDB.Begin() 937 Expect(err).NotTo(HaveOccurred()) 938 defer tx.Close() 939 Expect(tx.Create(resource)).To(Succeed()) 940 Expect(tx.Commit()).To(Succeed()) 941 942 action = "read" 943 }) 944 945 Describe("When invoked correctly", func() { 946 Context("With transaction", func() { 947 BeforeEach(func() { 948 events["test"] = ` 949 context.network = gohan_model_fetch(context, 'network', 'test1', null); 950 ` 951 }) 952 953 It("Correctly fetches the element", func() { 954 tx, err := testDB.Begin() 955 Expect(err).NotTo(HaveOccurred()) 956 defer tx.Close() 957 context["transaction"] = tx 958 959 Expect(env.HandleEvent("test", context)).To(Succeed()) 960 Expect(tx.Commit()).To(Succeed()) 961 By(fmt.Sprintf("%v", context)) 962 resultRaw, ok := context["network"] 963 Expect(ok).To(BeTrue()) 964 _, ok = resultRaw.(map[string]interface{}) 965 Expect(ok).To(BeTrue()) 966 }) 967 }) 968 969 Context("Asking for a nonexistent resource", func() { 970 BeforeEach(func() { 971 events["test"] = ` 972 context.network = gohan_model_fetch(context, 'network', 'neEstas', null);` 973 }) 974 975 It("Returns the not found error", func() { 976 tx, err := testDB.Begin() 977 Expect(err).NotTo(HaveOccurred()) 978 defer tx.Close() 979 context["transaction"] = tx 980 Expect(env.HandleEvent("test", context)).To(Succeed()) 981 Expect(context["exception"]).To(HaveKeyWithValue("problem", int(resources.NotFound))) 982 Expect(context["exception_message"]).To(ContainSubstring("ResourceException")) 983 }) 984 }) 985 986 Context("With chained exception", func() { 987 BeforeEach(func() { 988 events["test"] = ` 989 context.network = gohan_model_fetch(context, 'network', 'test1', null);` 990 events["post_show_in_transaction"] = ` 991 throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);` 992 }) 993 994 It("Returns the proper error", func() { 995 tx, err := testDB.Begin() 996 Expect(err).NotTo(HaveOccurred()) 997 defer tx.Close() 998 context["transaction"] = tx 999 Expect(env.HandleEvent("test", context)).To(Succeed()) 1000 Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente"))) 1001 Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente")) 1002 }) 1003 }) 1004 1005 Context("With internal resource manipulation error", func() { 1006 BeforeEach(func() { 1007 events["test"] = ` 1008 context.network = gohan_model_fetch(context, 'network', 'test1', null);` 1009 events["post_show_in_transaction"] = ` 1010 delete context.response;` 1011 }) 1012 1013 It("Returns the proper error", func() { 1014 tx, err := testDB.Begin() 1015 Expect(err).NotTo(HaveOccurred()) 1016 defer tx.Close() 1017 context["transaction"] = tx 1018 err = env.HandleEvent("test", context) 1019 Expect(err).To(MatchError(ContainSubstring("No response"))) 1020 }) 1021 }) 1022 }) 1023 1024 Describe("When invoked incorrectly", func() { 1025 Context("With wrong number of arguments", func() { 1026 BeforeEach(func() { 1027 events["test"] = ` 1028 context.network = gohan_model_fetch();` 1029 }) 1030 1031 It("Returns the proper error", func() { 1032 err := env.HandleEvent("test", context) 1033 Expect(err).To(MatchError(ContainSubstring("arguments"))) 1034 }) 1035 }) 1036 1037 Context("With wrong schema ID", func() { 1038 BeforeEach(func() { 1039 events["test"] = ` 1040 context.network = gohan_model_fetch(context, 'netwerk', 'test1', null);` 1041 }) 1042 1043 It("Returns the proper error", func() { 1044 err := env.HandleEvent("test", context) 1045 Expect(err).To(MatchError(ContainSubstring("Unknown schema"))) 1046 }) 1047 }) 1048 1049 }) 1050 }) 1051 1052 Describe("Using gohan_model_create", func() { 1053 var ( 1054 tx transaction.Transaction 1055 ) 1056 1057 BeforeEach(func() { 1058 resource, err := manager.LoadResource(schemaID, network1) 1059 Expect(err).NotTo(HaveOccurred()) 1060 tx, err = testDB.Begin() 1061 Expect(err).NotTo(HaveOccurred()) 1062 defer tx.Close() 1063 Expect(tx.Create(resource)).To(Succeed()) 1064 Expect(tx.Commit()).To(Succeed()) 1065 1066 action = "create" 1067 }) 1068 1069 Describe("When invoked correctly", func() { 1070 Context("With transaction", func() { 1071 BeforeEach(func() { 1072 script := ` 1073 context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false});` 1074 events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"]) 1075 }) 1076 1077 It("Correctly creates the element", func() { 1078 tx, err := testDB.Begin() 1079 Expect(err).NotTo(HaveOccurred()) 1080 defer tx.Close() 1081 context["transaction"] = tx 1082 Expect(env.HandleEvent("test", context)).To(Succeed()) 1083 Expect(tx.Commit()).To(Succeed()) 1084 for key, value := range network2 { 1085 Expect(context).To(HaveKeyWithValue("network", HaveKeyWithValue(key, value))) 1086 } 1087 }) 1088 }) 1089 1090 Context("With chained exception", func() { 1091 BeforeEach(func() { 1092 script := ` 1093 context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false});` 1094 events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"]) 1095 events["post_create_in_transaction"] = ` 1096 throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);` 1097 }) 1098 1099 It("Returns the proper error", func() { 1100 tx, err := testDB.Begin() 1101 Expect(err).NotTo(HaveOccurred()) 1102 defer tx.Close() 1103 context["transaction"] = tx 1104 Expect(env.HandleEvent("test", context)).To(Succeed()) 1105 Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente"))) 1106 Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente")) 1107 }) 1108 }) 1109 1110 Context("With internal resource manipulation error", func() { 1111 BeforeEach(func() { 1112 script := ` 1113 context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false});` 1114 events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"]) 1115 events["post_create_in_transaction"] = ` 1116 delete context.response;` 1117 }) 1118 1119 It("Returns the proper error", func() { 1120 tx, err := testDB.Begin() 1121 Expect(err).NotTo(HaveOccurred()) 1122 defer tx.Close() 1123 context["transaction"] = tx 1124 err = env.HandleEvent("test", context) 1125 Expect(err).To(MatchError(ContainSubstring("No response"))) 1126 }) 1127 }) 1128 }) 1129 1130 Describe("When invoked incorrectly", func() { 1131 Context("With wrong number of arguments", func() { 1132 BeforeEach(func() { 1133 events["test"] = ` 1134 context.network = gohan_model_create();` 1135 }) 1136 1137 It("Returns the proper error", func() { 1138 err := env.HandleEvent("test", context) 1139 Expect(err).To(MatchError(ContainSubstring("arguments"))) 1140 }) 1141 }) 1142 1143 Context("With wrong schema ID", func() { 1144 BeforeEach(func() { 1145 script := ` 1146 context.network = gohan_model_create(context, 'netwerk', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v'});` 1147 events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"]) 1148 }) 1149 1150 It("Returns the proper error", func() { 1151 err := env.HandleEvent("test", context) 1152 Expect(err).To(MatchError(ContainSubstring("Unknown schema"))) 1153 }) 1154 }) 1155 1156 Context("With wrong resource to create", func() { 1157 BeforeEach(func() { 1158 events["test"] = ` 1159 context.network = gohan_model_create(context, 'network', 'Ne estas reto');` 1160 }) 1161 1162 It("Returns the proper error", func() { 1163 err := env.HandleEvent("test", context) 1164 Expect(err).To(MatchError(ContainSubstring("be of type 'Object'"))) 1165 }) 1166 }) 1167 }) 1168 }) 1169 1170 Describe("Using gohan_model_update", func() { 1171 var ( 1172 tx transaction.Transaction 1173 ) 1174 1175 BeforeEach(func() { 1176 resource, err := manager.LoadResource(schemaID, network1) 1177 Expect(err).NotTo(HaveOccurred()) 1178 tx, err = testDB.Begin() 1179 Expect(err).NotTo(HaveOccurred()) 1180 defer tx.Close() 1181 Expect(tx.Create(resource)).To(Succeed()) 1182 Expect(tx.Commit()).To(Succeed()) 1183 1184 action = "update" 1185 }) 1186 1187 Describe("When invoked correctly", func() { 1188 Context("With transaction", func() { 1189 BeforeEach(func() { 1190 script := ` 1191 context.network = gohan_model_update(context, 'network', 'test1', {'%v': '%v'}, null);` 1192 events["test"] = fmt.Sprintf(script, "name", network2["name"]) 1193 }) 1194 1195 It("Correctly updates the element", func() { 1196 tx, err := testDB.Begin() 1197 Expect(err).NotTo(HaveOccurred()) 1198 defer tx.Close() 1199 context["transaction"] = tx 1200 1201 Expect(env.HandleEvent("test", context)).To(Succeed()) 1202 Expect(tx.Commit()).To(Succeed()) 1203 Expect(context).To(HaveKeyWithValue("network", HaveKeyWithValue("name", network2["name"]))) 1204 }) 1205 }) 1206 1207 Context("With chained exception", func() { 1208 BeforeEach(func() { 1209 script := ` 1210 context.network = gohan_model_update(context, 'network', 'test1', {'%v': '%v'}, null);` 1211 events["test"] = fmt.Sprintf(script, "name", network2["name"]) 1212 events["post_update_in_transaction"] = ` 1213 throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);` 1214 }) 1215 1216 It("Returns the proper error", func() { 1217 tx, err := testDB.Begin() 1218 Expect(err).NotTo(HaveOccurred()) 1219 defer tx.Close() 1220 context["transaction"] = tx 1221 1222 Expect(env.HandleEvent("test", context)).To(Succeed()) 1223 Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente"))) 1224 Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente")) 1225 }) 1226 }) 1227 1228 Context("With internal resource manipulation error", func() { 1229 BeforeEach(func() { 1230 script := ` 1231 context.network = gohan_model_update(context, 'network', 'test1', {'%v': '%v'}, null);` 1232 events["test"] = fmt.Sprintf(script, "name", network2["name"]) 1233 events["post_update_in_transaction"] = ` 1234 delete context.response;` 1235 }) 1236 1237 It("Returns the proper error", func() { 1238 tx, err := testDB.Begin() 1239 Expect(err).NotTo(HaveOccurred()) 1240 defer tx.Close() 1241 context["transaction"] = tx 1242 1243 err = env.HandleEvent("test", context) 1244 Expect(err).To(MatchError(ContainSubstring("No response"))) 1245 }) 1246 }) 1247 }) 1248 1249 Describe("When invoked incorrectly", func() { 1250 Context("With wrong number of arguments", func() { 1251 BeforeEach(func() { 1252 events["test"] = ` 1253 context.network = gohan_model_update();` 1254 }) 1255 1256 It("Returns the proper error", func() { 1257 err := env.HandleEvent("test", context) 1258 Expect(err).To(MatchError(ContainSubstring("arguments"))) 1259 }) 1260 }) 1261 1262 Context("With wrong schema ID", func() { 1263 BeforeEach(func() { 1264 script := ` 1265 context.network = gohan_model_update(context, 'netwerk', 'test1', {'%v': '%v'}, null);` 1266 events["test"] = fmt.Sprintf(script, "name", network2["name"]) 1267 }) 1268 1269 It("Returns the proper error", func() { 1270 err := env.HandleEvent("test", context) 1271 Expect(err).To(MatchError(ContainSubstring("Unknown schema"))) 1272 }) 1273 }) 1274 1275 Context("With wrong update data map", func() { 1276 BeforeEach(func() { 1277 events["test"] = ` 1278 context.network = gohan_model_update(context, 'network', 'test1', 'Ne estas reto', null);` 1279 }) 1280 1281 It("Returns the proper error", func() { 1282 tx, err := testDB.Begin() 1283 Expect(err).NotTo(HaveOccurred()) 1284 defer tx.Close() 1285 context["transaction"] = tx 1286 1287 err = env.HandleEvent("test", context) 1288 Expect(err).To(MatchError(ContainSubstring("be of type 'Object'"))) 1289 }) 1290 }) 1291 }) 1292 }) 1293 1294 Describe("Using gohan_model_delete", func() { 1295 var ( 1296 tx transaction.Transaction 1297 ) 1298 1299 BeforeEach(func() { 1300 resource, err := manager.LoadResource(schemaID, network1) 1301 Expect(err).NotTo(HaveOccurred()) 1302 tx, err = testDB.Begin() 1303 Expect(err).NotTo(HaveOccurred()) 1304 defer tx.Close() 1305 Expect(tx.Create(resource)).To(Succeed()) 1306 Expect(tx.Commit()).To(Succeed()) 1307 1308 action = "delete" 1309 }) 1310 1311 Describe("When invoked correctly", func() { 1312 Context("With transaction", func() { 1313 BeforeEach(func() { 1314 events["test"] = ` 1315 context.network = gohan_model_delete(context, 'network', 'test1');` 1316 }) 1317 1318 It("Correctly deletes the element", func() { 1319 tx, err := testDB.Begin() 1320 Expect(err).NotTo(HaveOccurred()) 1321 defer tx.Close() 1322 context["transaction"] = tx 1323 1324 Expect(env.HandleEvent("test", context)).To(Succeed()) 1325 Expect(tx.Commit()).To(Succeed()) 1326 }) 1327 }) 1328 1329 Context("With chained exception", func() { 1330 BeforeEach(func() { 1331 events["test"] = ` 1332 context.network = gohan_model_delete(context, 'network', 'test1');` 1333 events["post_delete_in_transaction"] = ` 1334 throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);` 1335 }) 1336 1337 It("Returns the proper error", func() { 1338 tx, err := testDB.Begin() 1339 Expect(err).NotTo(HaveOccurred()) 1340 defer tx.Close() 1341 context["transaction"] = tx 1342 1343 Expect(env.HandleEvent("test", context)).To(Succeed()) 1344 Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente"))) 1345 Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente")) 1346 }) 1347 }) 1348 }) 1349 1350 Describe("When invoked incorrectly", func() { 1351 Context("With wrong number of arguments", func() { 1352 BeforeEach(func() { 1353 events["test"] = ` 1354 context.network = gohan_model_delete();` 1355 }) 1356 1357 It("Returns the proper error", func() { 1358 err := env.HandleEvent("test", context) 1359 Expect(err).To(MatchError(ContainSubstring("arguments"))) 1360 }) 1361 }) 1362 1363 Context("With wrong schema ID", func() { 1364 BeforeEach(func() { 1365 events["test"] = ` 1366 context.network = gohan_model_delete(context, 'netwerk', 'test1');` 1367 }) 1368 1369 It("Returns the proper error", func() { 1370 err := env.HandleEvent("test", context) 1371 Expect(err).To(MatchError(ContainSubstring("Unknown schema"))) 1372 }) 1373 }) 1374 }) 1375 }) 1376 1377 Describe("Actually chaining them", func() { 1378 var ( 1379 tx transaction.Transaction 1380 createNetworkContext middleware.Context 1381 createSubnetContext middleware.Context 1382 readSubnetContext middleware.Context 1383 subnetEnv extension.Environment 1384 subnetEvents map[string]string 1385 ) 1386 1387 BeforeEach(func() { 1388 1389 resource, err := manager.LoadResource(schemaID, network1) 1390 Expect(err).NotTo(HaveOccurred()) 1391 tx, err = testDB.Begin() 1392 Expect(err).NotTo(HaveOccurred()) 1393 defer tx.Close() 1394 Expect(tx.Create(resource)).To(Succeed()) 1395 Expect(tx.Commit()).To(Succeed()) 1396 1397 action = "read" 1398 1399 createNetworkContext = middleware.Context{} 1400 createSubnetContext = middleware.Context{} 1401 readSubnetContext = middleware.Context{} 1402 subnetEvents = map[string]string{} 1403 }) 1404 1405 JustBeforeEach(func() { 1406 curAction := "create" 1407 curSchemaID := "subnet" 1408 curSchema, ok := manager.Schema(curSchemaID) 1409 Expect(ok).To(BeTrue()) 1410 1411 curPath := curSchema.GetPluralURL() 1412 1413 curPolicy, curRole := manager.PolicyValidate(curAction, curPath, auth) 1414 Expect(curPolicy).NotTo(BeNil()) 1415 createSubnetContext["policy"] = curPolicy 1416 createSubnetContext["role"] = curRole 1417 createSubnetContext["tenant_id"] = auth.TenantID() 1418 createSubnetContext["tenant_name"] = auth.TenantName() 1419 createSubnetContext["auth_token"] = auth.AuthToken() 1420 createSubnetContext["catalog"] = auth.Catalog() 1421 createSubnetContext["auth"] = auth 1422 createSubnetContext["identity_service"] = &middleware.FakeIdentity{} 1423 1424 curAction = "create" 1425 curPolicy, curRole = manager.PolicyValidate(curAction, curPath, auth) 1426 Expect(curPolicy).NotTo(BeNil()) 1427 readSubnetContext["policy"] = curPolicy 1428 readSubnetContext["role"] = curRole 1429 readSubnetContext["tenant_id"] = auth.TenantID() 1430 readSubnetContext["tenant_name"] = auth.TenantName() 1431 readSubnetContext["auth_token"] = auth.AuthToken() 1432 readSubnetContext["catalog"] = auth.Catalog() 1433 readSubnetContext["auth"] = auth 1434 readSubnetContext["identity_service"] = &middleware.FakeIdentity{} 1435 1436 subnetEnv = newEnvironment() 1437 environmentManager.RegisterEnvironment(curSchemaID, subnetEnv) 1438 curExtensions := []*schema.Extension{} 1439 for event, javascript := range subnetEvents { 1440 extension, err := schema.NewExtension(map[string]interface{}{ 1441 "id": event + "_extension", 1442 "code": `gohan_register_handler("` + event + `", function(context) {` + javascript + `});`, 1443 "path": curPath, 1444 }) 1445 Expect(err).ToNot(HaveOccurred()) 1446 curExtensions = append(curExtensions, extension) 1447 } 1448 Expect(subnetEnv.LoadExtensionsForPath(curExtensions, curPath)).To(Succeed()) 1449 1450 curAction = "create" 1451 curSchemaID = "network" 1452 curSchema, ok = manager.Schema(curSchemaID) 1453 Expect(ok).To(BeTrue()) 1454 1455 curPath = curSchema.GetPluralURL() 1456 1457 curPolicy, curRole = manager.PolicyValidate(curAction, curPath, auth) 1458 Expect(curPolicy).NotTo(BeNil()) 1459 createNetworkContext["policy"] = curPolicy 1460 createNetworkContext["role"] = curRole 1461 createNetworkContext["tenant_id"] = auth.TenantID() 1462 createNetworkContext["tenant_name"] = auth.TenantName() 1463 createNetworkContext["auth_token"] = auth.AuthToken() 1464 createNetworkContext["catalog"] = auth.Catalog() 1465 createNetworkContext["auth"] = auth 1466 createNetworkContext["identity_service"] = &middleware.FakeIdentity{} 1467 }) 1468 1469 Describe("When being chained", func() { 1470 Context("Without exceptions", func() { 1471 BeforeEach(func() { 1472 script := ` 1473 context.network = gohan_model_create(context, 1474 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 1475 'route_targets': [], 'providor_networks': {}, 'shared': false});` 1476 events["test"] = fmt.Sprintf(script, "id", 1477 network2["id"], "name", network2["name"], 1478 "description", network2["description"], "tenant_id", network2["tenant_id"]) 1479 script = ` 1480 console.log("model create"); 1481 gohan_model_create( 1482 { 1483 transaction: context.transaction, 1484 policy: context.policy, 1485 }, 1486 'subnet', 1487 {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 1488 'network_id': context.response.network.id, 'description': "test"});` 1489 events["post_create_in_transaction"] = fmt.Sprintf(script, "id", subnet1["id"], "name", subnet1["name"], "tenant_id", subnet1["tenant_id"], "cidr", subnet1["cidr"]) 1490 subnetEvents["test_subnet"] = ` 1491 context.subnet = gohan_model_fetch(context, 'subnet', 'test3', null); 1492 ` 1493 }) 1494 1495 It("Correctly handles chaining", func() { 1496 tx, err := testDB.Begin() 1497 Expect(err).NotTo(HaveOccurred()) 1498 createNetworkContext["transaction"] = tx 1499 By("Creating the network") 1500 Expect(env.HandleEvent("test", createNetworkContext)).To(Succeed()) 1501 tx.Commit() 1502 tx.Close() 1503 By("network created") 1504 for key, value := range network2 { 1505 Expect(createNetworkContext).To(HaveKeyWithValue("network", HaveKeyWithValue(key, value))) 1506 } 1507 1508 tx, err = testDB.Begin() 1509 Expect(err).NotTo(HaveOccurred()) 1510 readSubnetContext["transaction"] = tx 1511 1512 By("Also creating a default subnet for it") 1513 Expect(subnetEnv.HandleEvent("test_subnet", readSubnetContext)).To(Succeed()) 1514 tx.Close() 1515 for key, value := range subnet1 { 1516 Expect(readSubnetContext).To(HaveKey("subnet")) 1517 Expect(readSubnetContext).To(HaveKeyWithValue("subnet", HaveKeyWithValue(key, value))) 1518 } 1519 Expect(readSubnetContext).To(HaveKeyWithValue("subnet", HaveKey("description"))) 1520 }) 1521 }) 1522 1523 Context("With an exception", func() { 1524 BeforeEach(func() { 1525 script := ` 1526 context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false, 'description': ""});` 1527 1528 events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "tenant_id", network2["tenant_id"]) 1529 script = ` 1530 gohan_model_create(context, 1531 'subnet', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'network_id': context.response.id});` 1532 events["post_create_in_transaction"] = fmt.Sprintf(script, "id", subnet1["id"], "name", subnet1["name"], "tenant_id", subnet1["tenant_id"], "cidr", subnet1["cidr"]) 1533 subnetEvents["pre_create_in_transaction"] = ` 1534 throw new CustomException("Minas Tirith has fallen!", 390);` 1535 }) 1536 1537 It("Returns the proper error", func() { 1538 tx, err := testDB.Begin() 1539 Expect(err).NotTo(HaveOccurred()) 1540 defer tx.Close() 1541 createNetworkContext["transaction"] = tx 1542 1543 Expect(env.HandleEvent("test", createNetworkContext)).To(Succeed()) 1544 Expect(createNetworkContext["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Minas Tirith has fallen!"))) 1545 Expect(createNetworkContext["exception_message"]).To(ContainSubstring("Minas Tirith has fallen!")) 1546 }) 1547 }) 1548 }) 1549 }) 1550 }) 1551 }) 1552 Describe("Using gohan_sync_fetch builtin", func() { 1553 It("Should fetch sync", func() { 1554 extension, err := schema.NewExtension(map[string]interface{}{ 1555 "id": "test_extension", 1556 "code": ` 1557 gohan_register_handler( 1558 "test_event", 1559 function(context) { 1560 context.resp = gohan_sync_fetch("/gohan_sync_fetch_test"); 1561 } 1562 ); 1563 `, 1564 "path": ".*", 1565 }) 1566 Expect(err).ToNot(HaveOccurred()) 1567 extensions := []*schema.Extension{extension} 1568 env := newEnvironment() 1569 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1570 context := map[string]interface{}{} 1571 env.Sync.Delete("/gohan_sync_fetch_test") 1572 env.Sync.Update("/gohan_sync_fetch_test", "{}") 1573 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1574 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("value", "{}"))) 1575 }) 1576 }) 1577 Describe("Using gohan_sync_watch builtin", func() { 1578 It("Should timeout with no events", func() { 1579 extension, err := schema.NewExtension(map[string]interface{}{ 1580 "id": "test_extension", 1581 "code": ` 1582 gohan_register_handler( 1583 "test_event", 1584 function(context) { 1585 context.resp = gohan_sync_watch("/gohan_sync_watch_test", 500, 0); 1586 } 1587 ); 1588 `, 1589 "path": ".*", 1590 }) 1591 Expect(err).ToNot(HaveOccurred()) 1592 extensions := []*schema.Extension{extension} 1593 env := newEnvironment() 1594 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1595 1596 context := map[string]interface{}{} 1597 env.Sync.Delete("/gohan_sync_watch_test") 1598 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1599 Expect(context).To(HaveKeyWithValue("resp", HaveLen(0))) 1600 }) 1601 1602 It("Should timeout with one event", func() { 1603 extension, err := schema.NewExtension(map[string]interface{}{ 1604 "id": "test_extension", 1605 "code": ` 1606 gohan_register_handler( 1607 "test_event", 1608 function(context) { 1609 context.resp = gohan_sync_watch("/gohan_sync_watch_test", 500, 0); 1610 } 1611 ); 1612 `, 1613 "path": ".*", 1614 }) 1615 Expect(err).ToNot(HaveOccurred()) 1616 extensions := []*schema.Extension{extension} 1617 env := newEnvironment() 1618 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1619 1620 context := map[string]interface{}{} 1621 env.Sync.Delete("/gohan_sync_watch_test") 1622 go func() { 1623 time.Sleep(time.Duration(200) * time.Millisecond) 1624 env.Sync.Update("/gohan_sync_watch_test", "{}") 1625 }() 1626 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1627 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("action", "set"))) 1628 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("key", "/gohan_sync_watch_test"))) 1629 Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("data", map[string]interface{}{}))) 1630 }) 1631 }) 1632 var _ = Describe("Concurrency race", func() { 1633 var ( 1634 env *otto.Environment 1635 ) 1636 channel := make(chan string) 1637 Context("Given environment", func() { 1638 BeforeEach(func() { 1639 env = newEnvironment() 1640 env.SetUp() 1641 vm := env.VM 1642 builtins := map[string]interface{}{ 1643 "test_consume": func(call ottopkg.FunctionCall) ottopkg.Value { 1644 result := <-channel 1645 ottoResult, _ := vm.ToValue(result) 1646 return ottoResult 1647 }, 1648 "test_produce": func(call ottopkg.FunctionCall) ottopkg.Value { 1649 ottoProduct := otto.ConvertOttoToGo(call.Argument(0)) 1650 product := otto.ConvertOttoToGo(ottoProduct).(string) 1651 channel <- product 1652 return ottopkg.NullValue() 1653 }, 1654 } 1655 for name, object := range builtins { 1656 vm.Set(name, object) 1657 } 1658 1659 Expect(env.Load("<race_test>", ` 1660 var produce = function() { 1661 for (var i = 0; i < 10; i++) { 1662 console.log("producing:", i); 1663 test_produce(i.toString()); 1664 } 1665 }; 1666 1667 var consume = function() { 1668 for (var i = 0; i < 10; i++) { 1669 var result = test_consume(); 1670 console.log("consumed:", result); 1671 } 1672 }`)).To(BeNil()) 1673 environmentManager.RegisterEnvironment("test_race", env) 1674 }) 1675 1676 It("Should work", func() { 1677 var consumerError = make(chan error) 1678 var producerError = make(chan error) 1679 1680 go func() { 1681 testEnv, _ := environmentManager.GetEnvironment("test_race") 1682 ottoEnv := testEnv.(*otto.Environment) 1683 _, err := ottoEnv.VM.Call("consume", nil) 1684 consumerError <- err 1685 }() 1686 1687 go func() { 1688 testEnv, _ := environmentManager.GetEnvironment("test_race") 1689 ottoEnv := testEnv.(*otto.Environment) 1690 _, err := ottoEnv.VM.Call("produce", nil) 1691 producerError <- err 1692 }() 1693 1694 select { 1695 case err := <-consumerError: 1696 Expect(err).To(BeNil()) 1697 case <-time.After(1 * time.Second): 1698 Fail("Timeout when waiting for consumer") 1699 } 1700 select { 1701 case err := <-producerError: 1702 Expect(err).To(BeNil()) 1703 case <-time.After(1 * time.Second): 1704 Fail("Timeout when waiting for producer") 1705 } 1706 }) 1707 }) 1708 }) 1709 var _ = Describe("Timeout", func() { 1710 Context("stops if execution time exceeds timelimit", func() { 1711 It("Should work", func() { 1712 extension, err := schema.NewExtension(map[string]interface{}{ 1713 "id": "infinite_loop", 1714 "code": ` 1715 gohan_register_handler("test_event", function(context){ 1716 while(true){} 1717 });`, 1718 "path": ".*", 1719 }) 1720 Expect(err).ToNot(HaveOccurred()) 1721 extensions := []*schema.Extension{extension} 1722 env := otto.NewEnvironment("otto_test", testDB, &middleware.FakeIdentity{}, 1723 time.Duration(100), testSync) 1724 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1725 1726 context := map[string]interface{}{ 1727 "id": "test", 1728 } 1729 Expect(env.HandleEvent("test_event", context)).ToNot(Succeed()) 1730 }) 1731 }) 1732 }) 1733 }) 1734 var _ = Describe("Using gohan_file builtin", func() { 1735 var ( 1736 context map[string]interface{} 1737 env *otto.Environment 1738 ) 1739 1740 BeforeEach(func() { 1741 context = map[string]interface{}{ 1742 "id": "test", 1743 } 1744 env = newEnvironment() 1745 }) 1746 Context("List files", func() { 1747 It("Should work", func() { 1748 extension, err := schema.NewExtension(map[string]interface{}{ 1749 "id": "list_files", 1750 "code": ` 1751 gohan_register_handler("test_event", function(context){ 1752 context.list = gohan_file_list('./'); 1753 });`, 1754 "path": ".*", 1755 }) 1756 Expect(err).ToNot(HaveOccurred()) 1757 extensions := []*schema.Extension{extension} 1758 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1759 1760 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1761 Expect(context).To(HaveKey("list")) 1762 Expect(context["list"]).ToNot(BeNil()) 1763 }) 1764 It("Shouldn't work", func() { 1765 extension, err := schema.NewExtension(map[string]interface{}{ 1766 "id": "list_files", 1767 "code": ` 1768 gohan_register_handler("test_event", function(context){ 1769 context.list = gohan_file_list('./doesnt_exist'); 1770 });`, 1771 "path": ".*", 1772 }) 1773 Expect(err).ToNot(HaveOccurred()) 1774 extensions := []*schema.Extension{extension} 1775 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1776 1777 Expect(env.HandleEvent("test_event", context)).ToNot(Succeed()) 1778 Expect(context).ToNot(HaveKey("list")) 1779 }) 1780 }) 1781 Context("Read file", func() { 1782 It("Should work", func() { 1783 extension, err := schema.NewExtension(map[string]interface{}{ 1784 "id": "read_file", 1785 "code": ` 1786 gohan_register_handler("test_event", function(context){ 1787 context.list = gohan_file_read('./test.db'); 1788 });`, 1789 "path": ".*", 1790 }) 1791 Expect(err).ToNot(HaveOccurred()) 1792 extensions := []*schema.Extension{extension} 1793 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1794 1795 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1796 Expect(context).To(HaveKey("list")) 1797 Expect(context["list"]).ToNot(BeNil()) 1798 }) 1799 It("Shouldn't work", func() { 1800 extension, err := schema.NewExtension(map[string]interface{}{ 1801 "id": "read_file", 1802 "code": ` 1803 gohan_register_handler("test_event", function(context){ 1804 context.list = gohan_file_read('./doesnt_exist'); 1805 });`, 1806 "path": ".*", 1807 }) 1808 Expect(err).ToNot(HaveOccurred()) 1809 extensions := []*schema.Extension{extension} 1810 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1811 1812 Expect(env.HandleEvent("test_event", context)).ToNot(Succeed()) 1813 Expect(context).ToNot(HaveKey("list")) 1814 }) 1815 }) 1816 Context("Is dir", func() { 1817 It("Is dir", func() { 1818 extension, err := schema.NewExtension(map[string]interface{}{ 1819 "id": "read_file", 1820 "code": ` 1821 gohan_register_handler("test_event", function(context){ 1822 context.dir = gohan_file_dir('./'); 1823 });`, 1824 "path": ".*", 1825 }) 1826 Expect(err).ToNot(HaveOccurred()) 1827 extensions := []*schema.Extension{extension} 1828 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1829 1830 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1831 Expect(context).To(HaveKeyWithValue("dir", true)) 1832 }) 1833 It("Isn't dir", func() { 1834 extension, err := schema.NewExtension(map[string]interface{}{ 1835 "id": "read_file", 1836 "code": ` 1837 gohan_register_handler("test_event", function(context){ 1838 context.dir= gohan_file_dir('./test.db'); 1839 });`, 1840 "path": ".*", 1841 }) 1842 Expect(err).ToNot(HaveOccurred()) 1843 extensions := []*schema.Extension{extension} 1844 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1845 1846 Expect(env.HandleEvent("test_event", context)).To(Succeed()) 1847 Expect(context).To(HaveKeyWithValue("dir", false)) 1848 }) 1849 It("Shouldn't work", func() { 1850 extension, err := schema.NewExtension(map[string]interface{}{ 1851 "id": "read_file", 1852 "code": ` 1853 gohan_register_handler("test_event", function(context){ 1854 context.dir = gohan_file_dir('./doesnt_exist'); 1855 });`, 1856 "path": ".*", 1857 }) 1858 Expect(err).ToNot(HaveOccurred()) 1859 extensions := []*schema.Extension{extension} 1860 Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed()) 1861 1862 Expect(env.HandleEvent("test_event", context)).ToNot(Succeed()) 1863 Expect(context).ToNot(HaveKey("dir")) 1864 }) 1865 }) 1866 })