<commit_before>package testing

import (
	"database/sql"
	"fmt"
	"log"
	"net/url"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach-go/testserver"

	// Import postgres driver.
	_ "github.com/lib/pq"
)

// application represents a single instance of an application running an ORM and
// exposing an HTTP REST API.
type application struct {
	language string
	orm      string
}

func (app application) name() string {
	return fmt.Sprintf("%s/%s", app.language, app.orm)
}

func (app application) dir() string {
	return fmt.Sprintf("../%s", app.name())
}

func (app application) dbName() string {
	return fmt.Sprintf("company_%s", app.orm)
}

// customURLSchemes contains custom schemes for database URLs that are needed
// for test apps that rely on a custom ORM dialect.
var customURLSchemes = map[application]string{
	{language: "python", orm: "sqlalchemy"}: "cockroachdb",
}

// initTestDatabase launches a test database as a subprocess.
func initTestDatabase(t *testing.T, app application) (*sql.DB, *url.URL, func()) {
	ts, err := testserver.NewTestServer()
	if err != nil {
		t.Fatal(err)
	}

	if err := ts.Start(); err != nil {
		t.Fatal(err)
	}

	url := ts.PGURL()
	if url == nil {
		t.Fatalf("url not found")
	}
	url.Path = app.dbName()

	db, err := sql.Open("postgres", url.String())
	if err != nil {
		t.Fatal(err)
	}

	ts.WaitForInit(db)

	// Create the database if it does not exist.
	if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + app.dbName()); err != nil {
		t.Fatal(err)
	}

	if scheme, ok := customURLSchemes[app]; ok {
		url.Scheme = scheme
	}
	return db, url, func() {
		_ = db.Close()
		ts.Stop()
	}
}

// initORMApp launches an ORM application as a subprocess and returns a
// function that terminates that process.
func initORMApp(app application, dbURL *url.URL) (func() error, error) {
	cmd := exec.Command("make", "start", "-C", app.dir(), "ADDR="+dbURL.String())
	cmd.Stdout = os.Stderr
	cmd.Stderr = os.Stderr

	// make will launch the application in a child process, and this is the most
	// straightforward way to kill all ancestors.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	killCmd := func() error {
		if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
			return err
		}
		// This error is expected.
		if err := cmd.Wait(); err.Error() != "signal: "+syscall.SIGKILL.String() {
			return err
		}

		// Killing a process is not instant. For example, with the Hibernate server,
		// it often takes ~10 seconds for the listen port to become available after
		// this function is called. This is despite the above code that issues a
		// SIGKILL to the process group for the test server.
		for {
			if !(apiHandler{}).canDial() {
				break
			}
			log.Printf("waiting for app server port to become available")
			time.Sleep(time.Second)
		}

		return nil
	}

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("command %s failed to start: args=%s", cmd.Args, err)
	}

	const maxWait = 3 * time.Minute
	const waitDelay = 250 * time.Millisecond

	for waited := time.Duration(0); ; waited += waitDelay {
		if processState := cmd.ProcessState; processState != nil && processState.Exited() {
			return nil, fmt.Errorf("command %s exited: %v", cmd.Args, cmd.Wait())
		}
		if err := (apiHandler{}).ping(app.name()); err != nil {
			if waited > maxWait {
				if err := killCmd(); err != nil {
					log.Printf("failed to kill command %s with PID %d: %s", cmd.Args, cmd.ProcessState.Pid(), err)
				}
				return nil, err
			}
			time.Sleep(waitDelay)
			continue
		}
		return killCmd, nil
	}
}

func testORM(t *testing.T, language, orm string) {
	app := application{
		language: language,
		orm:      orm,
	}

	db, dbURL, stopDB := initTestDatabase(t, app)
	defer stopDB()

	td := testDriver{
		db:     db,
		dbName: app.dbName(),
	}

	t.Run("FirstRun", func(t *testing.T) {
		stopApp, err := initORMApp(app, dbURL)
		if err != nil {
			t.Fatal(err)
		}
		defer func() {
			if err := stopApp(); err != nil {
				t.Fatal(err)
			}
		}()

		// Test that the correct tables were generated.
		t.Run("GeneratedTables", td.TestGeneratedTables)

		// Test that the correct columns in those tables were generated.
		t.Run("GeneratedColumns", parallelTestGroup{
			"CustomersTable":     td.TestGeneratedCustomersTableColumns,
			"ProductsTable":      td.TestGeneratedProductsTableColumns,
			"OrdersTable":        td.TestGeneratedOrdersTableColumns,
			"OrderProductsTable": td.TestGeneratedOrderProductsTableColumns,
		}.T)

		// Test that the tables begin empty.
		t.Run("EmptyTables", parallelTestGroup{
			"CustomersTable":     td.TestCustomersEmpty,
			"ProductsTable":      td.TestProductsTableEmpty,
			"OrdersTable":        td.TestOrdersTableEmpty,
			"OrderProductsTable": td.TestOrderProductsTableEmpty,
		}.T)

		// Test that the API returns empty sets for each collection.
		t.Run("RetrieveFromAPIBeforeCreation", parallelTestGroup{
			"Customers": td.TestRetrieveCustomersBeforeCreation,
			"Products":  td.TestRetrieveProductsBeforeCreation,
			"Orders":    td.TestRetrieveOrdersBeforeCreation,
		}.T)

		// Test the creation of initial objects.
		t.Run("CreateCustomer", td.TestCreateCustomer)
		t.Run("CreateProduct", td.TestCreateProduct)

		// Test that the API returns what we just created.
		t.Run("RetrieveFromAPIAfterInitialCreation", parallelTestGroup{
			"Customers": td.TestRetrieveCustomerAfterCreation,
			"Products":  td.TestRetrieveProductAfterCreation,
		}.T)

		// Test the creation of dependent objects.
		t.Run("CreateOrder", td.TestCreateOrder)

		// Test that the API returns what we just created.
		t.Run("RetrieveFromAPIAfterDependentCreation", parallelTestGroup{
			"Order": td.TestRetrieveProductAfterCreation,
		}.T)
	})

	t.Run("SecondRun", func(t *testing.T) {
		stopApp, err := initORMApp(app, dbURL)
		if err != nil {
			t.Fatal(err)
		}
		defer func() {
			if err := stopApp(); err != nil {
				t.Fatal(err)
			}
		}()

		// Test that the API still returns all created objects.
		t.Run("RetrieveFromAPIAfterRestart", parallelTestGroup{
			"Customers": td.TestRetrieveCustomerAfterCreation,
			"Products":  td.TestRetrieveProductAfterCreation,
			"Order":     td.TestRetrieveProductAfterCreation,
		}.T)
	})
}

func TestGORM(t *testing.T) {
	testORM(t, "go", "gorm")
}

func TestHibernate(t *testing.T) {
	testORM(t, "java", "hibernate")
}

func TestSequelize(t *testing.T) {
	testORM(t, "node", "sequelize")
}

func TestSQLAlchemy(t *testing.T) {
	testORM(t, "python", "sqlalchemy")
}

func TestActiveRecord(t *testing.T) {
	testORM(t, "ruby", "activerecord")
}

func TestActiveRecord4(t *testing.T) {
	testORM(t, "ruby", "ar4")
}
<commit_msg>skip SQL Alchemy tests<commit_after>package testing

import (
	"database/sql"
	"fmt"
	"log"
	"net/url"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach-go/testserver"

	// Import postgres driver.
	_ "github.com/lib/pq"
)

// application represents a single instance of an application running an ORM and
// exposing an HTTP REST API.
type application struct {
	language string
	orm      string
}

func (app application) name() string {
	return fmt.Sprintf("%s/%s", app.language, app.orm)
}

func (app application) dir() string {
	return fmt.Sprintf("../%s", app.name())
}

func (app application) dbName() string {
	return fmt.Sprintf("company_%s", app.orm)
}

// customURLSchemes contains custom schemes for database URLs that are needed
// for test apps that rely on a custom ORM dialect.
var customURLSchemes = map[application]string{
	{language: "python", orm: "sqlalchemy"}: "cockroachdb",
}

// initTestDatabase launches a test database as a subprocess.
func initTestDatabase(t *testing.T, app application) (*sql.DB, *url.URL, func()) {
	ts, err := testserver.NewTestServer()
	if err != nil {
		t.Fatal(err)
	}

	if err := ts.Start(); err != nil {
		t.Fatal(err)
	}

	url := ts.PGURL()
	if url == nil {
		t.Fatalf("url not found")
	}
	url.Path = app.dbName()

	db, err := sql.Open("postgres", url.String())
	if err != nil {
		t.Fatal(err)
	}

	ts.WaitForInit(db)

	// Create the database if it does not exist.
	if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS " + app.dbName()); err != nil {
		t.Fatal(err)
	}

	if scheme, ok := customURLSchemes[app]; ok {
		url.Scheme = scheme
	}
	return db, url, func() {
		_ = db.Close()
		ts.Stop()
	}
}

// initORMApp launches an ORM application as a subprocess and returns a
// function that terminates that process.
func initORMApp(app application, dbURL *url.URL) (func() error, error) {
	cmd := exec.Command("make", "start", "-C", app.dir(), "ADDR="+dbURL.String())
	cmd.Stdout = os.Stderr
	cmd.Stderr = os.Stderr

	// make will launch the application in a child process, and this is the most
	// straightforward way to kill all ancestors.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	killCmd := func() error {
		if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
			return err
		}
		// This error is expected.
		if err := cmd.Wait(); err.Error() != "signal: "+syscall.SIGKILL.String() {
			return err
		}

		// Killing a process is not instant. For example, with the Hibernate server,
		// it often takes ~10 seconds for the listen port to become available after
		// this function is called. This is despite the above code that issues a
		// SIGKILL to the process group for the test server.
		for {
			if !(apiHandler{}).canDial() {
				break
			}
			log.Printf("waiting for app server port to become available")
			time.Sleep(time.Second)
		}

		return nil
	}

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("command %s failed to start: args=%s", cmd.Args, err)
	}

	const maxWait = 3 * time.Minute
	const waitDelay = 250 * time.Millisecond

	for waited := time.Duration(0); ; waited += waitDelay {
		if processState := cmd.ProcessState; processState != nil && processState.Exited() {
			return nil, fmt.Errorf("command %s exited: %v", cmd.Args, cmd.Wait())
		}
		if err := (apiHandler{}).ping(app.name()); err != nil {
			if waited > maxWait {
				if err := killCmd(); err != nil {
					log.Printf("failed to kill command %s with PID %d: %s", cmd.Args, cmd.ProcessState.Pid(), err)
				}
				return nil, err
			}
			time.Sleep(waitDelay)
			continue
		}
		return killCmd, nil
	}
}

func testORM(t *testing.T, language, orm string) {
	app := application{
		language: language,
		orm:      orm,
	}

	db, dbURL, stopDB := initTestDatabase(t, app)
	defer stopDB()

	td := testDriver{
		db:     db,
		dbName: app.dbName(),
	}

	t.Run("FirstRun", func(t *testing.T) {
		stopApp, err := initORMApp(app, dbURL)
		if err != nil {
			t.Fatal(err)
		}
		defer func() {
			if err := stopApp(); err != nil {
				t.Fatal(err)
			}
		}()

		// Test that the correct tables were generated.
		t.Run("GeneratedTables", td.TestGeneratedTables)

		// Test that the correct columns in those tables were generated.
		t.Run("GeneratedColumns", parallelTestGroup{
			"CustomersTable":     td.TestGeneratedCustomersTableColumns,
			"ProductsTable":      td.TestGeneratedProductsTableColumns,
			"OrdersTable":        td.TestGeneratedOrdersTableColumns,
			"OrderProductsTable": td.TestGeneratedOrderProductsTableColumns,
		}.T)

		// Test that the tables begin empty.
		t.Run("EmptyTables", parallelTestGroup{
			"CustomersTable":     td.TestCustomersEmpty,
			"ProductsTable":      td.TestProductsTableEmpty,
			"OrdersTable":        td.TestOrdersTableEmpty,
			"OrderProductsTable": td.TestOrderProductsTableEmpty,
		}.T)

		// Test that the API returns empty sets for each collection.
		t.Run("RetrieveFromAPIBeforeCreation", parallelTestGroup{
			"Customers": td.TestRetrieveCustomersBeforeCreation,
			"Products":  td.TestRetrieveProductsBeforeCreation,
			"Orders":    td.TestRetrieveOrdersBeforeCreation,
		}.T)

		// Test the creation of initial objects.
		t.Run("CreateCustomer", td.TestCreateCustomer)
		t.Run("CreateProduct", td.TestCreateProduct)

		// Test that the API returns what we just created.
		t.Run("RetrieveFromAPIAfterInitialCreation", parallelTestGroup{
			"Customers": td.TestRetrieveCustomerAfterCreation,
			"Products":  td.TestRetrieveProductAfterCreation,
		}.T)

		// Test the creation of dependent objects.
		t.Run("CreateOrder", td.TestCreateOrder)

		// Test that the API returns what we just created.
		t.Run("RetrieveFromAPIAfterDependentCreation", parallelTestGroup{
			"Order": td.TestRetrieveProductAfterCreation,
		}.T)
	})

	t.Run("SecondRun", func(t *testing.T) {
		stopApp, err := initORMApp(app, dbURL)
		if err != nil {
			t.Fatal(err)
		}
		defer func() {
			if err := stopApp(); err != nil {
				t.Fatal(err)
			}
		}()

		// Test that the API still returns all created objects.
		t.Run("RetrieveFromAPIAfterRestart", parallelTestGroup{
			"Customers": td.TestRetrieveCustomerAfterCreation,
			"Products":  td.TestRetrieveProductAfterCreation,
			"Order":     td.TestRetrieveProductAfterCreation,
		}.T)
	})
}

