github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/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  	"regexp"
    21  	"time"
    22  
    23  	ottopkg "github.com/dop251/otto"
    24  	. "github.com/onsi/ginkgo"
    25  	. "github.com/onsi/gomega"
    26  	"github.com/onsi/gomega/ghttp"
    27  
    28  	"github.com/cloudwan/gohan/db/transaction"
    29  	"github.com/cloudwan/gohan/extension"
    30  	"github.com/cloudwan/gohan/extension/otto"
    31  	"github.com/cloudwan/gohan/schema"
    32  	"github.com/cloudwan/gohan/server/middleware"
    33  	"github.com/cloudwan/gohan/server/resources"
    34  	"github.com/cloudwan/gohan/util"
    35  )
    36  
    37  var _ = Describe("Otto extension manager", func() {
    38  	var (
    39  		manager            *schema.Manager
    40  		environmentManager *extension.Manager
    41  	)
    42  
    43  	BeforeEach(func() {
    44  		manager = schema.GetManager()
    45  		environmentManager = extension.GetManager()
    46  	})
    47  
    48  	AfterEach(func() {
    49  		tx, err := testDB.Begin()
    50  		Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.")
    51  		defer tx.Close()
    52  		for _, schema := range schema.GetManager().Schemas() {
    53  			if whitelist[schema.ID] {
    54  				continue
    55  			}
    56  			err = clearTable(tx, schema)
    57  			Expect(err).ToNot(HaveOccurred(), "Failed to clear table.")
    58  		}
    59  		err = tx.Commit()
    60  		Expect(err).ToNot(HaveOccurred(), "Failed to commite transaction.")
    61  
    62  		extension.ClearManager()
    63  	})
    64  
    65  	Describe("Loading an extension", func() {
    66  		Context("When extension is not a valid JavaScript", func() {
    67  			It("returns a meaningful compilation error", func() {
    68  				goodExtension, err := schema.NewExtension(map[string]interface{}{
    69  					"id":   "good_extension",
    70  					"code": `gohan_register_handler("test_event", function(context) {});`,
    71  					"path": ".*",
    72  				})
    73  				Expect(err).ToNot(HaveOccurred())
    74  				goodExtension.URL = "good_extension.js"
    75  
    76  				badExtension, err := schema.NewExtension(map[string]interface{}{
    77  					"id":   "bad_extension",
    78  					"code": `gohan_register_handler("test_event", function(context {});`,
    79  					"path": ".*",
    80  				})
    81  				Expect(err).ToNot(HaveOccurred())
    82  				badExtension.URL = "bad_extension.js"
    83  
    84  				extensions := []*schema.Extension{goodExtension, badExtension}
    85  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
    86  				err = env.LoadExtensionsForPath(extensions, "test_path")
    87  				Expect(err).To(HaveOccurred(), "Expected compilation errors.")
    88  
    89  				pattern := regexp.MustCompile(`^(?P<file>[^:]+).*Line\s(?P<line>\d+).*`)
    90  				match := pattern.FindStringSubmatch(err.Error())
    91  				Expect(len(match)).To(Equal(3))
    92  
    93  				groups := make(map[string]string)
    94  				for i, name := range pattern.SubexpNames() {
    95  					groups[name] = match[i]
    96  				}
    97  
    98  				Expect(groups).To(HaveKeyWithValue("file", "bad_extension.js"))
    99  				Expect(groups).To(HaveKeyWithValue("line", "1"))
   100  			})
   101  		})
   102  
   103  		Context("When extension URL uses file:// protocol", func() {
   104  			It("should read the file and run the extension", func() {
   105  				extension, err := schema.NewExtension(map[string]interface{}{
   106  					"id":   "test_extension",
   107  					"url":  "file://../tests/sample_extension.js",
   108  					"path": ".*",
   109  				})
   110  				Expect(err).ToNot(HaveOccurred())
   111  
   112  				extensions := []*schema.Extension{extension}
   113  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   114  				Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed())
   115  
   116  				context := map[string]interface{}{
   117  					"id": "test",
   118  				}
   119  				Expect(env.HandleEvent("test_event", context)).To(Succeed())
   120  				Expect(context["resp"]).ToNot(BeNil())
   121  			})
   122  		})
   123  
   124  		Context("When extension URL uses http:// protocol", func() {
   125  			It("should download and run the extension", func() {
   126  				server := ghttp.NewServer()
   127  				code := `
   128  					gohan_register_handler("test_event", function(context){
   129  						context.resp = "Hello";
   130  					});
   131  				`
   132  				server.AppendHandlers(ghttp.CombineHandlers(
   133  					ghttp.VerifyRequest("GET", "/extension.js"),
   134  					ghttp.RespondWith(200, code),
   135  				))
   136  
   137  				extension, err := schema.NewExtension(map[string]interface{}{
   138  					"id":   "test_extension",
   139  					"url":  server.URL() + "/extension.js",
   140  					"path": ".*",
   141  				})
   142  				Expect(err).ToNot(HaveOccurred())
   143  				extensions := []*schema.Extension{extension}
   144  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   145  				Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed())
   146  
   147  				context := map[string]interface{}{
   148  					"id": "test",
   149  				}
   150  				Expect(env.HandleEvent("test_event", context)).To(Succeed())
   151  				Expect(context["resp"]).ToNot(BeNil())
   152  				server.Close()
   153  			})
   154  		})
   155  	})
   156  
   157  	Describe("Running an extension", func() {
   158  		Context("When a runtime error occurs", func() {
   159  			It("should return a meaningful error", func() {
   160  				goodExtension, err := schema.NewExtension(map[string]interface{}{
   161  					"id":   "good_extension",
   162  					"code": `gohan_register_handler("test_event", function(context) {});`,
   163  					"path": ".*",
   164  				})
   165  				Expect(err).ToNot(HaveOccurred())
   166  				goodExtension.URL = "good_extension.js"
   167  
   168  				badExtension, err := schema.NewExtension(map[string]interface{}{
   169  					"id": "bad_extension",
   170  					"code": `gohan_register_handler("test_event", function foo(context) {
   171  					var a = 5;
   172  					console.log(b);
   173  				});`,
   174  					"path": ".*",
   175  				})
   176  				Expect(err).ToNot(HaveOccurred())
   177  				badExtension.URL = "bad_extension.js"
   178  
   179  				extensions := []*schema.Extension{goodExtension, badExtension}
   180  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   181  				err = env.LoadExtensionsForPath(extensions, "test_path")
   182  
   183  				context := map[string]interface{}{
   184  					"id": "test",
   185  				}
   186  				err = env.HandleEvent("test_event", context)
   187  				Expect(err).To(HaveOccurred())
   188  
   189  				Expect(regexp.MatchString(`ReferenceError:\s'b'`, err.Error())).To(BeTrue())
   190  
   191  				pattern := regexp.MustCompile(`at\s(?P<function>\w+)\s\((?P<file>.*?):(?P<line>\d+).*?\)`)
   192  				match := pattern.FindStringSubmatch(err.Error())
   193  				Expect(len(match)).To(Equal(4))
   194  				//FIXME(timorl): because of a throw bug in otto we cannot expect more meaningful errors
   195  			})
   196  		})
   197  
   198  		Context("When a nil value is passed", func() {
   199  			It("should be represented as null in the virtual machine", func() {
   200  				goodExtension, err := schema.NewExtension(map[string]interface{}{
   201  					"id": "good_extension",
   202  					"code": `gohan_register_handler("test_event", function(context) {
   203  						if (context.nulo === null) {
   204  							context.respondo = "verdo"
   205  						} else {
   206  							context.respondo = "ne verdo"
   207  						}
   208  					});`,
   209  					"path": ".*",
   210  				})
   211  				Expect(err).ToNot(HaveOccurred())
   212  				goodExtension.URL = "good_extension.js"
   213  
   214  				extensions := []*schema.Extension{goodExtension}
   215  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   216  				err = env.LoadExtensionsForPath(extensions, "test_path")
   217  
   218  				context := map[string]interface{}{
   219  					"id":   "test",
   220  					"nulo": nil,
   221  				}
   222  				Expect(env.HandleEvent("test_event", context)).To(Succeed())
   223  				Expect(context).To(HaveKeyWithValue("respondo", "verdo"))
   224  			})
   225  		})
   226  	})
   227  
   228  	Describe("Using gohan_http builtin", func() {
   229  		Context("When the destination is reachable", func() {
   230  			It("Should return the contents", func() {
   231  				server := ghttp.NewServer()
   232  				server.AppendHandlers(ghttp.CombineHandlers(
   233  					ghttp.VerifyRequest("GET", "/contents"),
   234  					ghttp.RespondWith(200, "HELLO"),
   235  				))
   236  
   237  				extension, err := schema.NewExtension(map[string]interface{}{
   238  					"id": "test_extension",
   239  					"code": `
   240  						gohan_register_handler("test_event", function(context){
   241  								context.resp = gohan_http('GET', '` + server.URL() + `/contents', {}, {});
   242  						});`,
   243  					"path": ".*",
   244  				})
   245  				Expect(err).ToNot(HaveOccurred())
   246  				extensions := []*schema.Extension{extension}
   247  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   248  				Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed())
   249  
   250  				context := map[string]interface{}{
   251  					"id": "test",
   252  				}
   253  				Expect(env.HandleEvent("test_event", context)).To(Succeed())
   254  				Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("status_code", "200")))
   255  				Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("body", "HELLO")))
   256  				server.Close()
   257  			})
   258  		})
   259  
   260  		Context("When the destination is not reachable", func() {
   261  			It("Should return the error", func() {
   262  				extension, err := schema.NewExtension(map[string]interface{}{
   263  					"id": "test_extension",
   264  					"code": `
   265  						gohan_register_handler("test_event", function(context){
   266  							context.resp = gohan_http('GET', 'http://localhost:38000/contents', {}, {});
   267  						});`,
   268  					"path": ".*",
   269  				})
   270  				Expect(err).ToNot(HaveOccurred())
   271  				extensions := []*schema.Extension{extension}
   272  				env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   273  				Expect(env.LoadExtensionsForPath(extensions, "test_path")).To(Succeed())
   274  
   275  				context := map[string]interface{}{
   276  					"id": "test",
   277  				}
   278  				Expect(env.HandleEvent("test_event", context)).To(Succeed())
   279  				Expect(context).To(HaveKeyWithValue("resp", HaveKeyWithValue("status", "err")))
   280  				Expect(context).To(HaveKeyWithValue("resp", HaveKey("error")))
   281  			})
   282  		})
   283  	})
   284  
   285  	Describe("Using gohan database manipulation builtins", func() {
   286  		var (
   287  			adminAuth     schema.Authorization
   288  			memberAuth    schema.Authorization
   289  			auth          schema.Authorization
   290  			context       middleware.Context
   291  			schemaID      string
   292  			path          string
   293  			action        string
   294  			currentSchema *schema.Schema
   295  			extensions    []*schema.Extension
   296  			env           extension.Environment
   297  			events        map[string]string
   298  
   299  			network1 map[string]interface{}
   300  			network2 map[string]interface{}
   301  			subnet1  map[string]interface{}
   302  		)
   303  
   304  		BeforeEach(func() {
   305  			adminAuth = schema.NewAuthorization(adminTenantID, "admin", adminTokenID, []string{"admin"}, nil)
   306  			memberAuth = schema.NewAuthorization(memberTenantID, "member", memberTokenID, []string{"_member_"}, nil)
   307  			auth = adminAuth
   308  
   309  			context = middleware.Context{}
   310  
   311  			events = map[string]string{}
   312  
   313  			network1 = map[string]interface{}{
   314  				"id":                "test1",
   315  				"name":              "Rohan",
   316  				"description":       "The proud horsemasters",
   317  				"tenant_id":         adminTenantID,
   318  				"providor_networks": map[string]interface{}{},
   319  				"route_targets":     []interface{}{},
   320  				"shared":            false,
   321  			}
   322  			network2 = map[string]interface{}{
   323  				"id":                "test2",
   324  				"name":              "Gondor",
   325  				"description":       "Once glorious empire",
   326  				"tenant_id":         adminTenantID,
   327  				"providor_networks": map[string]interface{}{},
   328  				"route_targets":     []interface{}{},
   329  				"shared":            false,
   330  			}
   331  			subnet1 = map[string]interface{}{
   332  				"id":        "test3",
   333  				"name":      "Minas Tirith",
   334  				"tenant_id": adminTenantID,
   335  				"cidr":      "10.10.0.0/16",
   336  			}
   337  		})
   338  
   339  		JustBeforeEach(func() {
   340  			var ok bool
   341  			currentSchema, ok = manager.Schema(schemaID)
   342  			Expect(ok).To(BeTrue())
   343  
   344  			path = currentSchema.GetPluralURL()
   345  
   346  			policy, role := manager.PolicyValidate(action, path, auth)
   347  			Expect(policy).NotTo(BeNil())
   348  			context["policy"] = policy
   349  			context["role"] = role
   350  			context["tenant_id"] = auth.TenantID()
   351  			context["tenant_name"] = auth.TenantName()
   352  			context["auth_token"] = auth.AuthToken()
   353  			context["catalog"] = auth.Catalog()
   354  			context["auth"] = auth
   355  			context["identity_service"] = &middleware.FakeIdentity{}
   356  
   357  			env = otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   358  			environmentManager.RegisterEnvironment(schemaID, env)
   359  			extensions = []*schema.Extension{}
   360  			for event, javascript := range events {
   361  				extension, err := schema.NewExtension(map[string]interface{}{
   362  					"id":   event + "_extension",
   363  					"code": `gohan_register_handler("` + event + `", function(context) {` + javascript + `});`,
   364  					"path": path,
   365  				})
   366  				Expect(err).ToNot(HaveOccurred())
   367  				extensions = append(extensions, extension)
   368  			}
   369  			Expect(env.LoadExtensionsForPath(extensions, path)).To(Succeed())
   370  		})
   371  
   372  		AfterEach(func() {
   373  			tx, err := testDB.Begin()
   374  			Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.")
   375  			environmentManager.UnRegisterEnvironment(schemaID)
   376  			defer tx.Close()
   377  			for _, schema := range schema.GetManager().Schemas() {
   378  				if whitelist[schema.ID] {
   379  					continue
   380  				}
   381  				err = clearTable(tx, schema)
   382  				Expect(err).ToNot(HaveOccurred(), "Failed to clear table.")
   383  			}
   384  			err = tx.Commit()
   385  			Expect(err).ToNot(HaveOccurred(), "Failed to commite transaction.")
   386  		})
   387  
   388  		Describe("Using gohan_db_* builtins", func() {
   389  			BeforeEach(func() {
   390  				schemaID = "network"
   391  				action = "read"
   392  			})
   393  
   394  			Context("When given a transaction", func() {
   395  				It("Correctly handles CRUD operations", func() {
   396  					tx, err := testDB.Begin()
   397  					defer tx.Commit()
   398  
   399  					extension, err := schema.NewExtension(map[string]interface{}{
   400  						"id": "test_extension",
   401  						"code": `
   402  						gohan_register_handler("test_event", function(context){
   403  						  gohan_db_create(context.transaction,
   404  						    'network', {
   405  								'id':'test1',
   406  								'name': 'name',
   407  								'description': 'description',
   408  								'providor_networks': {},
   409  								'route_targets': [],
   410  								'shared': false,
   411  								'tenant_id': 'admin'});
   412  						  context.network = gohan_db_fetch(context.transaction, 'network', 'test1', 'admin');
   413  						  gohan_db_update(context.transaction,
   414  						    'network', {'id':'test1', 'name': 'name_updated', 'tenant_id': 'admin'});
   415  						  context.networks = gohan_db_list(context.transaction, 'network', {});
   416  						  gohan_db_delete(context.transaction, 'network', 'test1');
   417  						  context.networks2 = gohan_db_list(context.transaction, 'network', {});
   418  						});`,
   419  						"path": ".*",
   420  					})
   421  					Expect(err).ToNot(HaveOccurred())
   422  					extensions := []*schema.Extension{extension}
   423  					env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   424  					env.LoadExtensionsForPath(extensions, "test_path")
   425  
   426  					context := map[string]interface{}{
   427  						"id":          "test",
   428  						"transaction": tx,
   429  					}
   430  					Expect(env.HandleEvent("test_event", context)).To(Succeed())
   431  					Expect(context["network"]).ToNot(BeNil())
   432  					Expect(context["networks"]).ToNot(BeNil())
   433  				})
   434  			})
   435  
   436  			Context("When given no transaction", func() {
   437  				It("Correctly handles CRUD operations", func() {
   438  					tx, err := testDB.Begin()
   439  					defer tx.Commit()
   440  
   441  					extension, err := schema.NewExtension(map[string]interface{}{
   442  						"id": "test_extension",
   443  						"code": `
   444  						gohan_register_handler("test_event", function(context){
   445  						  gohan_db_create(context.transaction,
   446  						    'network', {
   447  								'id':'test1',
   448  								'name': 'name',
   449  								'description': 'description',
   450  								'providor_networks': {},
   451  								'route_targets': [],
   452  								'shared': false,
   453  								'tenant_id': 'admin'});
   454  						  context.network = gohan_db_fetch(context.transaction, 'network', 'test1', 'admin');
   455  						  gohan_db_update(context.transaction,
   456  						    'network', {'id':'test1', 'name': 'name_updated', 'tenant_id': 'admin'});
   457  						  context.networks = gohan_db_list(context.transaction, 'network', {});
   458  						  gohan_db_delete(context.transaction, 'network', 'test1');
   459  						  context.networks2 = gohan_db_list(context.transaction, 'network', {});
   460  						});`,
   461  						"path": ".*",
   462  					})
   463  					Expect(err).ToNot(HaveOccurred())
   464  					extensions := []*schema.Extension{extension}
   465  					env := otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
   466  					env.LoadExtensionsForPath(extensions, "test_path")
   467  
   468  					context := map[string]interface{}{
   469  						"id":          "test",
   470  						"transaction": nil,
   471  					}
   472  					Expect(env.HandleEvent("test_event", context)).To(Succeed())
   473  					Expect(context["network"]).ToNot(BeNil())
   474  					Expect(context["networks"]).ToNot(BeNil())
   475  				})
   476  			})
   477  		})
   478  
   479  		Describe("Using chaining builtins", func() {
   480  			BeforeEach(func() {
   481  				schemaID = "network"
   482  			})
   483  
   484  			Describe("Using gohan_model_list", func() {
   485  				var (
   486  					tx transaction.Transaction
   487  				)
   488  
   489  				BeforeEach(func() {
   490  					resource, err := manager.LoadResource(schemaID, network1)
   491  					Expect(err).NotTo(HaveOccurred())
   492  					tx, err = testDB.Begin()
   493  					Expect(err).NotTo(HaveOccurred())
   494  					defer tx.Close()
   495  					Expect(tx.Create(resource)).To(Succeed())
   496  					Expect(tx.Commit()).To(Succeed())
   497  
   498  					action = "read"
   499  				})
   500  
   501  				Describe("When invoked correctly", func() {
   502  					Context("With transaction", func() {
   503  						BeforeEach(func() {
   504  							events["test"] = `
   505  								context.networks = gohan_model_list(context, 'network', {});`
   506  						})
   507  
   508  						It("Correctly lists elements", func() {
   509  							tx, err := testDB.Begin()
   510  							Expect(err).NotTo(HaveOccurred())
   511  							defer tx.Close()
   512  							context["transaction"] = tx
   513  
   514  							Expect(env.HandleEvent("test", context)).To(Succeed())
   515  							Expect(tx.Commit()).To(Succeed())
   516  							Expect(context).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network1))))
   517  						})
   518  					})
   519  
   520  					Context("With a string filter", func() {
   521  						BeforeEach(func() {
   522  							events["test"] = `
   523  								console.log(context)
   524  								context.networks = gohan_model_list(context, 'network', {'id': 'test1'});`
   525  						})
   526  
   527  						It("Correctly lists elements", func() {
   528  							tx, err := testDB.Begin()
   529  							Expect(err).NotTo(HaveOccurred())
   530  							defer tx.Close()
   531  							context["transaction"] = tx
   532  							Expect(env.HandleEvent("test", context)).To(Succeed())
   533  							Expect(context).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network1))))
   534  						})
   535  					})
   536  
   537  					Context("With an array filter", func() {
   538  						BeforeEach(func() {
   539  							events["test"] = `
   540  								context.networks = gohan_model_list(context, 'network', {'id': ['test1']});`
   541  						})
   542  
   543  						It("Correctly lists elements", func() {
   544  							tx, err := testDB.Begin()
   545  							Expect(err).NotTo(HaveOccurred())
   546  							defer tx.Close()
   547  							context["transaction"] = tx
   548  							Expect(env.HandleEvent("test", context)).To(Succeed())
   549  							Expect(context).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network1))))
   550  						})
   551  					})
   552  
   553  					Context("With chained exception", func() {
   554  						BeforeEach(func() {
   555  							events["test"] = `
   556  								context.networks = gohan_model_list(context, 'network', {});`
   557  							events["post_list_in_transaction"] = `
   558  								throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);`
   559  						})
   560  
   561  						It("Returns the proper error", func() {
   562  							tx, err := testDB.Begin()
   563  							Expect(err).NotTo(HaveOccurred())
   564  							defer tx.Close()
   565  							context["transaction"] = tx
   566  							Expect(env.HandleEvent("test", context)).To(Succeed())
   567  							Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente")))
   568  							Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente"))
   569  						})
   570  					})
   571  
   572  					Context("With internal resource manipulation error", func() {
   573  						BeforeEach(func() {
   574  							events["test"] = `
   575  								context.networks = gohan_model_list(context, 'network', {});`
   576  							events["post_list_in_transaction"] = `
   577  								delete context.response;`
   578  						})
   579  
   580  						It("Returns the proper error", func() {
   581  							tx, err := testDB.Begin()
   582  							Expect(err).NotTo(HaveOccurred())
   583  							defer tx.Close()
   584  							context["transaction"] = tx
   585  							err = env.HandleEvent("test", context)
   586  							Expect(err).To(MatchError(ContainSubstring("No response")))
   587  						})
   588  					})
   589  				})
   590  
   591  				Describe("When invoked incorrectly", func() {
   592  					Context("With wrong number of arguments", func() {
   593  						BeforeEach(func() {
   594  							events["test"] = `
   595  								context.networks = gohan_model_list();`
   596  						})
   597  
   598  						It("Returns the proper error", func() {
   599  							err := env.HandleEvent("test", context)
   600  							Expect(err).To(MatchError(ContainSubstring("arguments")))
   601  						})
   602  					})
   603  
   604  					Context("With wrong schema ID", func() {
   605  						BeforeEach(func() {
   606  							events["test"] = `
   607  								context.networks = gohan_model_list(context, 'netwerk', {});`
   608  						})
   609  
   610  						It("Returns the proper error", func() {
   611  							err := env.HandleEvent("test", context)
   612  							Expect(err).To(MatchError(ContainSubstring("Unknown schema")))
   613  						})
   614  					})
   615  
   616  					Context("With wrong filter", func() {
   617  						BeforeEach(func() {
   618  							events["test"] = `
   619  								context.networks = gohan_model_list(context, 'network', {'id': context.policy});`
   620  						})
   621  
   622  						It("Returns the proper error", func() {
   623  							err := env.HandleEvent("test", context)
   624  							Expect(err).To(MatchError(ContainSubstring("not a string")))
   625  						})
   626  					})
   627  
   628  					Context("With wrong filter but array", func() {
   629  						BeforeEach(func() {
   630  							events["test"] = `
   631  								context.networks = gohan_model_list(context, 'network', {'id': [context.policy]});`
   632  						})
   633  
   634  						It("Returns the proper error", func() {
   635  							err := env.HandleEvent("test", context)
   636  							Expect(err).To(MatchError(ContainSubstring("not a string")))
   637  						})
   638  					})
   639  				})
   640  			})
   641  
   642  			Describe("Using gohan_model_fetch", func() {
   643  				var (
   644  					tx transaction.Transaction
   645  				)
   646  
   647  				BeforeEach(func() {
   648  					resource, err := manager.LoadResource(schemaID, network1)
   649  					Expect(err).NotTo(HaveOccurred())
   650  					tx, err = testDB.Begin()
   651  					Expect(err).NotTo(HaveOccurred())
   652  					defer tx.Close()
   653  					Expect(tx.Create(resource)).To(Succeed())
   654  					Expect(tx.Commit()).To(Succeed())
   655  
   656  					action = "read"
   657  				})
   658  
   659  				Describe("When invoked correctly", func() {
   660  					Context("With transaction", func() {
   661  						BeforeEach(func() {
   662  							events["test"] = `
   663  								context.network = gohan_model_fetch(context, 'network', 'test1', null);
   664  								`
   665  						})
   666  
   667  						It("Correctly fetches the element", func() {
   668  							tx, err := testDB.Begin()
   669  							Expect(err).NotTo(HaveOccurred())
   670  							defer tx.Close()
   671  							context["transaction"] = tx
   672  
   673  							Expect(env.HandleEvent("test", context)).To(Succeed())
   674  							Expect(tx.Commit()).To(Succeed())
   675  							By(fmt.Sprintf("%v", context))
   676  							resultRaw, ok := context["network"]
   677  							Expect(ok).To(BeTrue())
   678  							_, ok = resultRaw.(map[string]interface{})
   679  							Expect(ok).To(BeTrue())
   680  						})
   681  					})
   682  
   683  					Context("Asking for a nonexistent resource", func() {
   684  						BeforeEach(func() {
   685  							events["test"] = `
   686  								context.network = gohan_model_fetch(context, 'network', 'neEstas', null);`
   687  						})
   688  
   689  						It("Returns the not found error", func() {
   690  							tx, err := testDB.Begin()
   691  							Expect(err).NotTo(HaveOccurred())
   692  							defer tx.Close()
   693  							context["transaction"] = tx
   694  							Expect(env.HandleEvent("test", context)).To(Succeed())
   695  							Expect(context["exception"]).To(HaveKeyWithValue("problem", int(resources.NotFound)))
   696  							Expect(context["exception_message"]).To(ContainSubstring("ResourceException"))
   697  						})
   698  					})
   699  
   700  					Context("With chained exception", func() {
   701  						BeforeEach(func() {
   702  							events["test"] = `
   703  								context.network = gohan_model_fetch(context, 'network', 'test1', null);`
   704  							events["post_show_in_transaction"] = `
   705  								throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);`
   706  						})
   707  
   708  						It("Returns the proper error", func() {
   709  							tx, err := testDB.Begin()
   710  							Expect(err).NotTo(HaveOccurred())
   711  							defer tx.Close()
   712  							context["transaction"] = tx
   713  							Expect(env.HandleEvent("test", context)).To(Succeed())
   714  							Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente")))
   715  							Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente"))
   716  						})
   717  					})
   718  
   719  					Context("With internal resource manipulation error", func() {
   720  						BeforeEach(func() {
   721  							events["test"] = `
   722  								context.network = gohan_model_fetch(context, 'network', 'test1', null);`
   723  							events["post_show_in_transaction"] = `
   724  								delete context.response;`
   725  						})
   726  
   727  						It("Returns the proper error", func() {
   728  							tx, err := testDB.Begin()
   729  							Expect(err).NotTo(HaveOccurred())
   730  							defer tx.Close()
   731  							context["transaction"] = tx
   732  							err = env.HandleEvent("test", context)
   733  							Expect(err).To(MatchError(ContainSubstring("No response")))
   734  						})
   735  					})
   736  				})
   737  
   738  				Describe("When invoked incorrectly", func() {
   739  					Context("With wrong number of arguments", func() {
   740  						BeforeEach(func() {
   741  							events["test"] = `
   742  								context.network = gohan_model_fetch();`
   743  						})
   744  
   745  						It("Returns the proper error", func() {
   746  							err := env.HandleEvent("test", context)
   747  							Expect(err).To(MatchError(ContainSubstring("arguments")))
   748  						})
   749  					})
   750  
   751  					Context("With wrong schema ID", func() {
   752  						BeforeEach(func() {
   753  							events["test"] = `
   754  								context.network = gohan_model_fetch(context, 'netwerk', 'test1', null);`
   755  						})
   756  
   757  						It("Returns the proper error", func() {
   758  							err := env.HandleEvent("test", context)
   759  							Expect(err).To(MatchError(ContainSubstring("Unknown schema")))
   760  						})
   761  					})
   762  
   763  				})
   764  			})
   765  
   766  			Describe("Using gohan_model_create", func() {
   767  				var (
   768  					tx transaction.Transaction
   769  				)
   770  
   771  				BeforeEach(func() {
   772  					resource, err := manager.LoadResource(schemaID, network1)
   773  					Expect(err).NotTo(HaveOccurred())
   774  					tx, err = testDB.Begin()
   775  					Expect(err).NotTo(HaveOccurred())
   776  					defer tx.Close()
   777  					Expect(tx.Create(resource)).To(Succeed())
   778  					Expect(tx.Commit()).To(Succeed())
   779  
   780  					action = "create"
   781  				})
   782  
   783  				Describe("When invoked correctly", func() {
   784  					Context("With transaction", func() {
   785  						BeforeEach(func() {
   786  							script := `
   787  							context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false});`
   788  							events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"])
   789  						})
   790  
   791  						It("Correctly creates the element", func() {
   792  							tx, err := testDB.Begin()
   793  							Expect(err).NotTo(HaveOccurred())
   794  							defer tx.Close()
   795  							context["transaction"] = tx
   796  							Expect(env.HandleEvent("test", context)).To(Succeed())
   797  							Expect(tx.Commit()).To(Succeed())
   798  							for key, value := range network2 {
   799  								Expect(context).To(HaveKeyWithValue("network", HaveKeyWithValue(key, value)))
   800  							}
   801  						})
   802  					})
   803  
   804  					Context("With chained exception", func() {
   805  						BeforeEach(func() {
   806  							script := `
   807  								context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false});`
   808  							events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"])
   809  							events["post_create_in_transaction"] = `
   810  								throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);`
   811  						})
   812  
   813  						It("Returns the proper error", 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["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente")))
   820  							Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente"))
   821  						})
   822  					})
   823  
   824  					Context("With internal resource manipulation error", func() {
   825  						BeforeEach(func() {
   826  							script := `
   827  								context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false});`
   828  							events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"])
   829  							events["post_create_in_transaction"] = `
   830  								delete context.response;`
   831  						})
   832  
   833  						It("Returns the proper error", func() {
   834  							tx, err := testDB.Begin()
   835  							Expect(err).NotTo(HaveOccurred())
   836  							defer tx.Close()
   837  							context["transaction"] = tx
   838  							err = env.HandleEvent("test", context)
   839  							Expect(err).To(MatchError(ContainSubstring("No response")))
   840  						})
   841  					})
   842  				})
   843  
   844  				Describe("When invoked incorrectly", func() {
   845  					Context("With wrong number of arguments", func() {
   846  						BeforeEach(func() {
   847  							events["test"] = `
   848  								context.network = gohan_model_create();`
   849  						})
   850  
   851  						It("Returns the proper error", func() {
   852  							err := env.HandleEvent("test", context)
   853  							Expect(err).To(MatchError(ContainSubstring("arguments")))
   854  						})
   855  					})
   856  
   857  					Context("With wrong schema ID", func() {
   858  						BeforeEach(func() {
   859  							script := `
   860  								context.network = gohan_model_create(context, 'netwerk', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v'});`
   861  							events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "description", network2["description"], "tenant_id", network2["tenant_id"])
   862  						})
   863  
   864  						It("Returns the proper error", func() {
   865  							err := env.HandleEvent("test", context)
   866  							Expect(err).To(MatchError(ContainSubstring("Unknown schema")))
   867  						})
   868  					})
   869  
   870  					Context("With wrong resource to create", func() {
   871  						BeforeEach(func() {
   872  							events["test"] = `
   873  								context.network = gohan_model_create(context, 'network', 'Ne estas reto');`
   874  						})
   875  
   876  						It("Returns the proper error", func() {
   877  							err := env.HandleEvent("test", context)
   878  							Expect(err).To(MatchError(ContainSubstring("Not a dictionary")))
   879  						})
   880  					})
   881  				})
   882  			})
   883  
   884  			Describe("Using gohan_model_update", func() {
   885  				var (
   886  					tx transaction.Transaction
   887  				)
   888  
   889  				BeforeEach(func() {
   890  					resource, err := manager.LoadResource(schemaID, network1)
   891  					Expect(err).NotTo(HaveOccurred())
   892  					tx, err = testDB.Begin()
   893  					Expect(err).NotTo(HaveOccurred())
   894  					defer tx.Close()
   895  					Expect(tx.Create(resource)).To(Succeed())
   896  					Expect(tx.Commit()).To(Succeed())
   897  
   898  					action = "update"
   899  				})
   900  
   901  				Describe("When invoked correctly", func() {
   902  					Context("With transaction", func() {
   903  						BeforeEach(func() {
   904  							script := `
   905  								context.network = gohan_model_update(context, 'network', 'test1', {'%v': '%v'}, null);`
   906  							events["test"] = fmt.Sprintf(script, "name", network2["name"])
   907  						})
   908  
   909  						It("Correctly updates the element", func() {
   910  							tx, err := testDB.Begin()
   911  							Expect(err).NotTo(HaveOccurred())
   912  							defer tx.Close()
   913  							context["transaction"] = tx
   914  
   915  							Expect(env.HandleEvent("test", context)).To(Succeed())
   916  							Expect(tx.Commit()).To(Succeed())
   917  							Expect(context).To(HaveKeyWithValue("network", HaveKeyWithValue("name", network2["name"])))
   918  						})
   919  					})
   920  
   921  					Context("With chained exception", func() {
   922  						BeforeEach(func() {
   923  							script := `
   924  								context.network = gohan_model_update(context, 'network', 'test1', {'%v': '%v'}, null);`
   925  							events["test"] = fmt.Sprintf(script, "name", network2["name"])
   926  							events["post_update_in_transaction"] = `
   927  								throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);`
   928  						})
   929  
   930  						It("Returns the proper error", func() {
   931  							tx, err := testDB.Begin()
   932  							Expect(err).NotTo(HaveOccurred())
   933  							defer tx.Close()
   934  							context["transaction"] = tx
   935  
   936  							Expect(env.HandleEvent("test", context)).To(Succeed())
   937  							Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente")))
   938  							Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente"))
   939  						})
   940  					})
   941  
   942  					Context("With internal resource manipulation error", func() {
   943  						BeforeEach(func() {
   944  							script := `
   945  								context.network = gohan_model_update(context,  'network', 'test1', {'%v': '%v'}, null);`
   946  							events["test"] = fmt.Sprintf(script, "name", network2["name"])
   947  							events["post_update_in_transaction"] = `
   948  								delete context.response;`
   949  						})
   950  
   951  						It("Returns the proper error", func() {
   952  							tx, err := testDB.Begin()
   953  							Expect(err).NotTo(HaveOccurred())
   954  							defer tx.Close()
   955  							context["transaction"] = tx
   956  
   957  							err = env.HandleEvent("test", context)
   958  							Expect(err).To(MatchError(ContainSubstring("No response")))
   959  						})
   960  					})
   961  				})
   962  
   963  				Describe("When invoked incorrectly", func() {
   964  					Context("With wrong number of arguments", func() {
   965  						BeforeEach(func() {
   966  							events["test"] = `
   967  								context.network = gohan_model_update();`
   968  						})
   969  
   970  						It("Returns the proper error", func() {
   971  							err := env.HandleEvent("test", context)
   972  							Expect(err).To(MatchError(ContainSubstring("arguments")))
   973  						})
   974  					})
   975  
   976  					Context("With wrong schema ID", func() {
   977  						BeforeEach(func() {
   978  							script := `
   979  								context.network = gohan_model_update(context,  'netwerk', 'test1', {'%v': '%v'}, null);`
   980  							events["test"] = fmt.Sprintf(script, "name", network2["name"])
   981  						})
   982  
   983  						It("Returns the proper error", func() {
   984  							err := env.HandleEvent("test", context)
   985  							Expect(err).To(MatchError(ContainSubstring("Unknown schema")))
   986  						})
   987  					})
   988  
   989  					Context("With wrong update data map", func() {
   990  						BeforeEach(func() {
   991  							events["test"] = `
   992  								context.network = gohan_model_update(context, 'network', 'test1', 'Ne estas reto', null);`
   993  						})
   994  
   995  						It("Returns the proper error", func() {
   996  							tx, err := testDB.Begin()
   997  							Expect(err).NotTo(HaveOccurred())
   998  							defer tx.Close()
   999  							context["transaction"] = tx
  1000  
  1001  							err = env.HandleEvent("test", context)
  1002  							Expect(err).To(MatchError(ContainSubstring("Not a dictionary")))
  1003  						})
  1004  					})
  1005  				})
  1006  			})
  1007  
  1008  			Describe("Using gohan_model_delete", func() {
  1009  				var (
  1010  					tx transaction.Transaction
  1011  				)
  1012  
  1013  				BeforeEach(func() {
  1014  					resource, err := manager.LoadResource(schemaID, network1)
  1015  					Expect(err).NotTo(HaveOccurred())
  1016  					tx, err = testDB.Begin()
  1017  					Expect(err).NotTo(HaveOccurred())
  1018  					defer tx.Close()
  1019  					Expect(tx.Create(resource)).To(Succeed())
  1020  					Expect(tx.Commit()).To(Succeed())
  1021  
  1022  					action = "delete"
  1023  				})
  1024  
  1025  				Describe("When invoked correctly", func() {
  1026  					Context("With transaction", func() {
  1027  						BeforeEach(func() {
  1028  							events["test"] = `
  1029  								context.network = gohan_model_delete(context, 'network', 'test1');`
  1030  						})
  1031  
  1032  						It("Correctly deletes the element", func() {
  1033  							tx, err := testDB.Begin()
  1034  							Expect(err).NotTo(HaveOccurred())
  1035  							defer tx.Close()
  1036  							context["transaction"] = tx
  1037  
  1038  							Expect(env.HandleEvent("test", context)).To(Succeed())
  1039  							Expect(tx.Commit()).To(Succeed())
  1040  						})
  1041  					})
  1042  
  1043  					Context("With chained exception", func() {
  1044  						BeforeEach(func() {
  1045  							events["test"] = `
  1046  								context.network = gohan_model_delete(context, 'network', 'test1');`
  1047  							events["post_delete_in_transaction"] = `
  1048  								throw new CustomException("Labori inteligente estas pli bona ol laboregi", 390);`
  1049  						})
  1050  
  1051  						It("Returns the proper error", func() {
  1052  							tx, err := testDB.Begin()
  1053  							Expect(err).NotTo(HaveOccurred())
  1054  							defer tx.Close()
  1055  							context["transaction"] = tx
  1056  
  1057  							Expect(env.HandleEvent("test", context)).To(Succeed())
  1058  							Expect(context["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Labori inteligente")))
  1059  							Expect(context["exception_message"]).To(ContainSubstring("Labori inteligente"))
  1060  						})
  1061  					})
  1062  				})
  1063  
  1064  				Describe("When invoked incorrectly", func() {
  1065  					Context("With wrong number of arguments", func() {
  1066  						BeforeEach(func() {
  1067  							events["test"] = `
  1068  								context.network = gohan_model_delete();`
  1069  						})
  1070  
  1071  						It("Returns the proper error", func() {
  1072  							err := env.HandleEvent("test", context)
  1073  							Expect(err).To(MatchError(ContainSubstring("arguments")))
  1074  						})
  1075  					})
  1076  
  1077  					Context("With wrong schema ID", func() {
  1078  						BeforeEach(func() {
  1079  							events["test"] = `
  1080  								context.network = gohan_model_delete(context, 'netwerk', 'test1');`
  1081  						})
  1082  
  1083  						It("Returns the proper error", func() {
  1084  							err := env.HandleEvent("test", context)
  1085  							Expect(err).To(MatchError(ContainSubstring("Unknown schema")))
  1086  						})
  1087  					})
  1088  				})
  1089  			})
  1090  
  1091  			Describe("Actually chaining them", func() {
  1092  				var (
  1093  					tx                   transaction.Transaction
  1094  					createNetworkContext middleware.Context
  1095  					createSubnetContext  middleware.Context
  1096  					readSubnetContext    middleware.Context
  1097  					subnetEnv            extension.Environment
  1098  					subnetEvents         map[string]string
  1099  				)
  1100  
  1101  				BeforeEach(func() {
  1102  
  1103  					resource, err := manager.LoadResource(schemaID, network1)
  1104  					Expect(err).NotTo(HaveOccurred())
  1105  					tx, err = testDB.Begin()
  1106  					Expect(err).NotTo(HaveOccurred())
  1107  					defer tx.Close()
  1108  					Expect(tx.Create(resource)).To(Succeed())
  1109  					Expect(tx.Commit()).To(Succeed())
  1110  
  1111  					action = "read"
  1112  
  1113  					createNetworkContext = middleware.Context{}
  1114  					createSubnetContext = middleware.Context{}
  1115  					readSubnetContext = middleware.Context{}
  1116  					subnetEvents = map[string]string{}
  1117  				})
  1118  
  1119  				JustBeforeEach(func() {
  1120  					curAction := "create"
  1121  					curSchemaID := "subnet"
  1122  					curSchema, ok := manager.Schema(curSchemaID)
  1123  					Expect(ok).To(BeTrue())
  1124  
  1125  					curPath := curSchema.GetPluralURL()
  1126  
  1127  					curPolicy, curRole := manager.PolicyValidate(curAction, curPath, auth)
  1128  					Expect(curPolicy).NotTo(BeNil())
  1129  					createSubnetContext["policy"] = curPolicy
  1130  					createSubnetContext["role"] = curRole
  1131  					createSubnetContext["tenant_id"] = auth.TenantID()
  1132  					createSubnetContext["tenant_name"] = auth.TenantName()
  1133  					createSubnetContext["auth_token"] = auth.AuthToken()
  1134  					createSubnetContext["catalog"] = auth.Catalog()
  1135  					createSubnetContext["auth"] = auth
  1136  					createSubnetContext["identity_service"] = &middleware.FakeIdentity{}
  1137  
  1138  					curAction = "create"
  1139  					curPolicy, curRole = manager.PolicyValidate(curAction, curPath, auth)
  1140  					Expect(curPolicy).NotTo(BeNil())
  1141  					readSubnetContext["policy"] = curPolicy
  1142  					readSubnetContext["role"] = curRole
  1143  					readSubnetContext["tenant_id"] = auth.TenantID()
  1144  					readSubnetContext["tenant_name"] = auth.TenantName()
  1145  					readSubnetContext["auth_token"] = auth.AuthToken()
  1146  					readSubnetContext["catalog"] = auth.Catalog()
  1147  					readSubnetContext["auth"] = auth
  1148  					readSubnetContext["identity_service"] = &middleware.FakeIdentity{}
  1149  
  1150  					subnetEnv = otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
  1151  					environmentManager.RegisterEnvironment(curSchemaID, subnetEnv)
  1152  					curExtensions := []*schema.Extension{}
  1153  					for event, javascript := range subnetEvents {
  1154  						extension, err := schema.NewExtension(map[string]interface{}{
  1155  							"id":   event + "_extension",
  1156  							"code": `gohan_register_handler("` + event + `", function(context) {` + javascript + `});`,
  1157  							"path": curPath,
  1158  						})
  1159  						Expect(err).ToNot(HaveOccurred())
  1160  						curExtensions = append(curExtensions, extension)
  1161  					}
  1162  					Expect(subnetEnv.LoadExtensionsForPath(curExtensions, curPath)).To(Succeed())
  1163  
  1164  					curAction = "create"
  1165  					curSchemaID = "network"
  1166  					curSchema, ok = manager.Schema(curSchemaID)
  1167  					Expect(ok).To(BeTrue())
  1168  
  1169  					curPath = curSchema.GetPluralURL()
  1170  
  1171  					curPolicy, curRole = manager.PolicyValidate(curAction, curPath, auth)
  1172  					Expect(curPolicy).NotTo(BeNil())
  1173  					createNetworkContext["policy"] = curPolicy
  1174  					createNetworkContext["role"] = curRole
  1175  					createNetworkContext["tenant_id"] = auth.TenantID()
  1176  					createNetworkContext["tenant_name"] = auth.TenantName()
  1177  					createNetworkContext["auth_token"] = auth.AuthToken()
  1178  					createNetworkContext["catalog"] = auth.Catalog()
  1179  					createNetworkContext["auth"] = auth
  1180  					createNetworkContext["identity_service"] = &middleware.FakeIdentity{}
  1181  				})
  1182  
  1183  				Describe("When being chained", func() {
  1184  					Context("Without exceptions", func() {
  1185  						BeforeEach(func() {
  1186  							script := `
  1187  									context.network = gohan_model_create(context,
  1188  										'network', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v',
  1189  											'route_targets': [], 'providor_networks': {}, 'shared': false});`
  1190  							events["test"] = fmt.Sprintf(script, "id",
  1191  								network2["id"], "name", network2["name"],
  1192  								"description", network2["description"], "tenant_id", network2["tenant_id"])
  1193  							script = `
  1194  									console.log("model create");
  1195  									gohan_model_create(
  1196  										{
  1197  											transaction: context.transaction,
  1198  											policy: context.policy,
  1199  										},
  1200  										'subnet',
  1201  										{'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v',
  1202  										'network_id': context.response.network.id, 'description': "test"});`
  1203  							events["post_create_in_transaction"] = fmt.Sprintf(script, "id", subnet1["id"], "name", subnet1["name"], "tenant_id", subnet1["tenant_id"], "cidr", subnet1["cidr"])
  1204  							subnetEvents["test_subnet"] = `
  1205  							context.subnet = gohan_model_fetch(context, 'subnet', 'test3', null);
  1206  							`
  1207  						})
  1208  
  1209  						It("Correctly handles chaining", func() {
  1210  							tx, err := testDB.Begin()
  1211  							Expect(err).NotTo(HaveOccurred())
  1212  							createNetworkContext["transaction"] = tx
  1213  							By("Creating the network")
  1214  							Expect(env.HandleEvent("test", createNetworkContext)).To(Succeed())
  1215  							tx.Commit()
  1216  							tx.Close()
  1217  							By("network created")
  1218  							for key, value := range network2 {
  1219  								Expect(createNetworkContext).To(HaveKeyWithValue("network", HaveKeyWithValue(key, value)))
  1220  							}
  1221  
  1222  							tx, err = testDB.Begin()
  1223  							Expect(err).NotTo(HaveOccurred())
  1224  							readSubnetContext["transaction"] = tx
  1225  
  1226  							By("Also creating a default subnet for it")
  1227  							Expect(subnetEnv.HandleEvent("test_subnet", readSubnetContext)).To(Succeed())
  1228  							tx.Close()
  1229  							for key, value := range subnet1 {
  1230  								Expect(readSubnetContext).To(HaveKey("subnet"))
  1231  								Expect(readSubnetContext).To(HaveKeyWithValue("subnet", HaveKeyWithValue(key, value)))
  1232  							}
  1233  							Expect(readSubnetContext).To(HaveKeyWithValue("subnet", HaveKey("description")))
  1234  						})
  1235  					})
  1236  
  1237  					Context("With an exception", func() {
  1238  						BeforeEach(func() {
  1239  							script := `
  1240  							context.network = gohan_model_create(context, 'network', {'%v': '%v', '%v': '%v', '%v': '%v', 'route_targets': [], 'providor_networks': {}, 'shared': false, 'description': ""});`
  1241  
  1242  							events["test"] = fmt.Sprintf(script, "id", network2["id"], "name", network2["name"], "tenant_id", network2["tenant_id"])
  1243  							script = `
  1244  								gohan_model_create(context,
  1245  									'subnet', {'%v': '%v', '%v': '%v', '%v': '%v', '%v': '%v', 'network_id': context.response.id});`
  1246  							events["post_create_in_transaction"] = fmt.Sprintf(script, "id", subnet1["id"], "name", subnet1["name"], "tenant_id", subnet1["tenant_id"], "cidr", subnet1["cidr"])
  1247  							subnetEvents["pre_create_in_transaction"] = `
  1248  								throw new CustomException("Minas Tirith has fallen!", 390);`
  1249  						})
  1250  
  1251  						It("Returns the proper error", func() {
  1252  							tx, err := testDB.Begin()
  1253  							Expect(err).NotTo(HaveOccurred())
  1254  							defer tx.Close()
  1255  							createNetworkContext["transaction"] = tx
  1256  
  1257  							Expect(env.HandleEvent("test", createNetworkContext)).To(Succeed())
  1258  							Expect(createNetworkContext["exception"]).To(HaveKeyWithValue("message", ContainSubstring("Minas Tirith has fallen!")))
  1259  							Expect(createNetworkContext["exception_message"]).To(ContainSubstring("Minas Tirith has fallen!"))
  1260  						})
  1261  					})
  1262  				})
  1263  			})
  1264  		})
  1265  	})
  1266  	var _ = Describe("Concurrency race", func() {
  1267  		var (
  1268  			env *otto.Environment
  1269  		)
  1270  		channel := make(chan string)
  1271  		Context("Given environment", func() {
  1272  			BeforeEach(func() {
  1273  				env = otto.NewEnvironment(testDB, &middleware.FakeIdentity{})
  1274  				env.SetUp()
  1275  				vm := env.VM
  1276  				builtins := map[string]interface{}{
  1277  					"test_consume": func(call ottopkg.FunctionCall) ottopkg.Value {
  1278  						result := <-channel
  1279  						ottoResult, _ := vm.ToValue(result)
  1280  						return ottoResult
  1281  					},
  1282  					"test_produce": func(call ottopkg.FunctionCall) ottopkg.Value {
  1283  						ottoProduct := otto.ConvertOttoToGo(call.Argument(0))
  1284  						product := otto.ConvertOttoToGo(ottoProduct).(string)
  1285  						channel <- product
  1286  						return ottopkg.NullValue()
  1287  					},
  1288  				}
  1289  				for name, object := range builtins {
  1290  					vm.Set(name, object)
  1291  				}
  1292  
  1293  				Expect(env.Load("<race_test>", `
  1294  				var produce = function() {
  1295  					for (var i = 0; i < 10; i++) {
  1296  						console.log("producing:", i);
  1297  						test_produce(i.toString());
  1298  					}
  1299  				};
  1300  
  1301  				var consume = function() {
  1302  					for (var i = 0; i < 10; i++) {
  1303  						var result = test_consume();
  1304  						console.log("consumed:", result);
  1305  					}
  1306  				}`)).To(BeNil())
  1307  				environmentManager.RegisterEnvironment("test_race", env)
  1308  			})
  1309  
  1310  			It("Should work", func() {
  1311  				var consumerError = make(chan error)
  1312  				var producerError = make(chan error)
  1313  
  1314  				go func() {
  1315  					testEnv, _ := environmentManager.GetEnvironment("test_race")
  1316  					ottoEnv := testEnv.(*otto.Environment)
  1317  					_, err := ottoEnv.VM.Call("consume", nil)
  1318  					consumerError <- err
  1319  				}()
  1320  
  1321  				go func() {
  1322  					testEnv, _ := environmentManager.GetEnvironment("test_race")
  1323  					ottoEnv := testEnv.(*otto.Environment)
  1324  					_, err := ottoEnv.VM.Call("produce", nil)
  1325  					producerError <- err
  1326  				}()
  1327  
  1328  				select {
  1329  				case err := <-consumerError:
  1330  					Expect(err).To(BeNil())
  1331  				case <-time.After(1 * time.Second):
  1332  					Fail("Timeout when waiting for consumer")
  1333  				}
  1334  				select {
  1335  				case err := <-producerError:
  1336  					Expect(err).To(BeNil())
  1337  				case <-time.After(1 * time.Second):
  1338  					Fail("Timeout when waiting for producer")
  1339  				}
  1340  			})
  1341  		})
  1342  	})
  1343  })