Пример #1
0
func TestErrorMetadata(t *testing.T) {
	var checkParseErr = func(path string, expErr string) {
		sc := scanner.Scanner{
			Position: scanner.Position{
				Filename: path,
			},
		}
		f, err := util.Open(path)
		if err != nil {
			t.Errorf("Couldn't open %s", path)
		}
		_, err = parse(*sc.Init(f))
		if err.Error() != expErr {
			t.Errorf("Expected \"%s\"\ngot \"%s\"", expErr, err)
			return
		}
	}
	var checkEvalErr = func(path string, expErr string) {
		sc := scanner.Scanner{
			Position: scanner.Position{
				Filename: path,
			},
		}
		f, err := util.Open(path)
		if err != nil {
			t.Errorf("Couldn't open %s", path)
		}
		parsed, err := parse(*sc.Init(f))
		if err != nil {
			t.Errorf("Unexpected parse error: %s", parsed)
		}

		_, _, err = eval(astRoot(parsed))
		if err.Error() != expErr {
			t.Errorf("Expected \"%s\"\ngot \"%s\"", expErr, err)
			return
		}
	}

	util.AppFs = afero.NewMemMapFs()
	util.WriteFile("paren.spec", []byte(`
// This is a comment.
(define Test "abc"`), 0644)
	checkParseErr("paren.spec", "paren.spec:3: unbalanced Parenthesis")

	util.WriteFile("undefined.spec", []byte(`
(+ 1 b)`), 0644)
	checkEvalErr("undefined.spec", "undefined.spec:2: unassigned variable: b")

	util.WriteFile("bad_type.spec", []byte(`
(define a "1")
(+ 1 a)`), 0644)
	checkEvalErr("bad_type.spec", `bad_type.spec:3: bad arithmetic argument: "1"`)

	// Test that functions which evaluate generated S-expressions still have proper
	// error messages.
	util.WriteFile("generated_sexp.spec", []byte(`(apply + (list 1 "1"))`), 0644)
	checkEvalErr("generated_sexp.spec",
		`generated_sexp.spec:1: bad arithmetic argument: "1"`)
}
Пример #2
0
func testCheckSpec(t *testing.T) {
	initVCSFunc()
	os.Setenv("QUILT_PATH", ".")
	util.AppFs = afero.NewMemMapFs()
	util.AppFs.Mkdir("test", 777)
	util.WriteFile("test/noDownload.spec",
		[]byte(`(import "nextimport/nextimport")`), 0644)
	util.AppFs.Mkdir("nextimport", 777)
	util.WriteFile("nextimport/nextimport.spec", []byte("(define dummy 1)"), 0644)
	if err := checkSpec("test/noDownload.spec", nil, nil); err != nil {
		t.Error(err)
	}

	if len(created) != 0 {
		t.Errorf("should not have downloaded, but downloaded %s", created)
	}

	// Verify that call is made to GetSpec
	util.WriteFile("test/toDownload.spec",
		[]byte(`(import "github.com/NetSys/quilt/specs/example")`), 0644)
	expected := "unable to open import github.com/NetSys/quilt/specs/example"
	if err := checkSpec("test/toDownload.spec", nil, nil); err.Error() != expected {
		t.Errorf("expected %s \n but got %s", expected, err.Error())
	}

	if len(created) == 0 {
		t.Error("did not download dependency!")
	}

	expected = "github.com/NetSys/quilt"
	if created[0] != expected {
		t.Errorf("expected to download %s \n but got %s", expected, created[0])
	}
}
Пример #3
0
func TestAutoDownload(t *testing.T) {
	util.AppFs = afero.NewMemMapFs()

	repoName := "autodownload"
	importPath := filepath.Join(repoName, "foo")
	util.WriteFile("test.js", []byte(fmt.Sprintf("require(%q);", importPath)), 0644)

	logger := newRepoLogger()
	getter := ImportGetter{
		Path:        ".",
		repoFactory: logger.newRepoFactory(nil),
	}

	expErr := "StitchError: unable to open import autodownload/foo: no loadable file"
	err := getter.checkSpec("test.js", nil, nil)
	if err == nil || err.Error() != expErr {
		t.Errorf("Wrong error, expected %q, got %v", expErr, err)
		return
	}

	assert.Equal(t, map[string][]string{
		importPath: {repoName},
	}, logger.created, "Should autodownload the repo")
	assert.Empty(t, logger.updated, "Shouldn't update any repos")
}
Пример #4
0
func TestPromptsUser(t *testing.T) {
	oldConfirm := confirm
	defer func() {
		confirm = oldConfirm
	}()

	util.AppFs = afero.NewMemMapFs()
	for _, confirmResp := range []bool{true, false} {
		confirm = func(in io.Reader, prompt string) (bool, error) {
			return confirmResp, nil
		}

		mockGetter := new(testutils.Getter)
		c := &clientMock.Client{
			ClusterReturn: []db.Cluster{
				{
					Spec: `{"old":"spec"}`,
				},
			},
		}
		mockGetter.On("Client", mock.Anything).Return(c, nil)

		util.WriteFile("test.js", []byte(""), 0644)
		runCmd := NewRunCommand()
		runCmd.clientGetter = mockGetter
		runCmd.stitch = "test.js"
		runCmd.Run()
		assert.Equal(t, confirmResp, c.DeployArg != "")
	}
}
Пример #5
0
func TestImportExists(t *testing.T) {
	util.AppFs = afero.NewMemMapFs()

	util.WriteFile("test.js", []byte(`require("existingImport")`), 0644)
	util.WriteFile("existingImport.js", []byte(""), 0644)

	logger := newRepoLogger()
	getter := ImportGetter{
		Path:        ".",
		repoFactory: logger.newRepoFactory(nil),
	}
	if err := getter.checkSpec("test.js", nil, nil); err != nil {
		t.Error(err)
		return
	}

	assert.Empty(t, logger.created, "Shouldn't create any repos")
	assert.Empty(t, logger.updated, "Shouldn't update any repos")
}
Пример #6
0
func (mr *mockRepo) create(dir string) error {
	for _, toCreate := range mr.toCreate {
		util.WriteFile(
			filepath.Join(dir, toCreate.name),
			[]byte(toCreate.contents),
			0644)
	}
	mr.logger.created[mr.repoName] = append(mr.logger.created[mr.repoName], dir)
	return nil
}
Пример #7
0
func TestMain(t *testing.T) {
	util.AppFs = afero.NewMemMapFs()
	util.WriteFile("test.js", []byte(testStitch), 0644)

	exitCode := Main([]string{"test.js", "graphviz"})

	assert.Zero(t, exitCode)
	res, err := util.ReadFile("test.dot")
	assert.Nil(t, err)
	assert.True(t, isGraphEqual(expGraph, res))
}
Пример #8
0
func initMachine(cloudConfig string, size string, id string) error {
	vdir, err := vagrantDir()
	if err != nil {
		return err
	}
	if _, err := os.Stat(vdir); os.IsNotExist(err) {
		os.Mkdir(vdir, os.ModeDir|os.ModePerm)
	}
	path := vdir + id
	os.Mkdir(path, os.ModeDir|os.ModePerm)

	_, stderr, err := shell(id, `vagrant --machine-readable init coreos-beta`)
	if err != nil {
		log.Errorf("Failed to initialize Vagrant environment: %s", stderr)
		destroy(id)
		return errors.New("unable to init machine")
	}

	err = util.WriteFile(path+"/user-data", []byte(cloudConfig), 0644)
	if err != nil {
		destroy(id)
		return err
	}

	err = util.WriteFile(path+"/vagrantFile", []byte(vagrantFile), 0644)
	if err != nil {
		destroy(id)
		return err
	}

	err = util.WriteFile(path+"/size", []byte(size), 0644)
	if err != nil {
		destroy(id)
		return err
	}

	return nil
}
Пример #9
0
func (api vagrantAPI) Init(cloudConfig string, size string, id string) error {
	vdir, err := api.VagrantDir()
	if err != nil {
		return err
	}
	if _, err := os.Stat(vdir); os.IsNotExist(err) {
		os.Mkdir(vdir, os.ModeDir|os.ModePerm)
	}
	path := vdir + id
	os.Mkdir(path, os.ModeDir|os.ModePerm)

	_, err = api.Shell(id, `vagrant --machine-readable init coreos-beta`)
	if err != nil {
		api.Destroy(id)
		return err
	}

	err = util.WriteFile(path+"/user-data", []byte(cloudConfig), 0644)
	if err != nil {
		api.Destroy(id)
		return err
	}

	vagrant := vagrantFile()
	err = util.WriteFile(path+"/vagrantFile", []byte(vagrant), 0644)
	if err != nil {
		api.Destroy(id)
		return err
	}

	err = util.WriteFile(path+"/size", []byte(size), 0644)
	if err != nil {
		api.Destroy(id)
		return err
	}

	return nil
}
Пример #10
0
func TestSyncKeys(t *testing.T) {
	tests := []keyTest{
		{
			dbKeys:     "key1\nkey2",
			expKeyFile: "key1\nkey2",
		},
		{
			dbKeys:     "key1\nkey2",
			keyFile:    "key1",
			expKeyFile: "key1\nkey2",
		},
		{
			dbKeys:     "key1\nkey2",
			keyFile:    "key1\nkey2",
			expKeyFile: "key1\nkey2",
		},
		{
			keyFile:    "key1\nkey2",
			expKeyFile: "",
		},
	}
	for _, test := range tests {
		util.AppFs = afero.NewMemMapFs()
		if test.keyFile != "" {
			err := util.WriteFile(
				authorizedKeysFile, []byte(test.keyFile), 0644)
			assert.NoError(t, err)
		}

		conn := db.New()
		conn.Transact(func(view db.Database) error {
			m := view.InsertMinion()
			m.Self = true
			m.AuthorizedKeys = test.dbKeys
			view.Commit(m)
			return nil
		})

		err := runOnce(conn)
		assert.NoError(t, err)

		actual, err := util.ReadFile(authorizedKeysFile)
		assert.NoError(t, err)
		assert.Equal(t, test.expKeyFile, actual)
	}
}
Пример #11
0
func runOnce(conn db.Conn) error {
	if _, err := util.AppFs.Stat(authorizedKeysFile); os.IsNotExist(err) {
		util.AppFs.Create(authorizedKeysFile)
	}
	currKeys, err := util.ReadFile(authorizedKeysFile)
	if err != nil {
		return err
	}

	m, err := conn.MinionSelf()
	if err != nil {
		return err
	}

	if m.AuthorizedKeys == currKeys {
		return nil
	}

	return util.WriteFile(authorizedKeysFile, []byte(m.AuthorizedKeys), 0644)
}
Пример #12
0
func TestRequire(t *testing.T) {
	squarer := `exports.square = function(x) {
		return x*x;
	};`
	tests := []requireTest{
		// Import returning a primitive.
		{
			files: []file{
				{
					name:     "/quilt_path/math.js",
					contents: squarer,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require("math").square(5);`,
			expVal:    float64(25),
		},
		// Import returning a type.
		{
			files: []file{
				{
					name: "/quilt_path/testImport.js",
					contents: `exports.getService = function() {
		return new Service("foo", []);
		};`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require("testImport").getService().hostname();`,
			expVal:    "foo.q",
		},
		// Import with an import.
		{
			files: []file{
				{
					name:     "/quilt_path/square.js",
					contents: squarer,
				},
				{
					name: "/quilt_path/cube.js",
					contents: `var square = require("square");
					exports.cube = function(x) {
						return x * square.square(x);
					};`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('cube').cube(5);`,
			expVal:    float64(125),
		},
		// Directly assigned exports.
		{
			files: []file{
				{
					name: "/quilt_path/square.js",
					contents: `module.exports = function(x) {
						return x*x
					}`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('square')(5);`,
			expVal:    float64(25),
		},
		// Test self import cycle.
		{
			files: []file{
				{
					name:     "/quilt_path/A.js",
					contents: `require("A");`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require("A");`,
			expErr:    `StitchError: import cycle: [A A]`,
		},
		// Test transitive import cycle.
		{
			files: []file{
				{
					name:     "/quilt_path/A.js",
					contents: `require("B");`,
				},
				{
					name:     "/quilt_path/B.js",
					contents: `require("A");`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('A');`,
			expErr:    `StitchError: import cycle: [A B A]`,
		},
		// No error if there's a path between two imports, but no cycle.
		{
			files: []file{
				{
					name:     "/quilt_path/A.js",
					contents: `require("B");`,
				},
				{
					name: "/quilt_path/B.js",
				},
			},
			quiltPath: "/quilt_path",
			mainFile: `require('B');
			require('A');
			"end"`,
			expVal: "end",
		},
		// Absolute import.
		{
			files: []file{
				{
					name:     "/abs/import.js",
					contents: squarer,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('/abs/import').square(5);`,
			expVal:    float64(25),
		},
		// Relative import.
		{
			files: []file{
				{
					name:     "/quilt_path/rel/square.js",
					contents: squarer,
				},
				{
					name: "/quilt_path/cube.js",
					contents: `var square = require("./rel/square");
					exports.cube = function(x) {
						return x * square.square(x);
					};`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('cube').cube(5);`,
			expVal:    float64(125),
		},
		// JSON import.
		{
			files: []file{
				{
					name: "/quilt_path/static.json",
					contents: `{
						"key": "val"
					}`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('static')['key'];`,
			expVal:    "val",
		},
		// JSON import of improperly formatted file.
		{
			files: []file{
				{
					name: "/quilt_path/static.json",
					contents: `{
						key: "val"
					}`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('static');`,
			expErr: "StitchError: unable to open import static: " +
				"invalid character 'k' looking for beginning of " +
				"object key string",
		},
		// Directory import with index.js.
		{
			files: []file{
				{
					name:     "/quilt_path/square/index.js",
					contents: squarer,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('square').square(5);`,
			expVal:    float64(25),
		},
		// Directory import with package.json.
		{
			files: []file{
				{
					name: "/quilt_path/square/package.json",
					contents: `{
						"main": "./foo.js"
					}
					`,
				},
				{
					name:     "/quilt_path/square/foo.js",
					contents: squarer,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('square').square(5);`,
			expVal:    float64(25),
		},
		// package.json formatting errors.
		{
			files: []file{
				{
					name: "/quilt_path/pkg-json/package.json",
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('pkg-json')`,
			expErr: "StitchError: unable to open import pkg-json: " +
				"unexpected end of JSON input",
		},
		{
			files: []file{
				{
					name:     "/quilt_path/pkg-json/package.json",
					contents: `{"main": 2}`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('pkg-json')`,
			expErr: "StitchError: unable to open import pkg-json: " +
				"bad package.json format",
		},
		// Missing files errors.
		{
			files: []file{
				{
					name:     "/quilt_path/pkg-json/package.json",
					contents: `{"main": "nonexistent"}`,
				},
			},
			quiltPath: "/quilt_path",
			mainFile:  `require('pkg-json')`,
			expErr: "StitchError: unable to open import pkg-json: " +
				"no loadable file",
		},
		{
			mainFile: `require('missing')`,
			expErr: "StitchError: unable to open import missing: " +
				"no loadable file",
		},
	}
	for _, test := range tests {
		util.AppFs = afero.NewMemMapFs()
		for _, f := range test.files {
			util.WriteFile(f.name, []byte(f.contents), 0644)
		}

		testVM, _ := newVM(ImportGetter{
			Path: test.quiltPath,
		})
		res, err := run(testVM, "main.js", test.mainFile)

		if err != nil || test.expErr != "" {
			assert.EqualError(t, err, test.expErr)
		} else {
			resIntf, _ := res.Export()
			assert.Equal(t, test.expVal, resIntf)
		}
	}
}
Пример #13
0
func TestImport(t *testing.T) {
	// Test module keyword
	code := `(module "math" (define Square (lambda (x) (* x x)))) (math.Square 2)`
	parseTest(t, code, `(module "math" (list)) 4`)

	// Test module with multiple-statement body
	parseTest(t,
		`(module "math" (define three 3)
		(define Triple (lambda (x) (* three x))))
		(math.Triple 2)`, `(module "math" (list) (list)) 6`)

	// Test importing from disk
	testFs := afero.NewMemMapFs()
	util.AppFs = testFs
	util.WriteFile("math.spec", []byte("(define Square (lambda (x) (* x x)))"), 0644)
	util.AppFs = testFs
	parseTestImport(t, `(import "math") (math.Square 2)`, `(module "math" (list)) 4`,
		".")

	// Test two imports in separate directories
	testFs = afero.NewMemMapFs()
	util.AppFs = testFs
	testFs.Mkdir("square", 777)
	util.WriteFile("square/square.spec",
		[]byte("(define Square (lambda (x) (* x x)))"), 0644)
	testFs.Mkdir("cube", 777)
	util.WriteFile("cube/cube.spec",
		[]byte("(define Cube (lambda (x) (* x x x)))"), 0644)
	parseTestImport(t,
		`(import "square/square")
		(import "cube/cube")
		(square.Square 2)
		(cube.Cube 2)`,
		`(module "square" (list))
		(module "cube" (list)) 4 8`,
		".")

	// Test import with an import
	testFs = afero.NewMemMapFs()
	util.AppFs = testFs
	util.WriteFile("square.spec", []byte("(define Square (lambda (x) (* x x)))"),
		0644)
	util.WriteFile("cube.spec", []byte(`(import "square")
	(define Cube (lambda (x) (* x (square.Square x))))`), 0644)
	parseTestImport(t, `(import "cube") (cube.Cube 2)`,
		`(module "cube" (module "square" (list)) (list)) 8`,
		".")

	// Test error in an imported module
	testFs = afero.NewMemMapFs()
	util.AppFs = testFs
	util.WriteFile("bad.spec", []byte(`(define BadFunc (lambda () (+ 1 "1")))`), 0644)
	runtimeErrImport(t, `(import "bad") (bad.BadFunc)`,
		`bad.spec:1: bad arithmetic argument: "1"`, ".")

	testFs = afero.NewMemMapFs()
	util.AppFs = testFs
	util.WriteFile("A.spec", []byte(`(import "A")`), 0644)
	importErr(t, `(import "A")`, `import cycle: [A A]`, ".")

	testFs = afero.NewMemMapFs()
	util.AppFs = testFs
	util.WriteFile("A.spec", []byte(`(import "B")`), 0644)
	util.WriteFile("B.spec", []byte(`(import "A")`), 0644)
	importErr(t, `(import "A")`, `import cycle: [A B A]`, ".")

	testFs = afero.NewMemMapFs()
	util.AppFs = testFs
	util.WriteFile("A.spec", []byte(`(import "B")
	(import "C")
	(define AddTwo (lambda (x) (+ (B.AddOne x) C.One)))`), 0644)
	util.WriteFile("B.spec", []byte(`(import "C")
	(define AddOne (lambda (x) (+ x C.One)))`), 0644)
	util.WriteFile("C.spec", []byte(`(define One 1)`), 0644)
	parseTestImport(t, `(import "A") (A.AddTwo 1)`,
		`(module "A" (module "B" (module "C" (list)) (list))
		(module "C" (list))
		(list)) 3`, ".")

	// Test that non-capitalized binds are not exported
	runtimeErr(t, `(module "A" (define addOne (lambda (x) (+ x 1))))
		(A.addOne 1)`, `2: unknown function: A.addOne`)
}
Пример #14
0
func TestRunSpec(t *testing.T) {
	os.Setenv("QUILT_PATH", "/quilt_path")
	stitch.DefaultImportGetter.Path = "/quilt_path"

	exJavascript := `deployment.deploy(new Machine({}));`
	exJSON := `{"Containers":[],"Labels":[],"Connections":[],"Placements":[],` +
		`"Machines":[{"Provider":"","Role":"","Size":"",` +
		`"CPU":{"Min":0,"Max":0},"RAM":{"Min":0,"Max":0},"DiskSize":0,` +
		`"Region":"","SSHKeys":[]}],"AdminACL":[],"MaxPrice":0,` +
		`"Namespace":"default-namespace","Invariants":[]}`
	tests := []runTest{
		{
			files: []file{
				{
					path:     "test.js",
					contents: exJavascript,
				},
			},
			path:         "test.js",
			expExitCode:  0,
			expDeployArg: exJSON,
		},
		{
			path:        "dne.js",
			expExitCode: 1,
			expEntries: []log.Entry{
				{
					Message: "open /quilt_path/dne.js: " +
						"file does not exist",
					Level: log.ErrorLevel,
				},
			},
		},
		{
			path:        "/dne.js",
			expExitCode: 1,
			expEntries: []log.Entry{
				{
					Message: "open /dne.js: file does not exist",
					Level:   log.ErrorLevel,
				},
			},
		},
		{
			files: []file{
				{
					path:     "/quilt_path/in_quilt_path.js",
					contents: exJavascript,
				},
			},
			path:         "in_quilt_path",
			expDeployArg: exJSON,
		},
		// Ensure we print a stacktrace when available.
		{
			files: []file{
				{
					path:     "/quilt_path/A.js",
					contents: `require("B").foo();`,
				},
				{
					path: "/quilt_path/B.js",
					contents: `module.exports.foo = function() {
						throw new Error("bar");
					}`,
				},
			},
			path:        "/quilt_path/A.js",
			expExitCode: 1,
			expEntries: []log.Entry{
				{
					Message: "Error: bar\n" +
						"    at /quilt_path/B.js:2:17\n" +
						"    at /quilt_path/A.js:1:67\n",
					Level: log.ErrorLevel,
				},
			},
		},
	}
	for _, test := range tests {
		util.AppFs = afero.NewMemMapFs()

		mockGetter := new(testutils.Getter)
		c := &clientMock.Client{}
		mockGetter.On("Client", mock.Anything).Return(c, nil)

		logHook := logrusTestHook.NewGlobal()

		for _, f := range test.files {
			util.WriteFile(f.path, []byte(f.contents), 0644)
		}
		runCmd := NewRunCommand()
		runCmd.clientGetter = mockGetter
		runCmd.stitch = test.path
		exitCode := runCmd.Run()

		assert.Equal(t, test.expExitCode, exitCode)
		assert.Equal(t, test.expDeployArg, c.DeployArg)

		assert.Equal(t, len(test.expEntries), len(logHook.Entries))
		for i, entry := range logHook.Entries {
			assert.Equal(t, test.expEntries[i].Message, entry.Message)
			assert.Equal(t, test.expEntries[i].Level, entry.Level)
		}
	}
}