func TestGORM(t *testing.T) {
	testORM(t, "go", "gorm")
}

func TestHibernate(t *testing.T) {
	testORM(t, "java", "hibernate")
}

func TestSequelize(t *testing.T) {
	testORM(t, "node", "sequelize")
}

func TestSQLAlchemy(t *testing.T) {
	t.Skip("https://github.com/cockroachdb/cockroach/issues/16715")
	testORM(t, "python", "sqlalchemy")
}

func TestActiveRecord(t *testing.T) {
	testORM(t, "ruby", "activerecord")
}

func TestActiveRecord4(t *testing.T) {
	testORM(t, "ruby", "ar4")
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestFprintCompact(t *testing.T) {
	for i, c := range astTests {
		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
			in := c.strs[0]
			prog, err := Parse(strings.NewReader(in), "", 0)
			if err != nil {
				t.Fatal(err)
			}
			want := in
			got := strFprint(prog, 0)
			if len(got) > 0 {
				got = got[:len(got)-1]
			}
			if got != want {
				t.Fatalf("Fprint mismatch\nwant: %q\ngot:  %q",
					want, got)
			}
		})
	}
}

func strFprint(node Node, spaces int) string {
	var buf bytes.Buffer
	c := PrintConfig{Spaces: spaces}
	c.Fprint(&buf, node)
	return buf.String()
}

func TestFprintWeirdFormat(t *testing.T) {
	var weirdFormats = [...]struct {
		in, want string
	}{
		{"foo; bar", "foo\nbar"},
		{"foo\n\n\nbar", "foo\n\nbar"},
		{"foo\n\n", "foo"},
		{"\n\nfoo", "foo"},
		{"# foo\n # bar", "# foo\n# bar"},
		{"a=b # inline\nbar", "a=b # inline\nbar"},
		{"a=`b` # inline", "a=`b` # inline"},
		{"`a` `b`", "`a` `b`"},
		{"if a\nthen\n\tb\nfi", "if a; then\n\tb\nfi"},
		{"if a; then\nb\nelse\nfi", "if a; then\n\tb\nfi"},
		{"foo >&2 <f bar", "foo >&2 <f bar"},
		{"foo >&2 bar <f", "foo >&2 bar <f"},
		{"foo >&2 bar <f bar2", "foo >&2 bar bar2 <f"},
		{"foo <<EOF bar\nl1\nEOF", "foo bar <<EOF\nl1\nEOF"},
		{
			"foo <<EOF && bar\nl1\nEOF",
			"foo <<EOF && bar\nl1\nEOF",
		},
		{
			"foo <<EOF &&\nl1\nEOF\nbar",
			"foo <<EOF && bar\nl1\nEOF",
		},
		{
			"foo <<EOF\nl1\nEOF\n\nfoo2",
			"foo <<EOF\nl1\nEOF\n\nfoo2",
		},
		{
			"{ foo; bar; }",
			"{\n\tfoo\n\tbar\n}",
		},
		{
			"(foo; bar)",
			"(\n\tfoo\n\tbar\n)",
		},
		{
			"{\nfoo\nbar; }",
			"{\n\tfoo\n\tbar\n}",
		},
		{
			"{\nbar\n# extra\n}",
			"{\n\tbar\n\t# extra\n}",
		},
		{
			"foo\nbar  # extra",
			"foo\nbar # extra",
		},
		{
			"foo # 1\nfooo # 2\nfo # 3",
			"foo  # 1\nfooo # 2\nfo   # 3",
		},
		{
			"foo   # 1\nfooo  # 2\nfo    # 3",
			"foo  # 1\nfooo # 2\nfo   # 3",
		},
		{
			"fooooo\nfoo # 1\nfooo # 2\nfo # 3\nfooooo",
			"fooooo\nfoo  # 1\nfooo # 2\nfo   # 3\nfooooo",
		},
		{
			"foo\nbar\nfoo # 1\nfooo # 2",
			"foo\nbar\nfoo  # 1\nfooo # 2",
		},
		{
			"foobar # 1\nfoo\nfoo # 2",
			"foobar # 1\nfoo\nfoo # 2",
		},
		{
			"foobar # 1\n#foo\nfoo # 2",
			"foobar # 1\n#foo\nfoo # 2",
		},
		{
			"foobar # 1\n\nfoo # 2",
			"foobar # 1\n\nfoo # 2",
		},
		{
			"foo # 2\nfoo2 bar # 1",
			"foo      # 2\nfoo2 bar # 1",
		},
		{
			"foo bar # 1\n! foo # 2",
			"foo bar # 1\n! foo   # 2",
		},
		{
			"foo bar # 1\n! foo # 2",
			"foo bar # 1\n! foo   # 2",
		},
		{
			"foo; foooo # 1",
			"foo\nfoooo # 1",
		},
		{
			"(\nbar\n# extra\n)",
			"(\n\tbar\n\t# extra\n)",
		},
		{
			"for a in 1 2\ndo\n\t# bar\ndone",
			"for a in 1 2; do\n\t# bar\ndone",
		},
		{
			"for a in 1 2; do\n\n\tbar\ndone",
			"for a in 1 2; do\n\n\tbar\ndone",
		},
		{
			"a \\\n\t&& b",
			"a \\\n\t&& b",
		},
		{
			"a \\\n\t&& b\nc",
			"a \\\n\t&& b\nc",
		},
		{
			"{\n(a \\\n&& b)\nc\n}",
			"{\n\t(a \\\n\t\t&& b)\n\tc\n}",
		},
		{
			"a && b \\\n&& c",
			"a && b \\\n\t&& c",
		},
		{
			"a \\\n&& $(b) && c \\\n&& d",
			"a \\\n\t&& $(b) && c \\\n\t&& d",
		},
		{
			"a \\\n&& b\nc \\\n&& d",
			"a \\\n\t&& b\nc \\\n\t&& d",
		},
		{
			"a | {\nb \\\n| c\n}",
			"a | {\n\tb \\\n\t\t| c\n}",
		},
		{
			"a \\\n\t&& if foo; then\nbar\nfi",
			"a \\\n\t&& if foo; then\n\t\tbar\n\tfi",
		},
		{
			"if\nfoo\nthen\nbar\nfi",
			"if\n\tfoo\nthen\n\tbar\nfi",
		},
		{
			"if foo \\\nbar\nthen\nbar\nfi",
			"if foo \\\n\tbar; then\n\tbar\nfi",
		},
		{
			"if foo \\\n&& bar\nthen\nbar\nfi",
			"if foo \\\n\t&& bar; then\n\tbar\nfi",
		},
		{
			"a |\nb |\nc",
			"a \\\n\t| b \\\n\t| c",
		},
		{
			"foo |\n# misplaced\nbar",
			"foo \\\n\t| bar # misplaced",
		},
		{
			"foo | while read l; do\nbar\ndone",
			"foo | while read l; do\n\tbar\ndone",
		},
		{
			"\"\\\nfoo\\\n  bar\"",
			"\"\\\nfoo\\\n  bar\"",
		},
		{
			"foo \\\n>bar\netc",
			"foo \\\n\t>bar\netc",
		},
		{
			"foo \\\nfoo2 \\\n>bar",
			"foo \\\n\tfoo2 \\\n\t>bar",
		},
		{
			"case $i in\n1)\nfoo\n;;\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\t;;\nesac",
		},
		{
			"case $i in\n1)\nfoo\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\t;;\nesac",
		},
		{
			"case $i in\n1) foo\nesac",
			"case $i in\n\t1) foo ;;\nesac",
		},
		{
			"case $i in\n1) foo; bar\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\tbar\n\t\t;;\nesac",
		},
		{
			"case $i in\n1) foo; bar;;\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\tbar\n\t\t;;\nesac",
		},
		{
			"a=(\nb\nc\n) foo",
			"a=(\n\tb\n\tc\n) foo",
		},
		{
			"foo <<EOF | `bar`\n3\nEOF",
			"foo <<EOF | `bar`\n3\nEOF",
		},
	}

	for i, tc := range weirdFormats {
		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
			for _, s := range [...]string{"", "\n"} {
				in := s + tc.in + s
				prog, err := Parse(strings.NewReader(in), "",
					ParseComments)
				if err != nil {
					t.Fatal(err)
				}
				want := tc.want + "\n"
				got := strFprint(prog, 0)
				if got != want {
					t.Fatalf("Fprint mismatch:\n"+
						"in:\n%s\nwant:\n%sgot:\n%s",
						in, want, got)
				}
			}
		})
	}
}

func parsePath(tb testing.TB, path string) File {
	f, err := os.Open(path)
	if err != nil {
		tb.Fatal(err)
	}
	defer f.Close()
	prog, err := Parse(f, "", ParseComments)
	if err != nil {
		tb.Fatal(err)
	}
	return prog
}

func TestFprintMultiline(t *testing.T) {
	path := filepath.Join("testdata", "canonical.sh")
	prog := parsePath(t, path)
	got := strFprint(prog, 0)

	want, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	if got != string(want) {
		t.Fatalf("Fprint mismatch in canonical.sh")
	}
}

func TestFprintNodeTypes(t *testing.T) {
	nodes := []Node{
		File{},
		Stmt{},
	}
	for _, node := range nodes {
		t.Run(fmt.Sprintf("%T", node), func(t *testing.T) {
			if err := Fprint(ioutil.Discard, node); err != nil {
				t.Fatal("unexpected error: %v", err)
			}
		})
	}
}

func TestFprintSpaces(t *testing.T) {
	var spaceFormats = [...]struct {
		spaces   int
		in, want string
	}{
		{
			0,
			"{\nfoo \\\nbar\n}",
			"{\n\tfoo \\\n\t\tbar\n}",
		},
		{
			-1,
			"{\nfoo \\\nbar\n}",
			"{\nfoo \\\nbar\n}",
		},
		{
			2,
			"{\nfoo \\\nbar\n}",
			"{\n  foo \\\n    bar\n}",
		},
		{
			4,
			"{\nfoo \\\nbar\n}",
			"{\n    foo \\\n        bar\n}",
		},
	}

	for i, tc := range spaceFormats {
		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
			prog, err := Parse(strings.NewReader(tc.in), "", ParseComments)
			if err != nil {
				t.Fatal(err)
			}
			want := tc.want + "\n"
			got := strFprint(prog, tc.spaces)
			if got != want {
				t.Fatalf("Fprint mismatch:\nin:\n%s\nwant:\n%sgot:\n%s",
					tc.in, want, got)
			}
		})
	}
}

var errBadWriter = fmt.Errorf("write: expected error")

type badWriter struct{}

func (b badWriter) Write(p []byte) (int, error) { return 0, errBadWriter }

func TestWriteErr(t *testing.T) {
	var out badWriter
	f := File{
		Stmts: []Stmt{
			{
				Redirs: []Redirect{{}},
				Cmd:    Subshell{},
			},
		},
	}
	err := Fprint(out, f)
	if err == nil {
		t.Fatalf("Expected error with bad writer")
	}
	if err != errBadWriter {
		t.Fatalf("Error mismatch with bad writer:\nwant: %v\ngot:  %v",
			errBadWriter, err)
	}
}
<commit_msg>print_test: fix go vet warning<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestFprintCompact(t *testing.T) {
	for i, c := range astTests {
		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
			in := c.strs[0]
			prog, err := Parse(strings.NewReader(in), "", 0)
			if err != nil {
				t.Fatal(err)
			}
			want := in
			got := strFprint(prog, 0)
			if len(got) > 0 {
				got = got[:len(got)-1]
			}
			if got != want {
				t.Fatalf("Fprint mismatch\nwant: %q\ngot:  %q",
					want, got)
			}
		})
	}
}

