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  })