func strFprint(node Node, spaces int) string {
	var buf bytes.Buffer
	c := PrintConfig{Spaces: spaces}
	c.Fprint(&buf, node)
	return buf.String()
}

func TestFprintWeirdFormat(t *testing.T) {
	var weirdFormats = [...]struct {
		in, want string
	}{
		{"foo; bar", "foo\nbar"},
		{"foo\n\n\nbar", "foo\n\nbar"},
		{"foo\n\n", "foo"},
		{"\n\nfoo", "foo"},
		{"# foo\n # bar", "# foo\n# bar"},
		{"a=b # inline\nbar", "a=b # inline\nbar"},
		{"a=`b` # inline", "a=`b` # inline"},
		{"`a` `b`", "`a` `b`"},
		{"if a\nthen\n\tb\nfi", "if a; then\n\tb\nfi"},
		{"if a; then\nb\nelse\nfi", "if a; then\n\tb\nfi"},
		{"foo >&2 <f bar", "foo >&2 <f bar"},
		{"foo >&2 bar <f", "foo >&2 bar <f"},
		{"foo >&2 bar <f bar2", "foo >&2 bar bar2 <f"},
		{"foo <<EOF bar\nl1\nEOF", "foo bar <<EOF\nl1\nEOF"},
		{
			"foo <<EOF && bar\nl1\nEOF",
			"foo <<EOF && bar\nl1\nEOF",
		},
		{
			"foo <<EOF &&\nl1\nEOF\nbar",
			"foo <<EOF && bar\nl1\nEOF",
		},
		{
			"foo <<EOF\nl1\nEOF\n\nfoo2",
			"foo <<EOF\nl1\nEOF\n\nfoo2",
		},
		{
			"{ foo; bar; }",
			"{\n\tfoo\n\tbar\n}",
		},
		{
			"(foo; bar)",
			"(\n\tfoo\n\tbar\n)",
		},
		{
			"{\nfoo\nbar; }",
			"{\n\tfoo\n\tbar\n}",
		},
		{
			"{\nbar\n# extra\n}",
			"{\n\tbar\n\t# extra\n}",
		},
		{
			"foo\nbar  # extra",
			"foo\nbar # extra",
		},
		{
			"foo # 1\nfooo # 2\nfo # 3",
			"foo  # 1\nfooo # 2\nfo   # 3",
		},
		{
			"foo   # 1\nfooo  # 2\nfo    # 3",
			"foo  # 1\nfooo # 2\nfo   # 3",
		},
		{
			"fooooo\nfoo # 1\nfooo # 2\nfo # 3\nfooooo",
			"fooooo\nfoo  # 1\nfooo # 2\nfo   # 3\nfooooo",
		},
		{
			"foo\nbar\nfoo # 1\nfooo # 2",
			"foo\nbar\nfoo  # 1\nfooo # 2",
		},
		{
			"foobar # 1\nfoo\nfoo # 2",
			"foobar # 1\nfoo\nfoo # 2",
		},
		{
			"foobar # 1\n#foo\nfoo # 2",
			"foobar # 1\n#foo\nfoo # 2",
		},
		{
			"foobar # 1\n\nfoo # 2",
			"foobar # 1\n\nfoo # 2",
		},
		{
			"foo # 2\nfoo2 bar # 1",
			"foo      # 2\nfoo2 bar # 1",
		},
		{
			"foo bar # 1\n! foo # 2",
			"foo bar # 1\n! foo   # 2",
		},
		{
			"foo bar # 1\n! foo # 2",
			"foo bar # 1\n! foo   # 2",
		},
		{
			"foo; foooo # 1",
			"foo\nfoooo # 1",
		},
		{
			"(\nbar\n# extra\n)",
			"(\n\tbar\n\t# extra\n)",
		},
		{
			"for a in 1 2\ndo\n\t# bar\ndone",
			"for a in 1 2; do\n\t# bar\ndone",
		},
		{
			"for a in 1 2; do\n\n\tbar\ndone",
			"for a in 1 2; do\n\n\tbar\ndone",
		},
		{
			"a \\\n\t&& b",
			"a \\\n\t&& b",
		},
		{
			"a \\\n\t&& b\nc",
			"a \\\n\t&& b\nc",
		},
		{
			"{\n(a \\\n&& b)\nc\n}",
			"{\n\t(a \\\n\t\t&& b)\n\tc\n}",
		},
		{
			"a && b \\\n&& c",
			"a && b \\\n\t&& c",
		},
		{
			"a \\\n&& $(b) && c \\\n&& d",
			"a \\\n\t&& $(b) && c \\\n\t&& d",
		},
		{
			"a \\\n&& b\nc \\\n&& d",
			"a \\\n\t&& b\nc \\\n\t&& d",
		},
		{
			"a | {\nb \\\n| c\n}",
			"a | {\n\tb \\\n\t\t| c\n}",
		},
		{
			"a \\\n\t&& if foo; then\nbar\nfi",
			"a \\\n\t&& if foo; then\n\t\tbar\n\tfi",
		},
		{
			"if\nfoo\nthen\nbar\nfi",
			"if\n\tfoo\nthen\n\tbar\nfi",
		},
		{
			"if foo \\\nbar\nthen\nbar\nfi",
			"if foo \\\n\tbar; then\n\tbar\nfi",
		},
		{
			"if foo \\\n&& bar\nthen\nbar\nfi",
			"if foo \\\n\t&& bar; then\n\tbar\nfi",
		},
		{
			"a |\nb |\nc",
			"a \\\n\t| b \\\n\t| c",
		},
		{
			"foo |\n# misplaced\nbar",
			"foo \\\n\t| bar # misplaced",
		},
		{
			"foo | while read l; do\nbar\ndone",
			"foo | while read l; do\n\tbar\ndone",
		},
		{
			"\"\\\nfoo\\\n  bar\"",
			"\"\\\nfoo\\\n  bar\"",
		},
		{
			"foo \\\n>bar\netc",
			"foo \\\n\t>bar\netc",
		},
		{
			"foo \\\nfoo2 \\\n>bar",
			"foo \\\n\tfoo2 \\\n\t>bar",
		},
		{
			"case $i in\n1)\nfoo\n;;\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\t;;\nesac",
		},
		{
			"case $i in\n1)\nfoo\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\t;;\nesac",
		},
		{
			"case $i in\n1) foo\nesac",
			"case $i in\n\t1) foo ;;\nesac",
		},
		{
			"case $i in\n1) foo; bar\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\tbar\n\t\t;;\nesac",
		},
		{
			"case $i in\n1) foo; bar;;\nesac",
			"case $i in\n\t1)\n\t\tfoo\n\t\tbar\n\t\t;;\nesac",
		},
		{
			"a=(\nb\nc\n) foo",
			"a=(\n\tb\n\tc\n) foo",
		},
		{
			"foo <<EOF | `bar`\n3\nEOF",
			"foo <<EOF | `bar`\n3\nEOF",
		},
	}

	for i, tc := range weirdFormats {
		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
			for _, s := range [...]string{"", "\n"} {
				in := s + tc.in + s
				prog, err := Parse(strings.NewReader(in), "",
					ParseComments)
				if err != nil {
					t.Fatal(err)
				}
				want := tc.want + "\n"
				got := strFprint(prog, 0)
				if got != want {
					t.Fatalf("Fprint mismatch:\n"+
						"in:\n%s\nwant:\n%sgot:\n%s",
						in, want, got)
				}
			}
		})
	}
}

func parsePath(tb testing.TB, path string) File {
	f, err := os.Open(path)
	if err != nil {
		tb.Fatal(err)
	}
	defer f.Close()
	prog, err := Parse(f, "", ParseComments)
	if err != nil {
		tb.Fatal(err)
	}
	return prog
}

func TestFprintMultiline(t *testing.T) {
	path := filepath.Join("testdata", "canonical.sh")
	prog := parsePath(t, path)
	got := strFprint(prog, 0)

	want, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	if got != string(want) {
		t.Fatalf("Fprint mismatch in canonical.sh")
	}
}

func TestFprintNodeTypes(t *testing.T) {
	nodes := []Node{
		File{},
		Stmt{},
	}
	for _, node := range nodes {
		t.Run(fmt.Sprintf("%T", node), func(t *testing.T) {
			if err := Fprint(ioutil.Discard, node); err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
		})
	}
}

func TestFprintSpaces(t *testing.T) {
	var spaceFormats = [...]struct {
		spaces   int
		in, want string
	}{
		{
			0,
			"{\nfoo \\\nbar\n}",
			"{\n\tfoo \\\n\t\tbar\n}",
		},
		{
			-1,
			"{\nfoo \\\nbar\n}",
			"{\nfoo \\\nbar\n}",
		},
		{
			2,
			"{\nfoo \\\nbar\n}",
			"{\n  foo \\\n    bar\n}",
		},
		{
			4,
			"{\nfoo \\\nbar\n}",
			"{\n    foo \\\n        bar\n}",
		},
	}

	for i, tc := range spaceFormats {
		t.Run(fmt.Sprintf("%03d", i), func(t *testing.T) {
			prog, err := Parse(strings.NewReader(tc.in), "", ParseComments)
			if err != nil {
				t.Fatal(err)
			}
			want := tc.want + "\n"
			got := strFprint(prog, tc.spaces)
			if got != want {
				t.Fatalf("Fprint mismatch:\nin:\n%s\nwant:\n%sgot:\n%s",
					tc.in, want, got)
			}
		})
	}
}

var errBadWriter = fmt.Errorf("write: expected error")

type badWriter struct{}

func (b badWriter) Write(p []byte) (int, error) { return 0, errBadWriter }

func TestWriteErr(t *testing.T) {
	var out badWriter
	f := File{
		Stmts: []Stmt{
			{
				Redirs: []Redirect{{}},
				Cmd:    Subshell{},
			},
		},
	}
	err := Fprint(out, f)
	if err == nil {
		t.Fatalf("Expected error with bad writer")
	}
	if err != errBadWriter {
		t.Fatalf("Error mismatch with bad writer:\nwant: %v\ngot:  %v",
			errBadWriter, err)
	}
}
<|endoftext|>


<commit_before>/*
                       WARNING WARNING WARNING

                Attention all potential contributors

   This testfile is not in the best state. We've been slowly transitioning
   from the built in "testing" package to using Ginkgo. As you can see, we've
   changed the format, but a lot of the setup, test body, descriptions, etc
   are either hardcoded, completely lacking, or misleading.

   For example:

   Describe("Testing with ginkgo"...)      // This is not a great description
   It("TestDoesSoemthing"...)              // This is a horrible description

   Describe("create-user command"...       // Describe the actual object under test
   It("creates a user when provided ..."   // this is more descriptive

   For good examples of writing Ginkgo tests for the cli, refer to

   src/github.com/cloudfoundry/cli/cf/commands/application/delete_app_test.go
   src/github.com/cloudfoundry/cli/cf/terminal/ui_test.go
   src/github.com/cloudfoundry/loggregator_consumer/consumer_test.go
*/

package route_test

import (
	. "github.com/cloudfoundry/cli/cf/commands/route"
	"github.com/cloudfoundry/cli/cf/models"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "github.com/cloudfoundry/cli/testhelpers/matchers"
)

func callMapRoute(args []string, requirementsFactory *testreq.FakeReqFactory, routeRepo *testapi.FakeRouteRepository, createRoute *testcmd.FakeRouteCreator) (ui *testterm.FakeUI) {
	ui = new(testterm.FakeUI)
	configRepo := testconfig.NewRepositoryWithDefaults()
	cmd := NewMapRoute(ui, configRepo, routeRepo, createRoute)
	testcmd.RunCommand(cmd, args, requirementsFactory)
	return
}

var _ = Describe("Testing with ginkgo", func() {
	It("TestMapRouteFailsWithUsage", func() {
		requirementsFactory := &testreq.FakeReqFactory{}
		routeRepo := &testapi.FakeRouteRepository{}

		ui := callMapRoute([]string{}, requirementsFactory, routeRepo, &testcmd.FakeRouteCreator{})
		Expect(ui.FailedWithUsage).To(BeTrue())

		ui = callMapRoute([]string{"foo"}, requirementsFactory, routeRepo, &testcmd.FakeRouteCreator{})
		Expect(ui.FailedWithUsage).To(BeTrue())

		ui = callMapRoute([]string{"foo", "bar"}, requirementsFactory, routeRepo, &testcmd.FakeRouteCreator{})
		Expect(ui.FailedWithUsage).To(BeFalse())
	})
	It("TestMapRouteRequirements", func() {

		routeRepo := &testapi.FakeRouteRepository{}
		requirementsFactory := &testreq.FakeReqFactory{LoginSuccess: true}

		callMapRoute([]string{"-n", "my-host", "my-app", "my-domain.com"}, requirementsFactory, routeRepo, &testcmd.FakeRouteCreator{})
		Expect(testcmd.CommandDidPassRequirements).To(BeTrue())
		Expect(requirementsFactory.ApplicationName).To(Equal("my-app"))
		Expect(requirementsFactory.DomainName).To(Equal("my-domain.com"))
	})
	It("TestMapRouteWhenBinding", func() {

		domain := models.DomainFields{}
		domain.Guid = "my-domain-guid"
		domain.Name = "example.com"
		route := models.Route{}
		route.Guid = "my-route-guid"
		route.Host = "foo"
		route.Domain = domain

		app := models.Application{}
		app.Guid = "my-app-guid"
		app.Name = "my-app"

		routeRepo := &testapi.FakeRouteRepository{}
		requirementsFactory := &testreq.FakeReqFactory{LoginSuccess: true, Application: app, Domain: domain}
		routeCreator := &testcmd.FakeRouteCreator{ReservedRoute: route}

		ui := callMapRoute([]string{"-n", "my-host", "my-app", "my-domain.com"}, requirementsFactory, routeRepo, routeCreator)

		Expect(ui.Outputs).To(ContainSubstrings(
			[]string{"Adding route", "foo.example.com", "my-app", "my-org", "my-space", "my-user"},
			[]string{"OK"},
		))

		Expect(routeRepo.BoundRouteGuid).To(Equal("my-route-guid"))
		Expect(routeRepo.BoundAppGuid).To(Equal("my-app-guid"))
	})

	It("TestMapRouteWhenRouteNotReserved", func() {
		domain := models.DomainFields{}
		domain.Name = "my-domain.com"
		route := models.Route{}
		route.Guid = "my-app-guid"
		route.Host = "my-host"
		route.Domain = domain
		app := models.Application{}
		app.Guid = "my-app-guid"
		app.Name = "my-app"

		routeRepo := &testapi.FakeRouteRepository{}
		requirementsFactory := &testreq.FakeReqFactory{LoginSuccess: true, Application: app}
		routeCreator := &testcmd.FakeRouteCreator{ReservedRoute: route}

		callMapRoute([]string{"-n", "my-host", "my-app", "my-domain.com"}, requirementsFactory, routeRepo, routeCreator)

		Expect(routeCreator.ReservedRoute).To(Equal(route))
	})
})
<commit_msg>Cleanup map-route tests<commit_after>package route_test

import (
	"github.com/cloudfoundry/cli/cf/models"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"

	. "github.com/cloudfoundry/cli/cf/commands/route"
	"github.com/cloudfoundry/cli/cf/configuration"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("map-route command", func() {
	var (
		ui                  *testterm.FakeUI
		configRepo          configuration.ReadWriter
		routeRepo           *testapi.FakeRouteRepository
		requirementsFactory *testreq.FakeReqFactory
		routeCreator        *testcmd.FakeRouteCreator
	)

	BeforeEach(func() {
		ui = new(testterm.FakeUI)
		configRepo = testconfig.NewRepositoryWithDefaults()
		routeRepo = new(testapi.FakeRouteRepository)
		routeCreator = &testcmd.FakeRouteCreator{}
		requirementsFactory = new(testreq.FakeReqFactory)
	})

	runCommand := func(args ...string) {
		testcmd.RunCommand(NewMapRoute(ui, configRepo, routeRepo, routeCreator), args, requirementsFactory)
	}

	Describe("requirements", func() {
		It("fails when not invoked with exactly two args", func() {
			runCommand("whoops-all-crunchberries")
			Expect(ui.FailedWithUsage).To(BeTrue())
		})

		It("fails when not logged in", func() {
			runCommand("whatever", "shuttup")
			Expect(testcmd.CommandDidPassRequirements).To(BeFalse())
		})
	})

	Context("when the user is logged in", func() {
		BeforeEach(func() {
			domain := models.DomainFields{Guid: "my-domain-guid", Name: "example.com"}
			route := models.Route{Guid: "my-route-guid", Host: "foo", Domain: domain}

			app := models.Application{}
			app.Guid = "my-app-guid"
			app.Name = "my-app"

			requirementsFactory.LoginSuccess = true
			requirementsFactory.Application = app
			requirementsFactory.Domain = domain
			routeCreator.ReservedRoute = route
		})

		It("maps a route, obviously", func() {
			runCommand("-n", "my-host", "my-app", "my-domain.com")

			Expect(ui.Outputs).To(ContainSubstrings(
				[]string{"Adding route", "foo.example.com", "my-app", "my-org", "my-space", "my-user"},
				[]string{"OK"},
			))

			Expect(routeRepo.BoundRouteGuid).To(Equal("my-route-guid"))
			Expect(routeRepo.BoundAppGuid).To(Equal("my-app-guid"))
			Expect(testcmd.CommandDidPassRequirements).To(BeTrue())
			Expect(requirementsFactory.ApplicationName).To(Equal("my-app"))
			Expect(requirementsFactory.DomainName).To(Equal("my-domain.com"))
		})
	})
})
<|endoftext|>


<commit_before>package mutualtls_test

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"lib/mutualtls"
	"math/rand"
	"net/http"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
)

var _ = Describe("TLS config for internal API server", func() {
	var (
		serverListenAddr string
		clientTLSConfig  *tls.Config
	)

	BeforeEach(func() {
		var err error
		serverListenAddr = fmt.Sprintf("127.0.0.1:%d", 40000+rand.Intn(10000))
		clientTLSConfig, err = mutualtls.NewClientTLSConfig(paths.ClientCertPath, paths.ClientKeyPath, paths.ServerCACertPath)
		Expect(err).NotTo(HaveOccurred())
	})

	startServer := func(tlsConfig *tls.Config) ifrit.Process {
		testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("hello"))
		})
		someServer := http_server.NewTLSServer(serverListenAddr, testHandler, tlsConfig)

		members := grouper.Members{{
			Name:   "http_server",
			Runner: someServer,
		}}
		group := grouper.NewOrdered(os.Interrupt, members)
		monitor := ifrit.Invoke(sigmon.New(group))

		Eventually(monitor.Ready()).Should(BeClosed())
		return monitor
	}

	makeRequest := func(serverAddr string, clientTLSConfig *tls.Config) (*http.Response, error) {
		req, err := http.NewRequest("GET", "https://"+serverAddr+"/", nil)
		Expect(err).NotTo(HaveOccurred())
		client := &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: clientTLSConfig,
			},
		}
		return client.Do(req)
	}

	Describe("NewServerTLSConfig", func() {
		It("returns a TLSConfig that can be used by an HTTP server", func() {
			serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
			Expect(err).NotTo(HaveOccurred())

			server := startServer(serverTLSConfig)

			resp, err := makeRequest(serverListenAddr, clientTLSConfig)
			Expect(err).NotTo(HaveOccurred())
			Expect(resp.StatusCode).To(Equal(http.StatusOK))
			respBytes, err := ioutil.ReadAll(resp.Body)
			Expect(err).NotTo(HaveOccurred())
			Expect(respBytes).To(Equal([]byte("hello")))
			Expect(resp.Body.Close()).To(Succeed())

			server.Signal(os.Interrupt)
			Eventually(server.Wait()).Should(Receive())
		})

		Context("when the key pair cannot be created", func() {
			It("returns a meaningful error", func() {
				_, err := mutualtls.NewServerTLSConfig("", "", "")
				Expect(err).To(MatchError(HavePrefix("unable to load cert or key")))
			})
		})

		Context("when the server has been configured with the wrong CA for the client", func() {
			It("refuses to connect to the client", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.WrongClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("remote error")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when the client has been configured without a CA", func() {
			BeforeEach(func() {
				clientTLSConfig.RootCAs = nil
			})

			It("refuses to connect to the server", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("x509: certificate signed by unknown authority")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when the client has been configured with the wrong CA for the server", func() {
			BeforeEach(func() {
				wrongServerCACert, err := ioutil.ReadFile(paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				clientCertPool := x509.NewCertPool()
				clientCertPool.AppendCertsFromPEM(wrongServerCACert)
				clientTLSConfig.RootCAs = clientCertPool
			})

			It("refuses to connect to the server", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("x509: certificate signed by unknown authority")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when the client does not present client certificates to the server", func() {
			BeforeEach(func() {
				clientTLSConfig.Certificates = nil
			})

			It("refuses the connection from the client", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("remote error")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when the client presents certificates that the server does not trust", func() {
			BeforeEach(func() {
				invalidClient, err := tls.LoadX509KeyPair(paths.WrongClientCertPath, paths.WrongClientKeyPath)
				Expect(err).NotTo(HaveOccurred())
				clientTLSConfig.Certificates = []tls.Certificate{invalidClient}
			})

			It("refuses the connection from the client", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("remote error")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when the client is configured to use an unsupported ciphersuite", func() {
			BeforeEach(func() {
				clientTLSConfig.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384}
			})

			It("refuses the connection from the client", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("remote error")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when the client is configured to use TLS 1.1", func() {
			BeforeEach(func() {
				clientTLSConfig.MinVersion = tls.VersionTLS11
				clientTLSConfig.MaxVersion = tls.VersionTLS11
			})

			It("refuses the connection from the client", func() {
				serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
				Expect(err).NotTo(HaveOccurred())

				server := startServer(serverTLSConfig)

				_, err = makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("remote error")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		It("returns config with reasonable security properties", func() {
			serverTLSConfig, err := mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
			Expect(err).NotTo(HaveOccurred())

			Expect(serverTLSConfig.PreferServerCipherSuites).To(BeTrue())
			Expect(serverTLSConfig.MinVersion).To(Equal(uint16(tls.VersionTLS12)))
			Expect(serverTLSConfig.CipherSuites).To(Equal([]uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}))
		})
	})
})
<commit_msg>Clean up mutualtls_test<commit_after>package mutualtls_test

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"lib/mutualtls"
	"math/rand"
	"net/http"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
)

var _ = Describe("TLS config for internal API server", func() {
	var (
		serverListenAddr string
		clientTLSConfig  *tls.Config
		serverTLSConfig  *tls.Config
	)

	BeforeEach(func() {
		var err error
		serverListenAddr = fmt.Sprintf("127.0.0.1:%d", 40000+rand.Intn(10000))
		clientTLSConfig, err = mutualtls.NewClientTLSConfig(paths.ClientCertPath, paths.ClientKeyPath, paths.ServerCACertPath)
		Expect(err).NotTo(HaveOccurred())
		serverTLSConfig, err = mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.ClientCACertPath)
		Expect(err).NotTo(HaveOccurred())
	})

	startServer := func(tlsConfig *tls.Config) ifrit.Process {
		testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("hello"))
		})
		someServer := http_server.NewTLSServer(serverListenAddr, testHandler, tlsConfig)

		members := grouper.Members{{
			Name:   "http_server",
			Runner: someServer,
		}}
		group := grouper.NewOrdered(os.Interrupt, members)
		monitor := ifrit.Invoke(sigmon.New(group))

		Eventually(monitor.Ready()).Should(BeClosed())
		return monitor
	}

	makeRequest := func(serverAddr string, clientTLSConfig *tls.Config) (*http.Response, error) {
		req, err := http.NewRequest("GET", "https://"+serverAddr+"/", nil)
		Expect(err).NotTo(HaveOccurred())
		client := &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: clientTLSConfig,
			},
		}
		return client.Do(req)
	}

	Describe("Server TLS Config", func() {
		It("returns a TLSConfig that can be used by an HTTP server", func() {
			server := startServer(serverTLSConfig)

			resp, err := makeRequest(serverListenAddr, clientTLSConfig)
			Expect(err).NotTo(HaveOccurred())
			Expect(resp.StatusCode).To(Equal(http.StatusOK))
			respBytes, err := ioutil.ReadAll(resp.Body)
			Expect(err).NotTo(HaveOccurred())
			Expect(respBytes).To(Equal([]byte("hello")))
			Expect(resp.Body.Close()).To(Succeed())

			server.Signal(os.Interrupt)
			Eventually(server.Wait()).Should(Receive())
		})

		Context("when the key pair cannot be created", func() {
			It("returns a meaningful error", func() {
				_, err := mutualtls.NewServerTLSConfig("", "", "")
				Expect(err).To(MatchError(HavePrefix("unable to load cert or key")))
			})
		})

		Context("when the server has been configured with the wrong CA for the client", func() {
			BeforeEach(func() {
				var err error
				serverTLSConfig, err = mutualtls.NewServerTLSConfig(paths.ServerCertPath, paths.ServerKeyPath, paths.WrongClientCACertPath)
				Expect(err).NotTo(HaveOccurred())
			})

			It("refuses to connect to the client", func() {
				server := startServer(serverTLSConfig)

				_, err := makeRequest(serverListenAddr, clientTLSConfig)
				Expect(err).To(MatchError(ContainSubstring("remote error")))

				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})
		})

		Context("when is misconfigured", func() {
			var server ifrit.Process
			BeforeEach(func() {
				server = startServer(serverTLSConfig)
			})

			AfterEach(func() {
				server.Signal(os.Interrupt)
				Eventually(server.Wait()).Should(Receive())
			})

			Context("when the client has been configured without a CA", func() {
				BeforeEach(func() {
					clientTLSConfig.RootCAs = nil
				})

				It("refuses to connect to the server", func() {
					_, err := makeRequest(serverListenAddr, clientTLSConfig)
					Expect(err).To(MatchError(ContainSubstring("x509: certificate signed by unknown authority")))
				})
			})

			Context("when the client has been configured with the wrong CA for the server", func() {
				BeforeEach(func() {
					wrongServerCACert, err := ioutil.ReadFile(paths.ClientCACertPath)
					Expect(err).NotTo(HaveOccurred())

					clientCertPool := x509.NewCertPool()
					clientCertPool.AppendCertsFromPEM(wrongServerCACert)
					clientTLSConfig.RootCAs = clientCertPool
				})

				It("refuses to connect to the server", func() {
					_, err := makeRequest(serverListenAddr, clientTLSConfig)
					Expect(err).To(MatchError(ContainSubstring("x509: certificate signed by unknown authority")))
				})
			})

			Context("when the client does not present client certificates to the server", func() {
				BeforeEach(func() {
					clientTLSConfig.Certificates = nil
				})

				It("refuses the connection from the client", func() {
					_, err := makeRequest(serverListenAddr, clientTLSConfig)
					Expect(err).To(MatchError(ContainSubstring("remote error")))
				})
			})

			Context("when the client presents certificates that the server does not trust", func() {
				BeforeEach(func() {
					invalidClient, err := tls.LoadX509KeyPair(paths.WrongClientCertPath, paths.WrongClientKeyPath)
					Expect(err).NotTo(HaveOccurred())
					clientTLSConfig.Certificates = []tls.Certificate{invalidClient}
				})

				It("refuses the connection from the client", func() {
					_, err := makeRequest(serverListenAddr, clientTLSConfig)
					Expect(err).To(MatchError(ContainSubstring("remote error")))
				})
			})

			Context("when the client is configured to use an unsupported ciphersuite", func() {
				BeforeEach(func() {
					clientTLSConfig.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384}
				})

				It("refuses the connection from the client", func() {
					_, err := makeRequest(serverListenAddr, clientTLSConfig)
					Expect(err).To(MatchError(ContainSubstring("remote error")))
				})
			})

			Context("when the client is configured to use TLS 1.1", func() {
				BeforeEach(func() {
					clientTLSConfig.MinVersion = tls.VersionTLS11
					clientTLSConfig.MaxVersion = tls.VersionTLS11
				})

				It("refuses the connection from the client", func() {
					_, err := makeRequest(serverListenAddr, clientTLSConfig)
					Expect(err).To(MatchError(ContainSubstring("remote error")))
				})
			})

		})

	})
})
<|endoftext|>


<commit_before>// Copyright 2014 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containermatcher

import (
	"archive/zip"
	"io"

	"github.com/richardlehane/siegfried/internal/siegreader"
)

type zipReader struct {
	idx     int
	rdr     *zip.Reader
	expired bool
	rc      io.ReadCloser
}

func (z *zipReader) Next() error {
	z.idx++
	if z.idx >= len(z.rdr.File) {
		return io.EOF
	}
	return nil
}

func (z *zipReader) Name() string {
	return z.rdr.File[z.idx].Name
}

func (z *zipReader) SetSource(bufs *siegreader.Buffers) (*siegreader.Buffer, error) {
	var err error
	z.rc, err = z.rdr.File[z.idx].Open()
	if err != nil {
		return nil, err
	}
	return bufs.Get(z.rc)
}

func (z *zipReader) Close() {
	if z.rc == nil {
		return
	}
	z.rc.Close()
}

func (z *zipReader) IsDir() bool {
	if z.idx < len(z.rdr.File) {
		return z.rdr.File[z.idx].FileHeader.FileInfo().IsDir()
	}
	return false
}

func zipRdr(b *siegreader.Buffer) (Reader, error) {
	r, err := zip.NewReader(siegreader.ReaderFrom(b), b.SizeNow())
	return &zipReader{idx: -1, rdr: r}, err
}
<commit_msg>remove trailing null bytes zip file names; fixes #150<commit_after>// Copyright 2014 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containermatcher

import (
	"archive/zip"
	"io"
	"strings"

	"github.com/richardlehane/siegfried/internal/siegreader"
)

type zipReader struct {
	idx     int
	rdr     *zip.Reader
	expired bool
	rc      io.ReadCloser
}

func (z *zipReader) Next() error {
	z.idx++
	if z.idx >= len(z.rdr.File) {
		return io.EOF
	}
	return nil
}

func (z *zipReader) Name() string {
	return strings.TrimSuffix(z.rdr.File[z.idx].Name, "\x00") // non-spec zip files may have null terminated strings
}

func (z *zipReader) SetSource(bufs *siegreader.Buffers) (*siegreader.Buffer, error) {
	var err error
	z.rc, err = z.rdr.File[z.idx].Open()
	if err != nil {
		return nil, err
	}
	return bufs.Get(z.rc)
}

func (z *zipReader) Close() {
	if z.rc == nil {
		return
	}
	z.rc.Close()
}

func (z *zipReader) IsDir() bool {
	if z.idx < len(z.rdr.File) {
		return z.rdr.File[z.idx].FileHeader.FileInfo().IsDir()
	}
	return false
}

func zipRdr(b *siegreader.Buffer) (Reader, error) {
	r, err := zip.NewReader(siegreader.ReaderFrom(b), b.SizeNow())
	return &zipReader{idx: -1, rdr: r}, err
}
<|endoftext|>


<commit_before>package publish

import (
	"fmt"
	"os"
	"path"
	"reflect"
	"strings"

	"github.com/qor/qor/admin"
)

type PublishController struct {
	*DB
}

func (db *PublishController) Preview(context *admin.Context) {
	draftDB := db.DraftMode()
	drafts := make(map[*admin.Resource]interface{})
	for _, model := range db.SupportedModels {
		var res *admin.Resource
		var name = modelType(model).Name()

		if r := context.Admin.GetResource(strings.ToLower(name)); r != nil {
			res = r
		} else {
			res = admin.NewResource(model)
		}

		results := res.NewSlice()
		draftDB.Unscoped().Where("publish_status = ?", DIRTY).Find(results)
		drafts[res] = results
	}
	context.Execute("publish/drafts", drafts)
}

func (db *PublishController) Diff(context *admin.Context) {
	resourceID := strings.Split(context.Request.URL.Path, "/")[4]
	params := strings.Split(resourceID, "__")
	name, id := params[0], params[1]
	res := context.Admin.GetResource(name)

	draft := res.NewStruct()
	db.DraftMode().Unscoped().First(draft, id)

	production := res.NewStruct()
	db.ProductionMode().Unscoped().First(production, id)

	results := map[string]interface{}{"Production": production, "Draft": draft, "Resource": res}

	fmt.Fprintf(context.Writer, context.Render("publish/diff", results))
}

func (db *PublishController) Publish(context *admin.Context) {
	var request = context.Request
	var ids = request.Form["checked_ids[]"]

	if request.Form.Get("publish_type") == "publish" {
		var records = []interface{}{}
		var values = map[string][]string{}

		for _, id := range ids {
			if keys := strings.Split(id, "__"); len(keys) == 2 {
				name, id := keys[0], keys[1]
				values[name] = append(values[name], id)
			}
		}

		for name, value := range values {
			res := context.Admin.GetResource(name)
			results := res.NewSlice()
			if db.DraftMode().Unscoped().Find(results, fmt.Sprintf("%v IN (?)", res.PrimaryKey()), value).Error == nil {
				resultValues := reflect.Indirect(reflect.ValueOf(results))
				for i := 0; i < resultValues.Len(); i++ {
					records = append(records, resultValues.Index(i).Interface())
				}
			}
		}
		db.DB.Publish(records...)
	} else if request.Form.Get("publish_type") == "discard" {

	}
}

func (db *DB) InjectQorAdmin(web *admin.Admin) {
	controller := PublishController{db}
	router := web.GetRouter()
	router.Get("^/publish/diff/", controller.Diff)
	router.Get("^/publish", controller.Preview)
	router.Post("^/publish", controller.Publish)

	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/qor/publish/views"))
	}
}
<commit_msg>Redirect back<commit_after>package publish

import (
	"fmt"
	"net/http"
	"os"
	"path"
	"reflect"
	"strings"

	"github.com/qor/qor/admin"
)

type PublishController struct {
	*DB
}

func (db *PublishController) Preview(context *admin.Context) {
	draftDB := db.DraftMode()
	drafts := make(map[*admin.Resource]interface{})
	for _, model := range db.SupportedModels {
		var res *admin.Resource
		var name = modelType(model).Name()

		if r := context.Admin.GetResource(strings.ToLower(name)); r != nil {
			res = r
		} else {
			res = admin.NewResource(model)
		}

		results := res.NewSlice()
		draftDB.Unscoped().Where("publish_status = ?", DIRTY).Find(results)
		drafts[res] = results
	}
	context.Execute("publish/drafts", drafts)
}

func (db *PublishController) Diff(context *admin.Context) {
	resourceID := strings.Split(context.Request.URL.Path, "/")[4]
	params := strings.Split(resourceID, "__")
	name, id := params[0], params[1]
	res := context.Admin.GetResource(name)

	draft := res.NewStruct()
	db.DraftMode().Unscoped().First(draft, id)

	production := res.NewStruct()
	db.ProductionMode().Unscoped().First(production, id)

	results := map[string]interface{}{"Production": production, "Draft": draft, "Resource": res}

	fmt.Fprintf(context.Writer, context.Render("publish/diff", results))
}

func (db *PublishController) Publish(context *admin.Context) {
	var request = context.Request
	var ids = request.Form["checked_ids[]"]

	if request.Form.Get("publish_type") == "publish" {
		var records = []interface{}{}
		var values = map[string][]string{}

		for _, id := range ids {
			if keys := strings.Split(id, "__"); len(keys) == 2 {
				name, id := keys[0], keys[1]
				values[name] = append(values[name], id)
			}
		}

		for name, value := range values {
			res := context.Admin.GetResource(name)
			results := res.NewSlice()
			if db.DraftMode().Unscoped().Find(results, fmt.Sprintf("%v IN (?)", res.PrimaryKey()), value).Error == nil {
				resultValues := reflect.Indirect(reflect.ValueOf(results))
				for i := 0; i < resultValues.Len(); i++ {
					records = append(records, resultValues.Index(i).Interface())
				}
			}
		}
		db.DB.Publish(records...)
		http.Redirect(context.Writer, context.Request, context.Request.RequestURI, http.StatusFound)
	} else if request.Form.Get("publish_type") == "discard" {
		fmt.Fprint(context.Writer, "not supported yet")
	}
}

func (db *DB) InjectQorAdmin(web *admin.Admin) {
	controller := PublishController{db}
	router := web.GetRouter()
	router.Get("^/publish/diff/", controller.Diff)
	router.Get("^/publish", controller.Preview)
	router.Post("^/publish", controller.Publish)

	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		admin.RegisterViewPath(path.Join(gopath, "src/github.com/qor/qor/publish/views"))
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package restore

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/dag"
	"github.com/jacobsa/comeback/internal/fs"
	. "github.com/jacobsa/ogletest"
	"github.com/jacobsa/timeutil"
)

func TestVisitor(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type VisitorTest struct {
	ctx       context.Context
	blobStore blob.Store

	// A directory that is deleted when the test completes.
	dir string

	// A visitor configured with the above directory.
	visitor dag.Visitor
}

var _ SetUpInterface = &VisitorTest{}
var _ TearDownInterface = &VisitorTest{}

func init() { RegisterTestSuite(&VisitorTest{}) }

func (t *VisitorTest) SetUp(ti *TestInfo) {
	var err error
	t.ctx = ti.Ctx

	// Create the blob store.
	t.blobStore, err = newFakeBlobStore(t.ctx)
	AssertEq(nil, err)

	// Set up the directory.
	t.dir, err = ioutil.TempDir("", "visitor_test")
	AssertEq(nil, err)

	// Create the visitor.
	t.visitor = newVisitor(t.dir, t.blobStore, log.New(ioutil.Discard, "", 0))
}

func (t *VisitorTest) TearDown() {
	var err error

	err = os.RemoveAll(t.dir)
	AssertEq(nil, err)
}

func (t *VisitorTest) call(n *node) (err error) {
	err = t.visitor.Visit(t.ctx, n)
	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *VisitorTest) ParentDirsAlreadyExist() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_MissingBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_CorruptBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_Empty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_NonEmpty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_PermsAndModTime() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) Directory_MissingBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) Directory_CorruptBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) Directory() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeDirectory,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeDir, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))
}

func (t *VisitorTest) Symlink() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeSymlink,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
			Target:      "taco/burrito",
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeSymlink, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))

	// Readlink
	target, err := os.Readlink(p)

	AssertEq(nil, err)
	ExpectEq("taco/burrito", target)
}
<commit_msg>Removed test names that don't make sense.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package restore

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/dag"
	"github.com/jacobsa/comeback/internal/fs"
	. "github.com/jacobsa/ogletest"
	"github.com/jacobsa/timeutil"
)

func TestVisitor(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type VisitorTest struct {
	ctx       context.Context
	blobStore blob.Store

	// A directory that is deleted when the test completes.
	dir string

	// A visitor configured with the above directory.
	visitor dag.Visitor
}

var _ SetUpInterface = &VisitorTest{}
var _ TearDownInterface = &VisitorTest{}

func init() { RegisterTestSuite(&VisitorTest{}) }

func (t *VisitorTest) SetUp(ti *TestInfo) {
	var err error
	t.ctx = ti.Ctx

	// Create the blob store.
	t.blobStore, err = newFakeBlobStore(t.ctx)
	AssertEq(nil, err)

	// Set up the directory.
	t.dir, err = ioutil.TempDir("", "visitor_test")
	AssertEq(nil, err)

	// Create the visitor.
	t.visitor = newVisitor(t.dir, t.blobStore, log.New(ioutil.Discard, "", 0))
}

func (t *VisitorTest) TearDown() {
	var err error

	err = os.RemoveAll(t.dir)
	AssertEq(nil, err)
}

func (t *VisitorTest) call(n *node) (err error) {
	err = t.visitor.Visit(t.ctx, n)
	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *VisitorTest) ParentDirsAlreadyExist() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_MissingBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_CorruptBlob() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_Empty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_NonEmpty() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) File_PermsAndModTime() {
	AssertTrue(false, "TODO")
}

func (t *VisitorTest) Directory() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeDirectory,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeDir, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))
}

func (t *VisitorTest) Symlink() {
	var err error

	n := &node{
		RelPath: "foo/bar/baz",
		Info: fs.DirectoryEntry{
			Type:        fs.TypeSymlink,
			Name:        "baz",
			Permissions: 0741,
			MTime:       time.Date(2012, time.August, 15, 12, 56, 00, 0, time.Local),
			Target:      "taco/burrito",
		},
	}

	// Call
	err = t.call(n)
	AssertEq(nil, err)

	// Stat
	p := path.Join(t.dir, n.RelPath)
	fi, err := os.Lstat(p)
	AssertEq(nil, err)

	ExpectEq("baz", fi.Name())
	ExpectEq(0741|os.ModeSymlink, fi.Mode())
	ExpectThat(fi.ModTime(), timeutil.TimeEq(n.Info.MTime))

	// Readlink
	target, err := os.Readlink(p)

	AssertEq(nil, err)
	ExpectEq("taco/burrito", target)
}
<|endoftext|>


<commit_before>package validator

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/protobuf/jsonpb"
	"github.com/googleapis/gapic-config-validator/internal/config"
	"github.com/jhump/protoreflect/desc"
	"google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/genproto/googleapis/longrunning"
)

var (
	wellKnownPatterns = map[string]bool{
		"projects/{project}":                      true,
		"organizations/{organization}":            true,
		"folders/{folder}":                        true,
		"projects/{project}/locations/{location}": true,
		"billingAccounts/{billing_account_id}":    true,
	}
)

func (v *validator) compare() {
	// compare interfaces
	v.compareServices()

	// compare resource references
	v.compareResourceRefs()
}

func (v *validator) compareServices() {
	for _, inter := range v.gapic.GetInterfaces() {
		serv := v.resolveServiceByName(inter.GetName())
		if serv == nil {
			v.addError("Interface %q does not exist", inter.GetName())
			continue
		}

		// compare resources
		v.compareResources(inter)

		// compare methods
		for _, method := range inter.GetMethods() {
			methodDesc := serv.FindMethodByName(method.GetName())
			if methodDesc == nil {
				v.addError("Method %q does not exist", inter.GetName()+"."+method.GetName())
				continue
			}

			v.compareMethod(methodDesc, method)
		}
	}
}

func (v *validator) compareMethod(methodDesc *desc.MethodDescriptor, method *config.MethodConfigProto) {
	fqn := methodDesc.GetFullyQualifiedName()
	mOpts := methodDesc.GetMethodOptions()

	// compare method_signatures & flattening groups
	if flattenings := method.GetFlattening(); flattenings != nil {
		eSigs, err := ext(mOpts, annotations.E_MethodSignature)
		if err != nil {
			v.addError("Method %q missing method_signature(s) for flattening(s)", fqn)
			goto LRO
		}
		sigs := eSigs.([]string)

		for _, flat := range flattenings.GetGroups() {
			joined := strings.Join(flat.GetParameters(), ",")
			if !containStr(sigs, joined) {
				v.addError("Method %q missing method_signature for flattening %q", fqn, joined)
			}
		}
	}

LRO:
	// compare operation_info & longrunning config
	if lro := method.GetLongRunning(); lro != nil {
		eLRO, err := ext(mOpts, longrunning.E_OperationInfo)
		if err != nil {
			v.addError("Method %q missing longrunning.operation_info", fqn)
			goto Behavior
		}
		info := eLRO.(*longrunning.OperationInfo)

		if info.GetResponseType() != lro.GetReturnType() {
			v.addError("Method %q operation_info.response_type %q does not match %q",
				fqn,
				info.GetResponseType(),
				lro.GetReturnType())
		}

		if info.GetMetadataType() != lro.GetMetadataType() {
			v.addError("Method %q operation_info.metadata_type %q does not match %q",
				fqn,
				info.GetMetadataType(),
				lro.GetMetadataType())
		}
	}

Behavior:
	// compare input message field_behaviors & required_fields
	if reqs := method.GetRequiredFields(); len(reqs) > 0 {
		input := methodDesc.GetInputType()

		for _, name := range reqs {
			field := input.FindFieldByName(name)
			if field == nil {
				v.addError("Field %q in method %q required_fields does not exist in %q",
					name,
					fqn,
					input.GetFullyQualifiedName())
				continue
			}

			eBehv, err := ext(field.GetFieldOptions(), annotations.E_FieldBehavior)
			if err != nil {
				v.addError("Field %q is missing field_behavior = REQUIRED per required_fields config", field.GetFullyQualifiedName())
				continue
			}
			behavior := eBehv.([]annotations.FieldBehavior)

			if !containBehavior(behavior, annotations.FieldBehavior_REQUIRED) {
				v.addError("Field %q is not annotated as REQUIRED per required_fields config", field.GetFullyQualifiedName())
			}
		}
	}
}

func (v *validator) compareResources(inter *config.InterfaceConfigProto) {
	for _, res := range inter.GetCollections() {
		if wellKnownPatterns[res.GetNamePattern()] {
			continue
		}

		for _, f := range v.files {
			for _, m := range f.GetMessageTypes() {
				eRes, err := ext(m.GetMessageOptions(), annotations.E_Resource)
				if err != nil {
					continue
				}
				resDesc := eRes.(*annotations.ResourceDescriptor)

				typ := resDesc.GetType()
				typ = strings.ToLower(typ[strings.Index(typ, "/")+1:])

				if typ == res.GetEntityName() {
					if !containStr(resDesc.GetPattern(), res.GetNamePattern()) {
						v.addError("resource definition for %q in %q does not have pattern %q",
							resDesc.GetType(),
							m.GetFullyQualifiedName(),
							res.GetNamePattern())
					}

					goto Next
				}
			}
		}

		v.addError("No corresponding resource definition for %q: %q", res.GetEntityName(), res.GetNamePattern())

	Next:
	}
}

func (v *validator) compareResourceRefs() {
	for _, ref := range v.gapic.GetResourceNameGeneration() {
		msgDesc := v.resolveMsgByLocalName(ref.GetMessageName())
		if msgDesc == nil {
			v.addError("Message %q in resource_name_generation item does not exist", ref.GetMessageName())
			continue
		}

		for fname, ref := range ref.GetFieldEntityMap() {
			// skip nested fields, presumably they are
			// being validated in the origial msg
			if strings.Contains(fname, ".") {
				continue
			}

			field := msgDesc.FindFieldByName(fname)
			if field == nil {
				v.addError("Field %q does not exist on message %q per resource_name_generation item", fname, msgDesc.GetFullyQualifiedName())
				continue
			}

			var typ string
			if eResRef, err := ext(field.GetFieldOptions(), annotations.E_ResourceReference); err == nil {
				resRef := eResRef.(*annotations.ResourceReference)

				typ = resRef.GetType()
				if typ == "" {
					typ = resRef.GetChildType()
				}
			} else if eRes, err := ext(msgDesc.GetMessageOptions(), annotations.E_Resource); err == nil {
				res := eRes.(*annotations.ResourceDescriptor)
				typ = res.GetType()
			} else {
				v.addError("Field %q missing resource_reference to %q", field.GetFullyQualifiedName(), ref)
				continue
			}

			t := strings.ToLower(typ[strings.Index(typ, "/")+1:])
			if !wellKnownTypes[typ] && t != ref {
				v.addError("Field %q resource_type_kind %q doesn't match %q in config", field.GetFullyQualifiedName(), typ, ref)
			}
		}
	}
}

func containBehavior(arr []annotations.FieldBehavior, behv annotations.FieldBehavior) bool {
	for _, b := range arr {
		if b == behv {
			return true
		}
	}

	return false
}

func containStr(arr []string, str string) bool {
	for _, s := range arr {
		if s == str {
			return true
		}
	}

	return false
}

func (v *validator) resolveServiceByName(name string) *desc.ServiceDescriptor {
	for _, f := range v.files {
		if s := f.FindService(name); s != nil {
			return s
		}
	}

	return nil
}

func (v *validator) resolveMsgByLocalName(name string) *desc.MessageDescriptor {
	for _, f := range v.files {
		fqn := f.GetPackage() + "." + name

		if m := f.FindMessage(fqn); m != nil {
			return m
		}
	}

	return nil
}

func (v *validator) parseParameters(p string) error {
	for _, s := range strings.Split(p, ",") {
		if e := strings.IndexByte(s, '='); e > 0 {
			switch s[:e] {
			case "gapic-yaml":

				f, err := ioutil.ReadFile(s[e+1:])
				if err != nil {
					return fmt.Errorf("error reading gapic config: %v", err)
				}

				// throw away the first line containing
				// "type: com.google.api.codegen.ConfigProto" because
				// that's not in the proto, causing an unmarshal error
				data := bytes.NewBuffer(f)
				data.ReadString('\n')

				j, err := yaml.YAMLToJSON(data.Bytes())
				if err != nil {
					return fmt.Errorf("error decoding gapic config: %v", err)
				}

				v.gapic = &config.ConfigProto{}
				err = jsonpb.Unmarshal(bytes.NewBuffer(j), v.gapic)
				if err != nil {
					return fmt.Errorf("error decoding gapic config: %v", err)
				}
			}
		}
	}

	return nil
}
<commit_msg>comparison: fix resc/entity name matching (#20)<commit_after>package validator

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"strings"
	"unicode"

	"github.com/ghodss/yaml"
	"github.com/golang/protobuf/jsonpb"
	"github.com/googleapis/gapic-config-validator/internal/config"
	"github.com/jhump/protoreflect/desc"
	"google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/genproto/googleapis/longrunning"
)

var (
	wellKnownPatterns = map[string]bool{
		"projects/{project}":                      true,
		"organizations/{organization}":            true,
		"folders/{folder}":                        true,
		"projects/{project}/locations/{location}": true,
		"billingAccounts/{billing_account_id}":    true,
	}
)

func (v *validator) compare() {
	// compare interfaces
	v.compareServices()

	// compare resource references
	v.compareResourceRefs()
}

func (v *validator) compareServices() {
	for _, inter := range v.gapic.GetInterfaces() {
		serv := v.resolveServiceByName(inter.GetName())
		if serv == nil {
			v.addError("Interface %q does not exist", inter.GetName())
			continue
		}

		// compare resources
		v.compareResources(inter)

		// compare methods
		for _, method := range inter.GetMethods() {
			methodDesc := serv.FindMethodByName(method.GetName())
			if methodDesc == nil {
				v.addError("Method %q does not exist", inter.GetName()+"."+method.GetName())
				continue
			}

			v.compareMethod(methodDesc, method)
		}
	}
}

func (v *validator) compareMethod(methodDesc *desc.MethodDescriptor, method *config.MethodConfigProto) {
	fqn := methodDesc.GetFullyQualifiedName()
	mOpts := methodDesc.GetMethodOptions()

	// compare method_signatures & flattening groups
	if flattenings := method.GetFlattening(); flattenings != nil {
		eSigs, err := ext(mOpts, annotations.E_MethodSignature)
		if err != nil {
			v.addError("Method %q missing method_signature(s) for flattening(s)", fqn)
			goto LRO
		}
		sigs := eSigs.([]string)

		for _, flat := range flattenings.GetGroups() {
			joined := strings.Join(flat.GetParameters(), ",")
			if !containStr(sigs, joined) {
				v.addError("Method %q missing method_signature for flattening %q", fqn, joined)
			}
		}
	}

LRO:
	// compare operation_info & longrunning config
	if lro := method.GetLongRunning(); lro != nil {
		eLRO, err := ext(mOpts, longrunning.E_OperationInfo)
		if err != nil {
			v.addError("Method %q missing longrunning.operation_info", fqn)
			goto Behavior
		}
		info := eLRO.(*longrunning.OperationInfo)

		if info.GetResponseType() != lro.GetReturnType() {
			v.addError("Method %q operation_info.response_type %q does not match %q",
				fqn,
				info.GetResponseType(),
				lro.GetReturnType())
		}

		if info.GetMetadataType() != lro.GetMetadataType() {
			v.addError("Method %q operation_info.metadata_type %q does not match %q",
				fqn,
				info.GetMetadataType(),
				lro.GetMetadataType())
		}
	}

Behavior:
	// compare input message field_behaviors & required_fields
	if reqs := method.GetRequiredFields(); len(reqs) > 0 {
		input := methodDesc.GetInputType()

		for _, name := range reqs {
			field := input.FindFieldByName(name)
			if field == nil {
				v.addError("Field %q in method %q required_fields does not exist in %q",
					name,
					fqn,
					input.GetFullyQualifiedName())
				continue
			}

			eBehv, err := ext(field.GetFieldOptions(), annotations.E_FieldBehavior)
			if err != nil {
				v.addError("Field %q is missing field_behavior = REQUIRED per required_fields config", field.GetFullyQualifiedName())
				continue
			}
			behavior := eBehv.([]annotations.FieldBehavior)

			if !containBehavior(behavior, annotations.FieldBehavior_REQUIRED) {
				v.addError("Field %q is not annotated as REQUIRED per required_fields config", field.GetFullyQualifiedName())
			}
		}
	}
}

func (v *validator) compareResources(inter *config.InterfaceConfigProto) {
	for _, res := range inter.GetCollections() {
		if wellKnownPatterns[res.GetNamePattern()] {
			continue
		}

		for _, f := range v.files {
			for _, m := range f.GetMessageTypes() {
				eRes, err := ext(m.GetMessageOptions(), annotations.E_Resource)
				if err != nil {
					continue
				}
				resDesc := eRes.(*annotations.ResourceDescriptor)

				typ := resDesc.GetType()
				typ = typ[strings.Index(typ, "/")+1:]

				entName := snakeToCamel(res.GetEntityName())

				if typ == entName {
					if !containStr(resDesc.GetPattern(), res.GetNamePattern()) {
						v.addError("resource definition for %q in %q does not have pattern %q",
							resDesc.GetType(),
							m.GetFullyQualifiedName(),
							res.GetNamePattern())
					}

					goto Next
				}
			}
		}

		v.addError("No corresponding resource definition for %q: %q", res.GetEntityName(), res.GetNamePattern())

	Next:
	}
}

func (v *validator) compareResourceRefs() {
	for _, ref := range v.gapic.GetResourceNameGeneration() {
		msgDesc := v.resolveMsgByLocalName(ref.GetMessageName())
		if msgDesc == nil {
			v.addError("Message %q in resource_name_generation item does not exist", ref.GetMessageName())
			continue
		}

		for fname, ref := range ref.GetFieldEntityMap() {
			// skip nested fields, presumably they are
			// being validated in the origial msg
			if strings.Contains(fname, ".") {
				continue
			}

			field := msgDesc.FindFieldByName(fname)
			if field == nil {
				v.addError("Field %q does not exist on message %q per resource_name_generation item", fname, msgDesc.GetFullyQualifiedName())
				continue
			}

			var typ string
			if eResRef, err := ext(field.GetFieldOptions(), annotations.E_ResourceReference); err == nil {
				resRef := eResRef.(*annotations.ResourceReference)

				typ = resRef.GetType()
				if typ == "" {
					typ = resRef.GetChildType()
				}
			} else if eRes, err := ext(msgDesc.GetMessageOptions(), annotations.E_Resource); err == nil {
				res := eRes.(*annotations.ResourceDescriptor)
				typ = res.GetType()
			} else {
				v.addError("Field %q missing resource_reference to %q", field.GetFullyQualifiedName(), ref)
				continue
			}

			t := strings.ToLower(typ[strings.Index(typ, "/")+1:])
			if !wellKnownTypes[typ] && t != ref {
				v.addError("Field %q resource_type_kind %q doesn't match %q in config", field.GetFullyQualifiedName(), typ, ref)
			}
		}
	}
}

func containBehavior(arr []annotations.FieldBehavior, behv annotations.FieldBehavior) bool {
	for _, b := range arr {
		if b == behv {
			return true
		}
	}

	return false
}

func containStr(arr []string, str string) bool {
	for _, s := range arr {
		if s == str {
			return true
		}
	}

	return false
}

func (v *validator) resolveServiceByName(name string) *desc.ServiceDescriptor {
	for _, f := range v.files {
		if s := f.FindService(name); s != nil {
			return s
		}
	}

	return nil
}

func (v *validator) resolveMsgByLocalName(name string) *desc.MessageDescriptor {
	for _, f := range v.files {
		fqn := f.GetPackage() + "." + name

		if m := f.FindMessage(fqn); m != nil {
			return m
		}
	}

	return nil
}

func (v *validator) parseParameters(p string) error {
	for _, s := range strings.Split(p, ",") {
		if e := strings.IndexByte(s, '='); e > 0 {
			switch s[:e] {
			case "gapic-yaml":

				f, err := ioutil.ReadFile(s[e+1:])
				if err != nil {
					return fmt.Errorf("error reading gapic config: %v", err)
				}

				// throw away the first line containing
				// "type: com.google.api.codegen.ConfigProto" because
				// that's not in the proto, causing an unmarshal error
				data := bytes.NewBuffer(f)
				data.ReadString('\n')

				j, err := yaml.YAMLToJSON(data.Bytes())
				if err != nil {
					return fmt.Errorf("error decoding gapic config: %v", err)
				}

				v.gapic = &config.ConfigProto{}
				err = jsonpb.Unmarshal(bytes.NewBuffer(j), v.gapic)
				if err != nil {
					return fmt.Errorf("error decoding gapic config: %v", err)
				}
			}
		}
	}

	return nil
}

// converts snake_case and SNAKE_CASE to CamelCase.
//
// copied from github.com/googleapis/gapic-generator-go
func snakeToCamel(s string) string {
	var sb strings.Builder
	up := true
	for _, r := range s {
		if r == '_' {
			up = true
		} else if up {
			sb.WriteRune(unicode.ToUpper(r))
			up = false
		} else {
			sb.WriteRune(unicode.ToLower(r))
		}
	}
	return sb.String()
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package zmqutil

import (
	"bytes"
	"crypto/rand"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/util"
	zmq "github.com/pebbe/zmq4"
	"sync"
	"time"
)

// structure to hold a client connection
type Client struct {
	publicKey       []byte
	privateKey      []byte
	serverPublicKey []byte
	address         string
	v6              bool
	socketType      zmq.Type
	socket          *zmq.Socket
	poller          *Poller
	events          zmq.State
	timeout         time.Duration
	timestamp       time.Time
}

const (
	publicKeySize  = 32
	privateKeySize = 32
	identifierSize = 32
)

type globalClientDataType struct {
	sync.Mutex
	clients map[*zmq.Socket]*Client
}

var globalClientData = globalClientDataType{
	clients: make(map[*zmq.Socket]*Client),
}

// create a client socket ususlly of type zmq.REQ or zmq.SUB
func NewClient(socketType zmq.Type, privateKey []byte, publicKey []byte, timeout time.Duration) (*Client, error) {

	if len(publicKey) != publicKeySize {
		return nil, fault.ErrInvalidPublicKey
	}
	if len(privateKey) != privateKeySize {
		return nil, fault.ErrInvalidPrivateKey
	}

	client := &Client{
		publicKey:       make([]byte, publicKeySize),
		privateKey:      make([]byte, privateKeySize),
		serverPublicKey: make([]byte, publicKeySize),
		address:         "",
		v6:              false,
		socketType:      socketType,
		socket:          nil,
		poller:          nil,
		events:          0,
		timeout:         timeout,
		timestamp:       time.Now(),
	}
	copy(client.privateKey, privateKey)
	copy(client.publicKey, publicKey)
	return client, nil
}

// create a socket and connect to specific server with specifed key
func (client *Client) openSocket() error {

	socket, err := zmq.NewSocket(client.socketType)
	if nil != err {
		return err
	}

	// create a secure random identifier
	randomIdBytes := make([]byte, identifierSize)
	_, err = rand.Read(randomIdBytes)
	if nil != err {
		return err
	}
	randomIdentifier := string(randomIdBytes)

	// set up as client
	err = socket.SetCurveServer(0)
	if nil != err {
		goto failure
	}
	err = socket.SetCurvePublickey(string(client.publicKey))
	if nil != err {
		goto failure
	}
	err = socket.SetCurveSecretkey(string(client.privateKey))
	if nil != err {
		goto failure
	}

	// local identitity is a random value
	err = socket.SetIdentity(randomIdentifier)
	if nil != err {
		goto failure
	}

	// destination identity is its public key
	err = socket.SetCurveServerkey(string(client.serverPublicKey))
	if nil != err {
		goto failure
	}

	// // basic socket options
	// err=socket.SetRouterMandatory(0)   // discard unroutable packets
	// err=socket.SetRouterHandover(true) // allow quick reconnect for a given public key
	// err=socket.SetImmediate(false)     // queue messages sent to disconnected peer

	// zero => do not set timeout
	if 0 != client.timeout {
		err = socket.SetSndtimeo(client.timeout)
		if nil != err {
			goto failure
		}
		err = socket.SetRcvtimeo(client.timeout)
		if nil != err {
			goto failure
		}
	}
	err = socket.SetLinger(0)
	if nil != err {
		goto failure
	}

	// stype specific options
	switch client.socketType {
	case zmq.REQ:
		err = socket.SetReqCorrelate(1)
		if nil != err {
			goto failure
		}
		err = socket.SetReqRelaxed(1)
		if nil != err {
			goto failure
		}

	case zmq.SUB:
		// set subscription prefix - empty => receive everything
		err = socket.SetSubscribe("")
		if nil != err {
			goto failure
		}

	default:
	}

	// this need zmq 4.2
	// heartbeat (constants from socket.go)
	err = socket.SetHeartbeatIvl(heartbeatInterval)
	if nil != err && zmq.ErrorNotImplemented42 != err {
		goto failure
	}
	err = socket.SetHeartbeatTimeout(heartbeatTimeout)
	if nil != err && zmq.ErrorNotImplemented42 != err {
		goto failure
	}
	err = socket.SetHeartbeatTtl(heartbeatTTL)
	if nil != err && zmq.ErrorNotImplemented42 != err {
		goto failure
	}

	// set IPv6 state before connect
	err = socket.SetIpv6(client.v6)
	if nil != err {
		goto failure
	}

	// new connection
	err = socket.Connect(client.address)
	if nil != err {
		goto failure
	}

	client.socket = socket

	// register client globally
	globalClientData.Lock()
	globalClientData.clients[socket] = client
	globalClientData.Unlock()

	// add to poller
	if nil != client.poller {
		client.poller.Add(client.socket, client.events)
	}
	return nil
failure:
	socket.Close()
	return err
}

// destroy the socket, bute leav other connection info so can recconect
// to the same endpoint again
func (client *Client) closeSocket() error {

	if nil == client.socket {
		return nil
	}

	// stop any polling
	if nil != client.poller {
		client.poller.Remove(client.socket)
	}

	// if already connected, disconnect first
	if "" != client.address {
		client.socket.Disconnect(client.address)
	}

	// unregister client globally
	globalClientData.Lock()
	delete(globalClientData.clients, client.socket)
	globalClientData.Unlock()

	// close socket
	err := client.socket.Close()
	client.socket = nil
	return err
}

// disconnect old address and connect to new
func (client *Client) Connect(conn *util.Connection, serverPublicKey []byte) error {

	// if already connected, disconnect first
	err := client.closeSocket()
	if nil != err {
		return err
	}
	client.address = ""

	// small delay to allow any backgroud socket closing
	// and to restrict rate of reconnection
	time.Sleep(5 * time.Millisecond)

	copy(client.serverPublicKey, serverPublicKey)

	client.address, client.v6 = conn.CanonicalIPandPort("tcp://")

	client.timestamp = time.Now()

	return client.openSocket()
}

// check if connected to a node
func (client *Client) IsConnected() bool {
	return "" != client.address
}

// check if connected to a specific node
func (client *Client) IsConnectedTo(serverPublicKey []byte) bool {
	return bytes.Equal(client.publicKey, serverPublicKey)
}

// // check if not connected to any node
// func (client *Client) IsDisconnected() bool {
// 	return "" == client.address
// }

// // get the age of connection
// func (client *Client) Age() time.Duration {
// 	return time.Since(client.timestamp)
// }

// close and reopen the connection
func (client *Client) Reconnect() error {
	_, err := client.ReconnectReturningSocket()
	return err
}

// close and reopen the connection
func (client *Client) ReconnectReturningSocket() (*zmq.Socket, error) {

	err := client.closeSocket()
	if nil != err {
		return nil, err
	}
	err = client.openSocket()
	if nil != err {
		return nil, err
	}
	return client.socket, nil
}

// disconnect old address and close
func (client *Client) Close() error {
	return client.closeSocket()
}

// disconnect old addresses and close all
func CloseClients(clients []*Client) {
	for _, client := range clients {
		if nil != client {
			client.Close()
		}
	}
}

// send a message
func (client *Client) Send(items ...interface{}) error {
	if "" == client.address {
		return fault.ErrNotConnected
	}

	last := len(items) - 1
	for i, item := range items {

		flag := zmq.SNDMORE
		if i == last {
			flag = 0
		}
		switch it := item.(type) {
		case string:
			_, err := client.socket.Send(it, flag)
			if nil != err {
				return err
			}
		case []byte:
			_, err := client.socket.SendBytes(it, flag)
			if nil != err {
				return err
			}
		}
	}
	return nil
}

// receive a reply
func (client *Client) Receive(flags zmq.Flag) ([][]byte, error) {
	if "" == client.address {
		return nil, fault.ErrNotConnected
	}
	data, err := client.socket.RecvMessageBytes(flags)
	return data, err
}

// add poller to client
func (client *Client) BeginPolling(poller *Poller, events zmq.State) *zmq.Socket {

	// if poller changed
	if nil != client.poller && nil != client.socket {
		client.poller.Remove(client.socket)
	}

	// add to new poller
	client.poller = poller
	client.events = events
	if nil != client.socket {
		poller.Add(client.socket, events)
	}
	return client.socket
}

// to string
func (client Client) String() string {
	return client.address
}

// find the client corresponding to a socket
func ClientFromSocket(socket *zmq.Socket) *Client {
	globalClientData.Lock()
	client := globalClientData.clients[socket]
	globalClientData.Unlock()
	return client
}
<commit_msg>[zmqutil] spelling changes<commit_after>// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package zmqutil

import (
	"bytes"
	"crypto/rand"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/util"
	zmq "github.com/pebbe/zmq4"
	"sync"
	"time"
)

// structure to hold a client connection
type Client struct {
	publicKey       []byte
	privateKey      []byte
	serverPublicKey []byte
	address         string
	v6              bool
	socketType      zmq.Type
	socket          *zmq.Socket
	poller          *Poller
	events          zmq.State
	timeout         time.Duration
	timestamp       time.Time
}

const (
	publicKeySize  = 32
	privateKeySize = 32
	identifierSize = 32
)

type globalClientDataType struct {
	sync.Mutex
	clients map[*zmq.Socket]*Client
}

var globalClientData = globalClientDataType{
	clients: make(map[*zmq.Socket]*Client),
}

// create a client socket ususlly of type zmq.REQ or zmq.SUB
func NewClient(socketType zmq.Type, privateKey []byte, publicKey []byte, timeout time.Duration) (*Client, error) {

	if len(publicKey) != publicKeySize {
		return nil, fault.ErrInvalidPublicKey
	}
	if len(privateKey) != privateKeySize {
		return nil, fault.ErrInvalidPrivateKey
	}

	client := &Client{
		publicKey:       make([]byte, publicKeySize),
		privateKey:      make([]byte, privateKeySize),
		serverPublicKey: make([]byte, publicKeySize),
		address:         "",
		v6:              false,
		socketType:      socketType,
		socket:          nil,
		poller:          nil,
		events:          0,
		timeout:         timeout,
		timestamp:       time.Now(),
	}
	copy(client.privateKey, privateKey)
	copy(client.publicKey, publicKey)
	return client, nil
}

// create a socket and connect to specific server with specifed key
func (client *Client) openSocket() error {

	socket, err := zmq.NewSocket(client.socketType)
	if nil != err {
		return err
	}

	// create a secure random identifier
	randomIdBytes := make([]byte, identifierSize)
	_, err = rand.Read(randomIdBytes)
	if nil != err {
		return err
	}
	randomIdentifier := string(randomIdBytes)

	// set up as client
	err = socket.SetCurveServer(0)
	if nil != err {
		goto failure
	}
	err = socket.SetCurvePublickey(string(client.publicKey))
	if nil != err {
		goto failure
	}
	err = socket.SetCurveSecretkey(string(client.privateKey))
	if nil != err {
		goto failure
	}

	// local identitity is a random value
	err = socket.SetIdentity(randomIdentifier)
	if nil != err {
		goto failure
	}

	// destination identity is its public key
	err = socket.SetCurveServerkey(string(client.serverPublicKey))
	if nil != err {
		goto failure
	}

	// // basic socket options
	// err=socket.SetRouterMandatory(0)   // discard unroutable packets
	// err=socket.SetRouterHandover(true) // allow quick reconnect for a given public key
	// err=socket.SetImmediate(false)     // queue messages sent to disconnected peer

	// zero => do not set timeout
	if 0 != client.timeout {
		err = socket.SetSndtimeo(client.timeout)
		if nil != err {
			goto failure
		}
		err = socket.SetRcvtimeo(client.timeout)
		if nil != err {
			goto failure
		}
	}
	err = socket.SetLinger(0)
	if nil != err {
		goto failure
	}

	// stype specific options
	switch client.socketType {
	case zmq.REQ:
		err = socket.SetReqCorrelate(1)
		if nil != err {
			goto failure
		}
		err = socket.SetReqRelaxed(1)
		if nil != err {
			goto failure
		}

	case zmq.SUB:
		// set subscription prefix - empty => receive everything
		err = socket.SetSubscribe("")
		if nil != err {
			goto failure
		}

	default:
	}

	// this need zmq 4.2
	// heartbeat (constants from socket.go)
	err = socket.SetHeartbeatIvl(heartbeatInterval)
	if nil != err && zmq.ErrorNotImplemented42 != err {
		goto failure
	}
	err = socket.SetHeartbeatTimeout(heartbeatTimeout)
	if nil != err && zmq.ErrorNotImplemented42 != err {
		goto failure
	}
	err = socket.SetHeartbeatTtl(heartbeatTTL)
	if nil != err && zmq.ErrorNotImplemented42 != err {
		goto failure
	}

	// set IPv6 state before connect
	err = socket.SetIpv6(client.v6)
	if nil != err {
		goto failure
	}

	// new connection
	err = socket.Connect(client.address)
	if nil != err {
		goto failure
	}

	client.socket = socket

	// register client globally
	globalClientData.Lock()
	globalClientData.clients[socket] = client
	globalClientData.Unlock()

	// add to poller
	if nil != client.poller {
		client.poller.Add(client.socket, client.events)
	}
	return nil
failure:
	socket.Close()
	return err
}

// destroy the socket, but leave other connection info so can reconnect
// to the same endpoint again
func (client *Client) closeSocket() error {

	if nil == client.socket {
		return nil
	}

	// stop any polling
	if nil != client.poller {
		client.poller.Remove(client.socket)
	}

	// if already connected, disconnect first
	if "" != client.address {
		client.socket.Disconnect(client.address)
	}

	// unregister client globally
	globalClientData.Lock()
	delete(globalClientData.clients, client.socket)
	globalClientData.Unlock()

	// close socket
	err := client.socket.Close()
	client.socket = nil
	return err
}

// disconnect old address and connect to new
func (client *Client) Connect(conn *util.Connection, serverPublicKey []byte) error {

	// if already connected, disconnect first
	err := client.closeSocket()
	if nil != err {
		return err
	}
	client.address = ""

	// small delay to allow any backgroud socket closing
	// and to restrict rate of reconnection
	time.Sleep(5 * time.Millisecond)

	copy(client.serverPublicKey, serverPublicKey)

	client.address, client.v6 = conn.CanonicalIPandPort("tcp://")

	client.timestamp = time.Now()

	return client.openSocket()
}

// check if connected to a node
func (client *Client) IsConnected() bool {
	return "" != client.address
}

// check if connected to a specific node
func (client *Client) IsConnectedTo(serverPublicKey []byte) bool {
	return bytes.Equal(client.publicKey, serverPublicKey)
}

// // check if not connected to any node
// func (client *Client) IsDisconnected() bool {
// 	return "" == client.address
// }

// // get the age of connection
// func (client *Client) Age() time.Duration {
// 	return time.Since(client.timestamp)
// }

// close and reopen the connection
func (client *Client) Reconnect() error {
	_, err := client.ReconnectReturningSocket()
	return err
}

// close and reopen the connection
func (client *Client) ReconnectReturningSocket() (*zmq.Socket, error) {

	err := client.closeSocket()
	if nil != err {
		return nil, err
	}
	err = client.openSocket()
	if nil != err {
		return nil, err
	}
	return client.socket, nil
}

// disconnect old address and close
func (client *Client) Close() error {
	return client.closeSocket()
}

// disconnect old addresses and close all
func CloseClients(clients []*Client) {
	for _, client := range clients {
		if nil != client {
			client.Close()
		}
	}
}

// send a message
func (client *Client) Send(items ...interface{}) error {
	if "" == client.address {
		return fault.ErrNotConnected
	}

	last := len(items) - 1
	for i, item := range items {

		flag := zmq.SNDMORE
		if i == last {
			flag = 0
		}
		switch it := item.(type) {
		case string:
			_, err := client.socket.Send(it, flag)
			if nil != err {
				return err
			}
		case []byte:
			_, err := client.socket.SendBytes(it, flag)
			if nil != err {
				return err
			}
		}
	}
	return nil
}

// receive a reply
func (client *Client) Receive(flags zmq.Flag) ([][]byte, error) {
	if "" == client.address {
		return nil, fault.ErrNotConnected
	}
	data, err := client.socket.RecvMessageBytes(flags)
	return data, err
}

// add poller to client
func (client *Client) BeginPolling(poller *Poller, events zmq.State) *zmq.Socket {

	// if poller changed
	if nil != client.poller && nil != client.socket {
		client.poller.Remove(client.socket)
	}

	// add to new poller
	client.poller = poller
	client.events = events
	if nil != client.socket {
		poller.Add(client.socket, events)
	}
	return client.socket
}

// to string
func (client Client) String() string {
	return client.address
}

// find the client corresponding to a socket
func ClientFromSocket(socket *zmq.Socket) *Client {
	globalClientData.Lock()
	client := globalClientData.clients[socket]
	globalClientData.Unlock()
	return client
}
<|endoftext|>