<commit_before>package generator

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
	"github.com/graphql-go/graphql"
	"reflect"
	"regexp"
	"strings"
	"testing"
)

func matchStrings(exp, got string) bool {
	trexp := strings.TrimSpace(exp)
	trgot := strings.TrimSpace(got)
	reg := regexp.MustCompile(`0x[0-9a-z]+`)
	trexp = reg.ReplaceAllString(trexp, "")
	trgot = reg.ReplaceAllString(trgot, "")
	return trexp == trgot
}

func nextLineMatch(exp, got []string, index1, index2 *int) bool {
	for i := *index1 + 1; i < len(exp); i++ {
		for p := *index2 + 1; p < len(got); p++ {
			if matchStrings(exp[i], got[p]) {
				*index1 = i
				*index2 = p
				return true
			}
		}
	}
	*index1, *index2 = -1, -1
	return false
}

func compareLines(str1, str2 string) {
	l1 := strings.Split(str1, "\n")
	l2 := strings.Split(str2, "\n")

	index1 := -1
	index2 := -1
	lastExp := -1
	lastGot := -1
	for nextLineMatch(l1, l2, &index1, &index2) {
		if index1-lastExp > 1 || index2-lastGot > 1 {
			// Unmatching lines in between!
			unmatched := l1[lastExp+1 : index1]
			unmatchedGot := l2[lastGot+1 : index2]
			fmt.Println("-----------------------------------------------")
			fmt.Println("Mismatch. Expected:")
			fmt.Println(strings.Join(unmatched, "\n"))
			fmt.Println("Got:")
			fmt.Println(strings.Join(unmatchedGot, "\n"))
			fmt.Println("-----------------------------------------------")
		}
		lastExp = index1
		lastGot = index2
	}

	if len(l1)-1 > lastExp {
		unmatched := l1[lastExp+1 : len(l1)-1]
		unmatchedGot := l2[lastGot+1 : len(l2)-1]
		fmt.Println("Mismatch. Expected:\n")
		fmt.Println(strings.Join(unmatched, "\n"))
		fmt.Println("\nGot:\n")
		fmt.Println(strings.Join(unmatchedGot, "\n"))
	}
}

func printFail(var1, var2 interface{}, t *testing.T) {
	t.Fail()
	spew.Config.DisableMethods = true
	fmt.Println("Unexpected schema found.\n")
	compareLines(spew.Sdump(var1), spew.Sdump(var2))
	//compareVars("", var1, var2)
}

func TestSimpleType(t *testing.T) {
	gql := `
type Oncle {
	pipe: ID
	five(argument: [String] = ["String", "String"] ): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: graphql.ID,
			},
			"five": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"argument": &graphql.ArgumentConfig{
						Type:         graphql.NewList(graphql.String),
						DefaultValue: []interface{}{"String", "String"},
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestReferenceTypes(t *testing.T) {
	gql := `
type Oncle {
	pipe: p
}
type p {
	feld: e
}
interface e {}
	`

	e := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "e",
	})
	p := graphql.NewObject(graphql.ObjectConfig{
		Name: "p",
		Fields: graphql.Fields{
			"feld": &graphql.Field{
				Type: e,
			},
		},
	})
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: p,
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestRequiredType(t *testing.T) {
	gql := `
type Oncle {
	pipe: String!
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: graphql.NewNonNull(graphql.String),
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSimpleInterface(t *testing.T) {
	gql := `
interface World {}
`
	expected := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "World",
	})

	ctx, _ := Generate(gql)
	world := ctx.Interface("World")
	if !reflect.DeepEqual(world, expected) {
		printFail(expected, world, t)
	}
}

func TestSimpleTypeImplementsInterface(t *testing.T) {
	gql := `
interface World {}
type Oncle implements World {}
`
	world := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "World",
	})
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name:       "Oncle",
		Interfaces: []*graphql.Interface{world},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSimpleTypeImplementsMultipleInterfaces(t *testing.T) {
	gql := `
interface World {}
interface Balloon {}
type Oncle implements World, Balloon {}
`
	world := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "World",
	})
	balloon := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "Balloon",
	})
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name:       "Oncle",
		Interfaces: []*graphql.Interface{world, balloon},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSingleValueEnum(t *testing.T) {
	gql := `enum Hello { WORLD }`

	expected := graphql.NewEnum(graphql.EnumConfig{
		Name: "Hello",
		Values: graphql.EnumValueConfigMap{
			"WORLD": &graphql.EnumValueConfig{
				Value: 0,
			},
		},
	})

	ctx, _ := Generate(gql)
	enum := ctx.Enums("Hello")
	if !reflect.DeepEqual(enum, expected) {
		printFail(expected, enum, t)
	}
}

func TestMultiValueEnum(t *testing.T) {
	gql := `enum Hello { WORLD, HERE }`

	expected := graphql.NewEnum(graphql.EnumConfig{
		Name: "Hello",
		Values: graphql.EnumValueConfigMap{
			"WORLD": &graphql.EnumValueConfig{
				Value: 0,
			},
			"HERE": &graphql.EnumValueConfig{
				Value: 1,
			},
		},
	})

	ctx, _ := Generate(gql)
	enum := ctx.Enums("Hello")
	if !reflect.DeepEqual(enum, expected) {
		printFail(expected, enum, t)
	}
}

func TestSimpleFieldWithArg(t *testing.T) {
	gql := `
type Hello {
	world(flag: Boolean): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"flag": &graphql.ArgumentConfig{
						Type: graphql.Boolean,
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleFieldWithArgDefaultValue(t *testing.T) {
	gql := `
type Hello {
	world(flag: Boolean! = true): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"flag": &graphql.ArgumentConfig{
						Type:         graphql.NewNonNull(graphql.Boolean),
						DefaultValue: true,
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleFieldWithListArg(t *testing.T) {
	gql := `
type Hello {
	world(things: [String]): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"things": &graphql.ArgumentConfig{
						Type: graphql.NewList(graphql.String),
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleFieldWithTwoArg(t *testing.T) {
	gql := `
type Hello {
	world(argOne: Boolean, argTwo: Int): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"argOne": &graphql.ArgumentConfig{
						Type: graphql.Boolean,
					},
					"argTwo": &graphql.ArgumentConfig{
						Type: graphql.Int,
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleUnion(t *testing.T) {
	gql := `
union Hello = World
type World {}`

	world := graphql.NewObject(graphql.ObjectConfig{
		Name: "World",
	})
	expected := graphql.NewUnion(graphql.UnionConfig{
		Name:  "Hello",
		Types: []*graphql.Object{world},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Union("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestMultiUnion(t *testing.T) {
	gql := `
type Wor {}
type ld {}
union Hello = Wor | ld`

	wor := graphql.NewObject(graphql.ObjectConfig{
		Name: "Wor",
	})
	ld := graphql.NewObject(graphql.ObjectConfig{
		Name: "ld",
	})
	expected := graphql.NewUnion(graphql.UnionConfig{
		Name:  "Hello",
		Types: []*graphql.Object{wor, ld},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Union("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestScalar(t *testing.T) {
	gql := `scalar Hello`

	expected := graphql.NewScalar(graphql.ScalarConfig{
		Name: "Hello",
	})

	ctx, _ := Generate(gql)
	hello := ctx.Scalar("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleInputObject(t *testing.T) {
	gql := `
input Hello {
	world: String
}`

	expected := graphql.NewInputObject(graphql.InputObjectConfig{
		Name: "Hello",
		Fields: graphql.InputObjectConfigFieldMap{
			"world": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.InputObject("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleExtendType(t *testing.T) {
	gql := `
type Hello {
  test: Boolean
}
extend type Hello {
  world: String
}`
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"test": &graphql.Field{
				Type: graphql.Boolean,
			},
			"world": &graphql.Field{
				Type: graphql.String,
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}
<commit_msg>Restructure generator tests<commit_after>package generator

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
	"github.com/graphql-go/graphql"
	"reflect"
	"regexp"
	"strings"
	"testing"
)

func matchStrings(exp, got string) bool {
	trexp := strings.TrimSpace(exp)
	trgot := strings.TrimSpace(got)
	reg := regexp.MustCompile(`0x[0-9a-z]+`)
	trexp = reg.ReplaceAllString(trexp, "")
	trgot = reg.ReplaceAllString(trgot, "")
	return trexp == trgot
}

func nextLineMatch(exp, got []string, index1, index2 *int) bool {
	for i := *index1 + 1; i < len(exp); i++ {
		for p := *index2 + 1; p < len(got); p++ {
			if matchStrings(exp[i], got[p]) {
				*index1 = i
				*index2 = p
				return true
			}
		}
	}
	*index1, *index2 = -1, -1
	return false
}

func compareLines(str1, str2 string) {
	l1 := strings.Split(str1, "\n")
	l2 := strings.Split(str2, "\n")

	index1 := -1
	index2 := -1
	lastExp := -1
	lastGot := -1
	for nextLineMatch(l1, l2, &index1, &index2) {
		if index1-lastExp > 1 || index2-lastGot > 1 {
			// Unmatching lines in between!
			unmatched := l1[lastExp+1 : index1]
			unmatchedGot := l2[lastGot+1 : index2]
			fmt.Println("-----------------------------------------------")
			fmt.Println("Mismatch. Expected:")
			fmt.Println(strings.Join(unmatched, "\n"))
			fmt.Println("Got:")
			fmt.Println(strings.Join(unmatchedGot, "\n"))
			fmt.Println("-----------------------------------------------")
		}
		lastExp = index1
		lastGot = index2
	}

	if len(l1)-1 > lastExp {
		unmatched := l1[lastExp+1 : len(l1)-1]
		unmatchedGot := l2[lastGot+1 : len(l2)-1]
		fmt.Println("Mismatch. Expected:\n")
		fmt.Println(strings.Join(unmatched, "\n"))
		fmt.Println("\nGot:\n")
		fmt.Println(strings.Join(unmatchedGot, "\n"))
	}
}

func printFail(var1, var2 interface{}, t *testing.T) {
	t.Fail()
	spew.Config.DisableMethods = true
	fmt.Println("Unexpected schema found.\n")
	compareLines(spew.Sdump(var1), spew.Sdump(var2))
	//compareVars("", var1, var2)
}

func TestSimpleType(t *testing.T) {
	gql := `
type Oncle {
	pipe: ID
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: graphql.ID,
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestReferenceTypes(t *testing.T) {
	gql := `
type Oncle {
	pipe: p
}
type p {
	feld: e
}
interface e {}
	`

	e := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "e",
	})
	p := graphql.NewObject(graphql.ObjectConfig{
		Name: "p",
		Fields: graphql.Fields{
			"feld": &graphql.Field{
				Type: e,
			},
		},
	})
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: p,
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestRequiredType(t *testing.T) {
	gql := `
type Oncle {
	pipe: String!
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: graphql.NewNonNull(graphql.String),
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSimpleInterface(t *testing.T) {
	gql := `
interface World {}
`
	expected := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "World",
	})

	ctx, _ := Generate(gql)
	world := ctx.Interface("World")
	if !reflect.DeepEqual(world, expected) {
		printFail(expected, world, t)
	}
}

func TestSimpleTypeImplementsInterface(t *testing.T) {
	gql := `
interface World {}
type Oncle implements World {}
`
	world := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "World",
	})
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name:       "Oncle",
		Interfaces: []*graphql.Interface{world},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSimpleTypeImplementsMultipleInterfaces(t *testing.T) {
	gql := `
interface World {}
interface Balloon {}
type Oncle implements World, Balloon {}
`
	world := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "World",
	})
	balloon := graphql.NewInterface(graphql.InterfaceConfig{
		Name: "Balloon",
	})
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name:       "Oncle",
		Interfaces: []*graphql.Interface{world, balloon},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSingleValueEnum(t *testing.T) {
	gql := `enum Hello { WORLD }`

	expected := graphql.NewEnum(graphql.EnumConfig{
		Name: "Hello",
		Values: graphql.EnumValueConfigMap{
			"WORLD": &graphql.EnumValueConfig{
				Value: 0,
			},
		},
	})

	ctx, _ := Generate(gql)
	enum := ctx.Enums("Hello")
	if !reflect.DeepEqual(enum, expected) {
		printFail(expected, enum, t)
	}
}

func TestMultiValueEnum(t *testing.T) {
	gql := `enum Hello { WORLD, HERE }`

	expected := graphql.NewEnum(graphql.EnumConfig{
		Name: "Hello",
		Values: graphql.EnumValueConfigMap{
			"WORLD": &graphql.EnumValueConfig{
				Value: 0,
			},
			"HERE": &graphql.EnumValueConfig{
				Value: 1,
			},
		},
	})

	ctx, _ := Generate(gql)
	enum := ctx.Enums("Hello")
	if !reflect.DeepEqual(enum, expected) {
		printFail(expected, enum, t)
	}
}

func TestSimpleFieldWithArg(t *testing.T) {
	gql := `
type Hello {
	world(flag: Boolean): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"flag": &graphql.ArgumentConfig{
						Type: graphql.Boolean,
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleFieldWithArgDefaultValue(t *testing.T) {
	gql := `
type Hello {
	world(flag: Boolean! = true): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"flag": &graphql.ArgumentConfig{
						Type:         graphql.NewNonNull(graphql.Boolean),
						DefaultValue: true,
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleFieldWithListArg(t *testing.T) {
	gql := `
type Hello {
	world(things: [String]): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"things": &graphql.ArgumentConfig{
						Type: graphql.NewList(graphql.String),
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleFieldWithTwoArg(t *testing.T) {
	gql := `
type Hello {
	world(argOne: Boolean, argTwo: Int): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"world": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"argOne": &graphql.ArgumentConfig{
						Type: graphql.Boolean,
					},
					"argTwo": &graphql.ArgumentConfig{
						Type: graphql.Int,
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestWithArgumentAndComplexDefaultValueType(t *testing.T) {
	gql := `
type Oncle {
	pipe: ID
	five(argument: [String] = ["String", "String"] ): String
}
	`

	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Oncle",
		Fields: graphql.Fields{
			"pipe": &graphql.Field{
				Type: graphql.ID,
			},
			"five": &graphql.Field{
				Type: graphql.String,
				Args: graphql.FieldConfigArgument{
					"argument": &graphql.ArgumentConfig{
						Type:         graphql.NewList(graphql.String),
						DefaultValue: []interface{}{"String", "String"},
					},
				},
			},
		},
	})

	ctx, _ := Generate(gql)
	oncle := ctx.Object("Oncle")
	if !reflect.DeepEqual(oncle, expected) {
		printFail(expected, oncle, t)
	}
}

func TestSimpleUnion(t *testing.T) {
	gql := `
union Hello = World
type World {}`

	world := graphql.NewObject(graphql.ObjectConfig{
		Name: "World",
	})
	expected := graphql.NewUnion(graphql.UnionConfig{
		Name:  "Hello",
		Types: []*graphql.Object{world},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Union("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestMultiUnion(t *testing.T) {
	gql := `
type Wor {}
type ld {}
union Hello = Wor | ld`

	wor := graphql.NewObject(graphql.ObjectConfig{
		Name: "Wor",
	})
	ld := graphql.NewObject(graphql.ObjectConfig{
		Name: "ld",
	})
	expected := graphql.NewUnion(graphql.UnionConfig{
		Name:  "Hello",
		Types: []*graphql.Object{wor, ld},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Union("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestScalar(t *testing.T) {
	gql := `scalar Hello`

	expected := graphql.NewScalar(graphql.ScalarConfig{
		Name: "Hello",
	})

	ctx, _ := Generate(gql)
	hello := ctx.Scalar("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleInputObject(t *testing.T) {
	gql := `
input Hello {
	world: String
}`

	expected := graphql.NewInputObject(graphql.InputObjectConfig{
		Name: "Hello",
		Fields: graphql.InputObjectConfigFieldMap{
			"world": &graphql.InputObjectFieldConfig{
				Type: graphql.String,
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.InputObject("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}

func TestSimpleExtendType(t *testing.T) {
	gql := `
type Hello {
  test: Boolean
}
extend type Hello {
  world: String
}`
	expected := graphql.NewObject(graphql.ObjectConfig{
		Name: "Hello",
		Fields: graphql.Fields{
			"test": &graphql.Field{
				Type: graphql.Boolean,
			},
			"world": &graphql.Field{
				Type: graphql.String,
			},
		},
	})

	ctx, _ := Generate(gql)
	hello := ctx.Object("Hello")
	if !reflect.DeepEqual(hello, expected) {
		printFail(expected, hello, t)
	}
}
<|endoftext|>


<commit_before>package stringset

// Implement Set as a collection of unique string values.
//
// For Set.String, use '{' and '}', output elements as double-quoted strings
// safely escaped with Go syntax, and use a comma and a single space between
// elements. For example, a set with 2 elements, "a" and "b", should be formatted as {"a", "b"}.
// Format the empty set as {}.

// Define the Set type here.

func New() Set {
	panic("Please implement the New function")
}

func NewFromSlice(l []string) Set {
	panic("Please implement the NewFromSlice function")
}

func (s Set) String() string {
	panic("Please implement the String function")
}

func (s Set) IsEmpty() bool {
	panic("Please implement the IsEmpty function")
}

func (s Set) Has(elem string) bool {
	panic("Please implement the Has function")
}

func (s Set) Add(elem string) {
	panic("Please implement the Add function")
}

func Subset(s1, s2 Set) bool {
	panic("Please implement the Subset function")
}

func Disjoint(s1, s2 Set) bool {
	panic("Please implement the Disjoint function")
}

func Equal(s1, s2 Set) bool {
	panic("Please implement the Equal function")
}

func Intersection(s1, s2 Set) Set {
	panic("Please implement the Intersection function")
}

func Difference(s1, s2 Set) Set {
	panic("Please implement the Difference function")
}

func Union(s1, s2 Set) Set {
	panic("Please implement the Union function")
}
<commit_msg>Solve custom set<commit_after>package stringset

import (
	"fmt"
	"reflect"
	"strings"
)

// Implement Set as a collection of unique string values.
//
// For Set.String, use '{' and '}', output elements as double-quoted strings
// safely escaped with Go syntax, and use a comma and a single space between
// elements. For example, a set with 2 elements, "a" and "b", should be formatted as {"a", "b"}.
// Format the empty set as {}.

// Define the Set type here.
type Set struct {
	set map[string]bool
}

func New() Set {
	return Set{map[string]bool{}}
}

func NewFromSlice(slice []string) (result Set) {
	result = Set{map[string]bool{}}
	for _, element := range slice {
		result.Add(element)
	}
	return result
}

func (s Set) String() string {
	if s.set == nil {
		return "{}"
	}
	return fmt.Sprintf("{%s}", strings.Join(s.formattedKeys(), ", "))
}

func (s Set) IsEmpty() bool {
	return len(s.set) == 0
}

func (s Set) Has(elem string) bool {
	_, ok := s.set[elem]
	return ok
}

func (s Set) Add(elem string) {
	s.set[elem] = true
}

func Subset(s1, s2 Set) bool {
	for key, val := range s1.set {
		if val && !s2.Has(key) {
			return false
		}
	}
	return true
}

func Disjoint(s1, s2 Set) bool {
	for key, val := range s1.set {
		if val && s2.Has(key) {
			return false
		}
	}
	for key, val := range s2.set {
		if val && s1.Has(key) {
			return false
		}
	}
	return true
}

func Equal(s1, s2 Set) bool {
	return reflect.DeepEqual(s1.set, s2.set)
}

func Intersection(s1, s2 Set) (result Set) {
	result = New()
	for key, val := range s1.set {
		if val && s2.Has(key) {
			result.Add(key)
		}
	}
	return result
}

func Difference(s1, s2 Set) (result Set) {
	result = New()
	for key, val := range s1.set {
		if val && !s2.Has(key) {
			result.Add(key)
		}
	}
	return result
}

func Union(s1, s2 Set) (result Set) {
	result = New()
	for key := range s1.set {
		result.Add(key)
	}
	for key := range s2.set {
		result.Add(key)
	}
	return result
}

func (s Set) formattedKeys() (formatted []string) {
	formatted = []string{}
	for _, key := range s.keys() {
		quotted := fmt.Sprintf("\"%s\"", key)
		formatted = append(formatted, quotted)
	}
	return formatted
}

func (s Set) keys() (keys []string) {
	for key := range s.set {
		keys = append(keys, key)
	}
	return keys
}
<|endoftext|>


<commit_before>package engine

import (
	"os"
	"testing"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
)

func TestBootstrap(t *testing.T) {
	tc := SetupEngineTest(t, "login")
	defer tc.Cleanup()

	u1 := CreateAndSignupFakeUser(tc, "login")
	Logout(tc)
	u1.LoginOrBust(tc)

	// do a upak load to make sure it is cached
	arg := libkb.NewLoadUserByUIDArg(context.TODO(), tc.G, u1.UID())
	tc.G.GetUPAKLoader().Load(arg)

	// get the status values
	uid := tc.G.Env.GetUID()
	username := tc.G.Env.GetUsername()
	deviceID := tc.G.Env.GetDeviceID()

	// Simulate restarting the service by wiping out the
	// passphrase stream cache and cached secret keys
	tc.G.LoginState().Account(func(a *libkb.Account) {
		a.ClearStreamCache()
		a.ClearCachedSecretKeys()
	}, "account - clear")
	tc.G.GetUPAKLoader().ClearMemory()

	// set server uri to nonexistent ip so api calls will fail
	prev := os.Getenv("KEYBASE_SERVER_URI")
	os.Setenv("KEYBASE_SERVER_URI", "http://127.0.0.127:3333")
	defer os.Setenv("KEYBASE_SERVER_URI", prev)
	tc.G.ConfigureAPI()
	tc.G.ConnectivityMonitor = OfflineConnectivityMonitor{}

	eng := NewLoginOffline(tc.G)
	ctx := &Context{NetContext: context.Background()}
	if err := RunEngine(eng, ctx); err != nil {
		t.Fatal(err)
	}

	beng := NewBootstrap(tc.G)
	bctx := &Context{NetContext: context.Background()}
	if err := RunEngine(beng, bctx); err != nil {
		t.Fatal(err)
	}
	status := beng.Status()

	if !status.Registered {
		t.Error("registered false")
	}
	if !status.LoggedIn {
		t.Error("not logged in")
	}
	if status.Uid.IsNil() {
		t.Errorf("uid nil")
	}
	if !status.Uid.Equal(uid) {
		t.Errorf("uid: %s, expected %s", status.Uid, uid)
	}
	if status.Username == "" {
		t.Errorf("username empty")
	}
	if status.Username != username.String() {
		t.Errorf("username: %q, expected %q", status.Username, username)
	}
	if !status.DeviceID.Eq(deviceID) {
		t.Errorf("device id: %q, expected %q", status.DeviceID, deviceID)
	}
	if status.DeviceName != defaultDeviceName {
		t.Errorf("device name: %q, expected %q", status.DeviceName, defaultDeviceName)
	}
}

type OfflineConnectivityMonitor struct {
}

func (s OfflineConnectivityMonitor) IsConnected(ctx context.Context) libkb.ConnectivityMonitorResult {
	return libkb.ConnectivityMonitorNo
}

var _ libkb.ConnectivityMonitor = OfflineConnectivityMonitor{}
<commit_msg>Add test for device name after signup<commit_after>package engine

import (
	"os"
	"testing"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
)

func TestBootstrap(t *testing.T) {
	tc := SetupEngineTest(t, "login")
	defer tc.Cleanup()

	u1 := CreateAndSignupFakeUser(tc, "login")
	Logout(tc)
	u1.LoginOrBust(tc)

	// do a upak load to make sure it is cached
	arg := libkb.NewLoadUserByUIDArg(context.TODO(), tc.G, u1.UID())
	tc.G.GetUPAKLoader().Load(arg)

	// get the status values
	uid := tc.G.Env.GetUID()
	username := tc.G.Env.GetUsername()
	deviceID := tc.G.Env.GetDeviceID()

	// Simulate restarting the service by wiping out the
	// passphrase stream cache and cached secret keys
	tc.G.LoginState().Account(func(a *libkb.Account) {
		a.ClearStreamCache()
		a.ClearCachedSecretKeys()
	}, "account - clear")
	tc.G.GetUPAKLoader().ClearMemory()

	// set server uri to nonexistent ip so api calls will fail
	prev := os.Getenv("KEYBASE_SERVER_URI")
	os.Setenv("KEYBASE_SERVER_URI", "http://127.0.0.127:3333")
	defer os.Setenv("KEYBASE_SERVER_URI", prev)
	tc.G.ConfigureAPI()
	tc.G.ConnectivityMonitor = OfflineConnectivityMonitor{}

	eng := NewLoginOffline(tc.G)
	ctx := &Context{NetContext: context.Background()}
	if err := RunEngine(eng, ctx); err != nil {
		t.Fatal(err)
	}

	beng := NewBootstrap(tc.G)
	bctx := &Context{NetContext: context.Background()}
	if err := RunEngine(beng, bctx); err != nil {
		t.Fatal(err)
	}
	status := beng.Status()

	if !status.Registered {
		t.Error("registered false")
	}
	if !status.LoggedIn {
		t.Error("not logged in")
	}
	if status.Uid.IsNil() {
		t.Errorf("uid nil")
	}
	if !status.Uid.Equal(uid) {
		t.Errorf("uid: %s, expected %s", status.Uid, uid)
	}
	if status.Username == "" {
		t.Errorf("username empty")
	}
	if status.Username != username.String() {
		t.Errorf("username: %q, expected %q", status.Username, username)
	}
	if !status.DeviceID.Eq(deviceID) {
		t.Errorf("device id: %q, expected %q", status.DeviceID, deviceID)
	}
	if status.DeviceName != defaultDeviceName {
		t.Errorf("device name: %q, expected %q", status.DeviceName, defaultDeviceName)
	}
}

func TestBootstrapAfterSignup(t *testing.T) {
	tc := SetupEngineTest(t, "login")
	defer tc.Cleanup()

	u1 := CreateAndSignupFakeUser(tc, "login")

	beng := NewBootstrap(tc.G)
	bctx := &Context{NetContext: context.Background()}
	if err := RunEngine(beng, bctx); err != nil {
		t.Fatal(err)
	}
	status := beng.Status()

	if !status.Registered {
		t.Error("registered false")
	}
	if !status.LoggedIn {
		t.Error("not logged in")
	}
	if status.Uid.IsNil() {
		t.Errorf("uid nil")
	}
	/*
		if !status.Uid.Equal(u1.Uid) {
			t.Errorf("uid: %s, expected %s", status.Uid, u1.uid)
		}
	*/
	if status.Username == "" {
		t.Errorf("username empty")
	}
	if status.Username != u1.Username {
		t.Errorf("username: %q, expected %q", status.Username, u1.Username)
	}
	/*
		if !status.DeviceID.Eq(deviceID) {
			t.Errorf("device id: %q, expected %q", status.DeviceID, deviceID)
		}
	*/
	if status.DeviceName != defaultDeviceName {
		t.Errorf("device name: %q, expected %q", status.DeviceName, defaultDeviceName)
	}
}

type OfflineConnectivityMonitor struct {
}

func (s OfflineConnectivityMonitor) IsConnected(ctx context.Context) libkb.ConnectivityMonitorResult {
	return libkb.ConnectivityMonitorNo
}

var _ libkb.ConnectivityMonitor = OfflineConnectivityMonitor{}
<|endoftext|>


<commit_before>package teams

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

func GetTeamShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string) (ret keybase1.TeamShowcase, err error) {
	team, err := Load(ctx, g, keybase1.LoadTeamArg{Name: teamname})
	if err != nil {
		return ret, fixupTeamGetError(ctx, g, err, teamname, false)
	}
	if team.IsImplicit() {
		return ret, fmt.Errorf("cannot manage implicit team by name")
	}
	return GetTeamShowcaseByID(ctx, g, team.ID)
}

func GetTeamShowcaseByID(ctx context.Context, g *libkb.GlobalContext, teamID keybase1.TeamID) (ret keybase1.TeamShowcase, err error) {
	arg := apiArg("team/get")
	arg.Args.Add("id", libkb.S{Val: teamID.String()})

	var rt rawTeam
	mctx := libkb.NewMetaContext(ctx, g)
	if err := mctx.G().API.GetDecode(mctx, arg, &rt); err != nil {
		return ret, err
	}
	return rt.Showcase, nil
}

type memberShowcaseRes struct {
	Status      libkb.AppStatus `json:"status"`
	IsShowcased bool            `json:"is_showcased"`
}

func (c *memberShowcaseRes) GetAppStatus() *libkb.AppStatus {
	return &c.Status
}

func GetTeamAndMemberShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string) (ret keybase1.TeamAndMemberShowcase, err error) {
	t, err := GetForDisplayByStringName(ctx, g, teamname)
	if err != nil {
		return ret, err
	}

	role, err := t.myRole(ctx)
	if err != nil {
		return ret, err
	}

	arg := apiArg("team/get")
	arg.Args.Add("id", libkb.S{Val: t.ID.String()})
	arg.Args.Add("showcase_only", libkb.B{Val: true})

	var teamRet rawTeam
	mctx := libkb.NewMetaContext(ctx, g)
	if err := mctx.G().API.GetDecode(mctx, arg, &teamRet); err != nil {
		return ret, err
	}
	ret.TeamShowcase = teamRet.Showcase

	// team/member_showcase endpoint is available either for
	// admins/owners, or for everyone if AnyMemberShowcase is set,
	// but this does not include implicit admins.
	if (teamRet.Showcase.AnyMemberShowcase && role != keybase1.TeamRole_NONE) || role.IsOrAbove(keybase1.TeamRole_ADMIN) {
		arg = apiArg("team/member_showcase")
		arg.Args.Add("tid", libkb.S{Val: t.ID.String()})

		var memberRet memberShowcaseRes
		if err := mctx.G().API.GetDecode(mctx, arg, &memberRet); err != nil {
			if appErr, ok := err.(libkb.AppStatusError); ok &&
				appErr.Code == int(keybase1.StatusCode_SCTeamShowcasePermDenied) {
				// It is possible that we were still a member when
				// GetForTeamManagement* was called, but we are not a member
				// anymore, so `team/member_showcase` fails. Note that this
				// endpoint does not work for implicit admins, hence the role
				// checks before calling it - but if we have outdated team
				// information, we might still end up here not being allowed
				// to call it and getting this error.
				mctx.Debug("GetTeamAndMemberShowcase hit a race with team %q", teamname)
				return ret, nil
			}

			return ret, err
		}

		ret.IsMemberShowcased = memberRet.IsShowcased
	}

	return ret, nil
}

func SetTeamShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string, isShowcased *bool, description *string, anyMemberShowcase *bool) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname, true)
	if err != nil {
		return err
	}

	if isShowcased == nil && description == nil && anyMemberShowcase == nil {
		return errors.New("at least one argument has to be non-nil")
	}

	arg := apiArg("team/team_showcase")
	arg.Args.Add("tid", libkb.S{Val: string(t.ID)})
	if isShowcased != nil {
		arg.Args.Add("is_showcased", libkb.B{Val: *isShowcased})
	}
	if description != nil {
		if len(*description) > 0 {
			arg.Args.Add("description", libkb.S{Val: *description})
		} else {
			arg.Args.Add("clear_description", libkb.B{Val: true})
		}
	}
	if anyMemberShowcase != nil {
		arg.Args.Add("any_member_showcase", libkb.B{Val: *anyMemberShowcase})
	}
	mctx := libkb.NewMetaContext(ctx, g)
	if _, err := mctx.G().API.Post(mctx, arg); err != nil {
		return err
	}
	return t.notifyNoChainChange(ctx, keybase1.TeamChangeSet{Misc: true})
}

func SetTeamMemberShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string, isShowcased bool) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname, false)
	if err != nil {
		return err
	}

	mctx := libkb.NewMetaContext(ctx, g)
	arg := apiArg("team/member_showcase")
	arg.Args.Add("tid", libkb.S{Val: string(t.ID)})
	arg.Args.Add("is_showcased", libkb.B{Val: isShowcased})
	_, err = mctx.G().API.Post(mctx, arg)
	if err != nil {
		return err
	}

	// Clear usercard cache so when user goes to People tab,
	// fresh card will be loaded.
	u := g.ActiveDevice.UID()
	mctx.Debug("Clearing Card cache for %s", u)
	if err := g.CardCache().Delete(u); err != nil {
		mctx.Debug("Error in CardCache.Delete: %s", err)
	}
	g.UserChanged(ctx, u)
	return nil
}
<commit_msg>Set `showcase_only` in GetTeamShowcaseByID (#21387)<commit_after>package teams

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

func GetTeamShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string) (ret keybase1.TeamShowcase, err error) {
	team, err := Load(ctx, g, keybase1.LoadTeamArg{Name: teamname})
	if err != nil {
		return ret, fixupTeamGetError(ctx, g, err, teamname, false)
	}
	if team.IsImplicit() {
		return ret, fmt.Errorf("cannot manage implicit team by name")
	}
	return GetTeamShowcaseByID(ctx, g, team.ID)
}

func GetTeamShowcaseByID(ctx context.Context, g *libkb.GlobalContext, teamID keybase1.TeamID) (ret keybase1.TeamShowcase, err error) {
	arg := apiArg("team/get")
	arg.Args.Add("id", libkb.S{Val: teamID.String()})
	arg.Args.Add("showcase_only", libkb.B{Val: true})

	var rt rawTeam
	mctx := libkb.NewMetaContext(ctx, g)
	if err := mctx.G().API.GetDecode(mctx, arg, &rt); err != nil {
		return ret, err
	}
	return rt.Showcase, nil
}

type memberShowcaseRes struct {
	Status      libkb.AppStatus `json:"status"`
	IsShowcased bool            `json:"is_showcased"`
}

func (c *memberShowcaseRes) GetAppStatus() *libkb.AppStatus {
	return &c.Status
}

func GetTeamAndMemberShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string) (ret keybase1.TeamAndMemberShowcase, err error) {
	t, err := GetForDisplayByStringName(ctx, g, teamname)
	if err != nil {
		return ret, err
	}

	role, err := t.myRole(ctx)
	if err != nil {
		return ret, err
	}

	arg := apiArg("team/get")
	arg.Args.Add("id", libkb.S{Val: t.ID.String()})
	arg.Args.Add("showcase_only", libkb.B{Val: true})

	var teamRet rawTeam
	mctx := libkb.NewMetaContext(ctx, g)
	if err := mctx.G().API.GetDecode(mctx, arg, &teamRet); err != nil {
		return ret, err
	}
	ret.TeamShowcase = teamRet.Showcase

	// team/member_showcase endpoint is available either for
	// admins/owners, or for everyone if AnyMemberShowcase is set,
	// but this does not include implicit admins.
	if (teamRet.Showcase.AnyMemberShowcase && role != keybase1.TeamRole_NONE) || role.IsOrAbove(keybase1.TeamRole_ADMIN) {
		arg = apiArg("team/member_showcase")
		arg.Args.Add("tid", libkb.S{Val: t.ID.String()})

		var memberRet memberShowcaseRes
		if err := mctx.G().API.GetDecode(mctx, arg, &memberRet); err != nil {
			if appErr, ok := err.(libkb.AppStatusError); ok &&
				appErr.Code == int(keybase1.StatusCode_SCTeamShowcasePermDenied) {
				// It is possible that we were still a member when
				// GetForTeamManagement* was called, but we are not a member
				// anymore, so `team/member_showcase` fails. Note that this
				// endpoint does not work for implicit admins, hence the role
				// checks before calling it - but if we have outdated team
				// information, we might still end up here not being allowed
				// to call it and getting this error.
				mctx.Debug("GetTeamAndMemberShowcase hit a race with team %q", teamname)
				return ret, nil
			}

			return ret, err
		}

		ret.IsMemberShowcased = memberRet.IsShowcased
	}

	return ret, nil
}

func SetTeamShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string, isShowcased *bool, description *string, anyMemberShowcase *bool) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname, true)
	if err != nil {
		return err
	}

	if isShowcased == nil && description == nil && anyMemberShowcase == nil {
		return errors.New("at least one argument has to be non-nil")
	}

	arg := apiArg("team/team_showcase")
	arg.Args.Add("tid", libkb.S{Val: string(t.ID)})
	if isShowcased != nil {
		arg.Args.Add("is_showcased", libkb.B{Val: *isShowcased})
	}
	if description != nil {
		if len(*description) > 0 {
			arg.Args.Add("description", libkb.S{Val: *description})
		} else {
			arg.Args.Add("clear_description", libkb.B{Val: true})
		}
	}
	if anyMemberShowcase != nil {
		arg.Args.Add("any_member_showcase", libkb.B{Val: *anyMemberShowcase})
	}
	mctx := libkb.NewMetaContext(ctx, g)
	if _, err := mctx.G().API.Post(mctx, arg); err != nil {
		return err
	}
	return t.notifyNoChainChange(ctx, keybase1.TeamChangeSet{Misc: true})
}

func SetTeamMemberShowcase(ctx context.Context, g *libkb.GlobalContext, teamname string, isShowcased bool) error {
	t, err := GetForTeamManagementByStringName(ctx, g, teamname, false)
	if err != nil {
		return err
	}

	mctx := libkb.NewMetaContext(ctx, g)
	arg := apiArg("team/member_showcase")
	arg.Args.Add("tid", libkb.S{Val: string(t.ID)})
	arg.Args.Add("is_showcased", libkb.B{Val: isShowcased})
	_, err = mctx.G().API.Post(mctx, arg)
	if err != nil {
		return err
	}

	// Clear usercard cache so when user goes to People tab,
	// fresh card will be loaded.
	u := g.ActiveDevice.UID()
	mctx.Debug("Clearing Card cache for %s", u)
	if err := g.CardCache().Delete(u); err != nil {
		mctx.Debug("Error in CardCache.Delete: %s", err)
	}
	g.UserChanged(ctx, u)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

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 wrangler

import (
	"context"
	"fmt"
	"sync"
	"time"

	"vitess.io/vitess/go/vt/log"

	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"

	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/vt/binlog/binlogplayer"
	"vitess.io/vitess/go/vt/concurrency"
	"vitess.io/vitess/go/vt/key"
	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/topotools"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vtgate/vindexes"
	"vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication"
)

type resharder struct {
	wr            *Wrangler
	keyspace      string
	workflow      string
	sourceShards  []*topo.ShardInfo
	sourceMasters map[string]*topo.TabletInfo
	targetShards  []*topo.ShardInfo
	targetMasters map[string]*topo.TabletInfo
	vschema       *vschemapb.Keyspace
	refStreams    map[string]*refStream
	cell          string //single cell or cellsAlias or comma-separated list of cells/cellsAliases
	tabletTypes   string
	stopAfterCopy bool
}

type refStream struct {
	workflow    string
	bls         *binlogdatapb.BinlogSource
	cell        string
	tabletTypes string
}

// Reshard initiates a resharding workflow.
func (wr *Wrangler) Reshard(ctx context.Context, keyspace, workflow string, sources, targets []string,
	skipSchemaCopy bool, cell, tabletTypes string, autoStart, stopAfterCopy bool) error {
	if err := wr.validateNewWorkflow(ctx, keyspace, workflow); err != nil {
		return err
	}
	if err := wr.ts.ValidateSrvKeyspace(ctx, keyspace, cell); err != nil {
		err2 := vterrors.Wrapf(err, "SrvKeyspace for %s is corrupt in cell %s", keyspace, cell)
		log.Errorf("%w", err2)
		return err2
	}

	rs, err := wr.buildResharder(ctx, keyspace, workflow, sources, targets, cell, tabletTypes)
	if err != nil {
		return vterrors.Wrap(err, "buildResharder")
	}
	rs.stopAfterCopy = stopAfterCopy
	if !skipSchemaCopy {
		if err := rs.copySchema(ctx); err != nil {
			return vterrors.Wrap(err, "copySchema")
		}
	}
	if err := rs.createStreams(ctx); err != nil {
		return vterrors.Wrap(err, "createStreams")
	}

	if autoStart {
		if err := rs.startStreams(ctx); err != nil {
			return vterrors.Wrap(err, "startStreams")
		}
	} else {
		wr.Logger().Infof("Streams will not be started since -auto_start is set to false")
	}
	return nil
}

func (wr *Wrangler) buildResharder(ctx context.Context, keyspace, workflow string, sources, targets []string, cell, tabletTypes string) (*resharder, error) {
	rs := &resharder{
		wr:            wr,
		keyspace:      keyspace,
		workflow:      workflow,
		sourceMasters: make(map[string]*topo.TabletInfo),
		targetMasters: make(map[string]*topo.TabletInfo),
		cell:          cell,
		tabletTypes:   tabletTypes,
	}
	for _, shard := range sources {
		si, err := wr.ts.GetShard(ctx, keyspace, shard)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetShard(%s) failed", shard)
		}
		if !si.IsMasterServing {
			return nil, fmt.Errorf("source shard %v is not in serving state", shard)
		}
		rs.sourceShards = append(rs.sourceShards, si)
		master, err := wr.ts.GetTablet(ctx, si.MasterAlias)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetTablet(%s) failed", si.MasterAlias)
		}
		rs.sourceMasters[si.ShardName()] = master
	}
	for _, shard := range targets {
		si, err := wr.ts.GetShard(ctx, keyspace, shard)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetShard(%s) failed", shard)
		}
		if si.IsMasterServing {
			return nil, fmt.Errorf("target shard %v is in serving state", shard)
		}
		rs.targetShards = append(rs.targetShards, si)
		master, err := wr.ts.GetTablet(ctx, si.MasterAlias)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetTablet(%s) failed", si.MasterAlias)
		}
		rs.targetMasters[si.ShardName()] = master
	}
	if err := topotools.ValidateForReshard(rs.sourceShards, rs.targetShards); err != nil {
		return nil, vterrors.Wrap(err, "ValidateForReshard")
	}
	if err := rs.validateTargets(ctx); err != nil {
		return nil, vterrors.Wrap(err, "validateTargets")
	}

	vschema, err := wr.ts.GetVSchema(ctx, keyspace)
	if err != nil {
		return nil, vterrors.Wrap(err, "GetVSchema")
	}
	rs.vschema = vschema

	if err := rs.readRefStreams(ctx); err != nil {
		return nil, vterrors.Wrap(err, "readRefStreams")
	}
	return rs, nil
}

func (rs *resharder) validateTargets(ctx context.Context) error {
	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		targetMaster := rs.targetMasters[target.ShardName()]
		query := fmt.Sprintf("select 1 from _vt.vreplication where db_name=%s", encodeString(targetMaster.DbName()))
		p3qr, err := rs.wr.tmc.VReplicationExec(ctx, targetMaster.Tablet, query)
		if err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetMaster.Tablet, query)
		}
		if len(p3qr.Rows) != 0 {
			return errors.New("some streams already exist in the target shards, please clean them up and retry the command")
		}
		return nil
	})
	return err
}

func (rs *resharder) readRefStreams(ctx context.Context) error {
	var mu sync.Mutex
	err := rs.forAll(rs.sourceShards, func(source *topo.ShardInfo) error {
		sourceMaster := rs.sourceMasters[source.ShardName()]

		query := fmt.Sprintf("select workflow, source, cell, tablet_types from _vt.vreplication where db_name=%s and message != 'FROZEN'", encodeString(sourceMaster.DbName()))
		p3qr, err := rs.wr.tmc.VReplicationExec(ctx, sourceMaster.Tablet, query)
		if err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", sourceMaster.Tablet, query)
		}
		qr := sqltypes.Proto3ToResult(p3qr)

		mu.Lock()
		defer mu.Unlock()

		mustCreate := false
		var ref map[string]bool
		if rs.refStreams == nil {
			rs.refStreams = make(map[string]*refStream)
			mustCreate = true
		} else {
			// Copy the ref streams for comparison.
			ref = make(map[string]bool, len(rs.refStreams))
			for k := range rs.refStreams {
				ref[k] = true
			}
		}
		for _, row := range qr.Rows {

			workflow := row[0].ToString()
			if workflow == "" {
				return fmt.Errorf("VReplication streams must have named workflows for migration: shard: %s:%s", source.Keyspace(), source.ShardName())
			}
			var bls binlogdatapb.BinlogSource
			if err := proto.UnmarshalText(row[1].ToString(), &bls); err != nil {
				return vterrors.Wrapf(err, "UnmarshalText: %v", row)
			}
			isReference, err := rs.blsIsReference(&bls)
			if err != nil {
				return vterrors.Wrap(err, "blsIsReference")
			}
			if !isReference {
				continue
			}
			key := fmt.Sprintf("%s:%s:%s", workflow, bls.Keyspace, bls.Shard)
			if mustCreate {
				rs.refStreams[key] = &refStream{
					workflow:    workflow,
					bls:         &bls,
					cell:        row[2].ToString(),
					tabletTypes: row[3].ToString(),
				}
			} else {
				if !ref[key] {
					return fmt.Errorf("streams are mismatched across source shards for workflow: %s", workflow)
				}
				delete(ref, key)
			}
		}
		if len(ref) != 0 {
			return fmt.Errorf("streams are mismatched across source shards: %v", ref)
		}
		return nil
	})
	return err
}

// blsIsReference is partially copied from streamMigrater.templatize.
// It reuses the constants from that function also.
func (rs *resharder) blsIsReference(bls *binlogdatapb.BinlogSource) (bool, error) {
	streamType := unknown
	for _, rule := range bls.Filter.Rules {
		typ, err := rs.identifyRuleType(rule)
		if err != nil {
			return false, err
		}
		switch typ {
		case sharded:
			if streamType == reference {
				return false, fmt.Errorf("cannot reshard streams with a mix of reference and sharded tables: %v", bls)
			}
			streamType = sharded
		case reference:
			if streamType == sharded {
				return false, fmt.Errorf("cannot reshard streams with a mix of reference and sharded tables: %v", bls)
			}
			streamType = reference
		}
	}
	return streamType == reference, nil
}

func (rs *resharder) identifyRuleType(rule *binlogdatapb.Rule) (int, error) {
	vtable, ok := rs.vschema.Tables[rule.Match]
	if !ok {
		return 0, fmt.Errorf("table %v not found in vschema", rule.Match)
	}
	if vtable.Type == vindexes.TypeReference {
		return reference, nil
	}
	// In this case, 'sharded' means that it's not a reference
	// table. We don't care about any other subtleties.
	return sharded, nil
}

func (rs *resharder) copySchema(ctx context.Context) error {
	oneSource := rs.sourceShards[0].MasterAlias
	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		return rs.wr.CopySchemaShard(ctx, oneSource, []string{"/.*"}, nil, false, rs.keyspace, target.ShardName(), 1*time.Second, false)
	})
	return err
}

func (rs *resharder) createStreams(ctx context.Context) error {
	var excludeRules []*binlogdatapb.Rule
	for tableName, table := range rs.vschema.Tables {
		if table.Type == vindexes.TypeReference {
			excludeRules = append(excludeRules, &binlogdatapb.Rule{
				Match:  tableName,
				Filter: "exclude",
			})
		}
	}

	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		targetMaster := rs.targetMasters[target.ShardName()]

		ig := vreplication.NewInsertGenerator(binlogplayer.BlpStopped, targetMaster.DbName())

		// copy excludeRules to prevent data race.
		copyExcludeRules := append([]*binlogdatapb.Rule(nil), excludeRules...)
		for _, source := range rs.sourceShards {
			if !key.KeyRangesIntersect(target.KeyRange, source.KeyRange) {
				continue
			}
			filter := &binlogdatapb.Filter{
				Rules: append(copyExcludeRules, &binlogdatapb.Rule{
					Match:  "/.*",
					Filter: key.KeyRangeString(target.KeyRange),
				}),
			}
			bls := &binlogdatapb.BinlogSource{
				Keyspace:      rs.keyspace,
				Shard:         source.ShardName(),
				Filter:        filter,
				StopAfterCopy: rs.stopAfterCopy,
			}
			ig.AddRow(rs.workflow, bls, "", rs.cell, rs.tabletTypes)
		}

		for _, rstream := range rs.refStreams {
			ig.AddRow(rstream.workflow, rstream.bls, "", rstream.cell, rstream.tabletTypes)
		}
		query := ig.String()
		if _, err := rs.wr.tmc.VReplicationExec(ctx, targetMaster.Tablet, query); err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetMaster.Tablet, query)
		}
		return nil
	})

	return err
}

func (rs *resharder) startStreams(ctx context.Context) error {
	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		targetMaster := rs.targetMasters[target.ShardName()]
		query := fmt.Sprintf("update _vt.vreplication set state='Running' where db_name=%s", encodeString(targetMaster.DbName()))
		if _, err := rs.wr.tmc.VReplicationExec(ctx, targetMaster.Tablet, query); err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetMaster.Tablet, query)
		}
		return nil
	})
	return err
}

func (rs *resharder) forAll(shards []*topo.ShardInfo, f func(*topo.ShardInfo) error) error {
	var wg sync.WaitGroup
	allErrors := &concurrency.AllErrorRecorder{}
	for _, shard := range shards {
		wg.Add(1)
		go func(shard *topo.ShardInfo) {
			defer wg.Done()

			if err := f(shard); err != nil {
				allErrors.RecordError(err)
			}
		}(shard)
	}
	wg.Wait()
	return allErrors.AggrError(vterrors.Aggregate)
}
<commit_msg>Address review comment<commit_after>/*
Copyright 2019 The Vitess Authors.

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 wrangler

import (
	"context"
	"fmt"
	"sync"
	"time"

	"vitess.io/vitess/go/vt/log"

	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"

	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/vt/binlog/binlogplayer"
	"vitess.io/vitess/go/vt/concurrency"
	"vitess.io/vitess/go/vt/key"
	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
	"vitess.io/vitess/go/vt/topo"
	"vitess.io/vitess/go/vt/topotools"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vtgate/vindexes"
	"vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication"
)

type resharder struct {
	wr            *Wrangler
	keyspace      string
	workflow      string
	sourceShards  []*topo.ShardInfo
	sourceMasters map[string]*topo.TabletInfo
	targetShards  []*topo.ShardInfo
	targetMasters map[string]*topo.TabletInfo
	vschema       *vschemapb.Keyspace
	refStreams    map[string]*refStream
	cell          string //single cell or cellsAlias or comma-separated list of cells/cellsAliases
	tabletTypes   string
	stopAfterCopy bool
}

type refStream struct {
	workflow    string
	bls         *binlogdatapb.BinlogSource
	cell        string
	tabletTypes string
}

// Reshard initiates a resharding workflow.
func (wr *Wrangler) Reshard(ctx context.Context, keyspace, workflow string, sources, targets []string,
	skipSchemaCopy bool, cell, tabletTypes string, autoStart, stopAfterCopy bool) error {
	if err := wr.validateNewWorkflow(ctx, keyspace, workflow); err != nil {
		return err
	}
	if err := wr.ts.ValidateSrvKeyspace(ctx, keyspace, cell); err != nil {
		err2 := vterrors.Wrapf(err, "SrvKeyspace for keyspace %s is corrupt in cell %s", keyspace, cell)
		log.Errorf("%w", err2)
		return err2
	}

	rs, err := wr.buildResharder(ctx, keyspace, workflow, sources, targets, cell, tabletTypes)
	if err != nil {
		return vterrors.Wrap(err, "buildResharder")
	}
	rs.stopAfterCopy = stopAfterCopy
	if !skipSchemaCopy {
		if err := rs.copySchema(ctx); err != nil {
			return vterrors.Wrap(err, "copySchema")
		}
	}
	if err := rs.createStreams(ctx); err != nil {
		return vterrors.Wrap(err, "createStreams")
	}

	if autoStart {
		if err := rs.startStreams(ctx); err != nil {
			return vterrors.Wrap(err, "startStreams")
		}
	} else {
		wr.Logger().Infof("Streams will not be started since -auto_start is set to false")
	}
	return nil
}

func (wr *Wrangler) buildResharder(ctx context.Context, keyspace, workflow string, sources, targets []string, cell, tabletTypes string) (*resharder, error) {
	rs := &resharder{
		wr:            wr,
		keyspace:      keyspace,
		workflow:      workflow,
		sourceMasters: make(map[string]*topo.TabletInfo),
		targetMasters: make(map[string]*topo.TabletInfo),
		cell:          cell,
		tabletTypes:   tabletTypes,
	}
	for _, shard := range sources {
		si, err := wr.ts.GetShard(ctx, keyspace, shard)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetShard(%s) failed", shard)
		}
		if !si.IsMasterServing {
			return nil, fmt.Errorf("source shard %v is not in serving state", shard)
		}
		rs.sourceShards = append(rs.sourceShards, si)
		master, err := wr.ts.GetTablet(ctx, si.MasterAlias)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetTablet(%s) failed", si.MasterAlias)
		}
		rs.sourceMasters[si.ShardName()] = master
	}
	for _, shard := range targets {
		si, err := wr.ts.GetShard(ctx, keyspace, shard)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetShard(%s) failed", shard)
		}
		if si.IsMasterServing {
			return nil, fmt.Errorf("target shard %v is in serving state", shard)
		}
		rs.targetShards = append(rs.targetShards, si)
		master, err := wr.ts.GetTablet(ctx, si.MasterAlias)
		if err != nil {
			return nil, vterrors.Wrapf(err, "GetTablet(%s) failed", si.MasterAlias)
		}
		rs.targetMasters[si.ShardName()] = master
	}
	if err := topotools.ValidateForReshard(rs.sourceShards, rs.targetShards); err != nil {
		return nil, vterrors.Wrap(err, "ValidateForReshard")
	}
	if err := rs.validateTargets(ctx); err != nil {
		return nil, vterrors.Wrap(err, "validateTargets")
	}

	vschema, err := wr.ts.GetVSchema(ctx, keyspace)
	if err != nil {
		return nil, vterrors.Wrap(err, "GetVSchema")
	}
	rs.vschema = vschema

	if err := rs.readRefStreams(ctx); err != nil {
		return nil, vterrors.Wrap(err, "readRefStreams")
	}
	return rs, nil
}

func (rs *resharder) validateTargets(ctx context.Context) error {
	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		targetMaster := rs.targetMasters[target.ShardName()]
		query := fmt.Sprintf("select 1 from _vt.vreplication where db_name=%s", encodeString(targetMaster.DbName()))
		p3qr, err := rs.wr.tmc.VReplicationExec(ctx, targetMaster.Tablet, query)
		if err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetMaster.Tablet, query)
		}
		if len(p3qr.Rows) != 0 {
			return errors.New("some streams already exist in the target shards, please clean them up and retry the command")
		}
		return nil
	})
	return err
}

func (rs *resharder) readRefStreams(ctx context.Context) error {
	var mu sync.Mutex
	err := rs.forAll(rs.sourceShards, func(source *topo.ShardInfo) error {
		sourceMaster := rs.sourceMasters[source.ShardName()]

		query := fmt.Sprintf("select workflow, source, cell, tablet_types from _vt.vreplication where db_name=%s and message != 'FROZEN'", encodeString(sourceMaster.DbName()))
		p3qr, err := rs.wr.tmc.VReplicationExec(ctx, sourceMaster.Tablet, query)
		if err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", sourceMaster.Tablet, query)
		}
		qr := sqltypes.Proto3ToResult(p3qr)

		mu.Lock()
		defer mu.Unlock()

		mustCreate := false
		var ref map[string]bool
		if rs.refStreams == nil {
			rs.refStreams = make(map[string]*refStream)
			mustCreate = true
		} else {
			// Copy the ref streams for comparison.
			ref = make(map[string]bool, len(rs.refStreams))
			for k := range rs.refStreams {
				ref[k] = true
			}
		}
		for _, row := range qr.Rows {

			workflow := row[0].ToString()
			if workflow == "" {
				return fmt.Errorf("VReplication streams must have named workflows for migration: shard: %s:%s", source.Keyspace(), source.ShardName())
			}
			var bls binlogdatapb.BinlogSource
			if err := proto.UnmarshalText(row[1].ToString(), &bls); err != nil {
				return vterrors.Wrapf(err, "UnmarshalText: %v", row)
			}
			isReference, err := rs.blsIsReference(&bls)
			if err != nil {
				return vterrors.Wrap(err, "blsIsReference")
			}
			if !isReference {
				continue
			}
			key := fmt.Sprintf("%s:%s:%s", workflow, bls.Keyspace, bls.Shard)
			if mustCreate {
				rs.refStreams[key] = &refStream{
					workflow:    workflow,
					bls:         &bls,
					cell:        row[2].ToString(),
					tabletTypes: row[3].ToString(),
				}
			} else {
				if !ref[key] {
					return fmt.Errorf("streams are mismatched across source shards for workflow: %s", workflow)
				}
				delete(ref, key)
			}
		}
		if len(ref) != 0 {
			return fmt.Errorf("streams are mismatched across source shards: %v", ref)
		}
		return nil
	})
	return err
}

// blsIsReference is partially copied from streamMigrater.templatize.
// It reuses the constants from that function also.
func (rs *resharder) blsIsReference(bls *binlogdatapb.BinlogSource) (bool, error) {
	streamType := unknown
	for _, rule := range bls.Filter.Rules {
		typ, err := rs.identifyRuleType(rule)
		if err != nil {
			return false, err
		}
		switch typ {
		case sharded:
			if streamType == reference {
				return false, fmt.Errorf("cannot reshard streams with a mix of reference and sharded tables: %v", bls)
			}
			streamType = sharded
		case reference:
			if streamType == sharded {
				return false, fmt.Errorf("cannot reshard streams with a mix of reference and sharded tables: %v", bls)
			}
			streamType = reference
		}
	}
	return streamType == reference, nil
}

func (rs *resharder) identifyRuleType(rule *binlogdatapb.Rule) (int, error) {
	vtable, ok := rs.vschema.Tables[rule.Match]
	if !ok {
		return 0, fmt.Errorf("table %v not found in vschema", rule.Match)
	}
	if vtable.Type == vindexes.TypeReference {
		return reference, nil
	}
	// In this case, 'sharded' means that it's not a reference
	// table. We don't care about any other subtleties.
	return sharded, nil
}

func (rs *resharder) copySchema(ctx context.Context) error {
	oneSource := rs.sourceShards[0].MasterAlias
	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		return rs.wr.CopySchemaShard(ctx, oneSource, []string{"/.*"}, nil, false, rs.keyspace, target.ShardName(), 1*time.Second, false)
	})
	return err
}

func (rs *resharder) createStreams(ctx context.Context) error {
	var excludeRules []*binlogdatapb.Rule
	for tableName, table := range rs.vschema.Tables {
		if table.Type == vindexes.TypeReference {
			excludeRules = append(excludeRules, &binlogdatapb.Rule{
				Match:  tableName,
				Filter: "exclude",
			})
		}
	}

	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		targetMaster := rs.targetMasters[target.ShardName()]

		ig := vreplication.NewInsertGenerator(binlogplayer.BlpStopped, targetMaster.DbName())

		// copy excludeRules to prevent data race.
		copyExcludeRules := append([]*binlogdatapb.Rule(nil), excludeRules...)
		for _, source := range rs.sourceShards {
			if !key.KeyRangesIntersect(target.KeyRange, source.KeyRange) {
				continue
			}
			filter := &binlogdatapb.Filter{
				Rules: append(copyExcludeRules, &binlogdatapb.Rule{
					Match:  "/.*",
					Filter: key.KeyRangeString(target.KeyRange),
				}),
			}
			bls := &binlogdatapb.BinlogSource{
				Keyspace:      rs.keyspace,
				Shard:         source.ShardName(),
				Filter:        filter,
				StopAfterCopy: rs.stopAfterCopy,
			}
			ig.AddRow(rs.workflow, bls, "", rs.cell, rs.tabletTypes)
		}

		for _, rstream := range rs.refStreams {
			ig.AddRow(rstream.workflow, rstream.bls, "", rstream.cell, rstream.tabletTypes)
		}
		query := ig.String()
		if _, err := rs.wr.tmc.VReplicationExec(ctx, targetMaster.Tablet, query); err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetMaster.Tablet, query)
		}
		return nil
	})

	return err
}

func (rs *resharder) startStreams(ctx context.Context) error {
	err := rs.forAll(rs.targetShards, func(target *topo.ShardInfo) error {
		targetMaster := rs.targetMasters[target.ShardName()]
		query := fmt.Sprintf("update _vt.vreplication set state='Running' where db_name=%s", encodeString(targetMaster.DbName()))
		if _, err := rs.wr.tmc.VReplicationExec(ctx, targetMaster.Tablet, query); err != nil {
			return vterrors.Wrapf(err, "VReplicationExec(%v, %s)", targetMaster.Tablet, query)
		}
		return nil
	})
	return err
}

func (rs *resharder) forAll(shards []*topo.ShardInfo, f func(*topo.ShardInfo) error) error {
	var wg sync.WaitGroup
	allErrors := &concurrency.AllErrorRecorder{}
	for _, shard := range shards {
		wg.Add(1)
		go func(shard *topo.ShardInfo) {
			defer wg.Done()

			if err := f(shard); err != nil {
				allErrors.RecordError(err)
			}
		}(shard)
	}
	wg.Wait()
	return allErrors.AggrError(vterrors.Aggregate)
}
<|endoftext|>


<commit_before>package docker_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"time"

	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gexec"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/ghttp"
)

var _ = Describe("Building", func() {
	var (
		builderCmd                 *exec.Cmd
		dockerRef                  string
		dockerImageURL             string
		dockerRegistryAddresses    string
		insecureDockerRegistries   string
		dockerDaemonExecutablePath string
		cacheDockerImage           bool
		outputMetadataDir          string
		outputMetadataJSONFilename string
		fakeDockerRegistry         *ghttp.Server
	)

	setupBuilder := func() *gexec.Session {
		session, err := gexec.Start(
			builderCmd,
			GinkgoWriter,
			GinkgoWriter,
		)
		Expect(err).NotTo(HaveOccurred())

		return session
	}

	setupFakeDockerRegistry := func() {
		fakeDockerRegistry.AppendHandlers(
			ghttp.VerifyRequest("GET", "/v1/_ping"),
			ghttp.CombineHandlers(
				ghttp.VerifyRequest("GET", "/v1/repositories/some-repo/images"),
				http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					w.Header().Set("X-Docker-Token", "token-1,token-2")
					w.Write([]byte(`[
                            {"id": "id-1", "checksum": "sha-1"},
                            {"id": "id-2", "checksum": "sha-2"},
                            {"id": "id-3", "checksum": "sha-3"}
                        ]`))
				}),
			),
		)

		fakeDockerRegistry.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest("GET", "/v1/repositories/library/some-repo/tags"),
				http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					w.Write([]byte(`{
                            "latest": "id-1",
                            "some-other-tag": "id-2"
                        }`))
				}),
			),
		)
	}

	BeforeEach(func() {
		var err error

		dockerRef = ""
		dockerImageURL = ""
		dockerRegistryAddresses = ""
		insecureDockerRegistries = ""
		dockerDaemonExecutablePath = "/usr/bin/docker"
		cacheDockerImage = true

		outputMetadataDir, err = ioutil.TempDir("", "building-result")
		Expect(err).NotTo(HaveOccurred())

		outputMetadataJSONFilename = path.Join(outputMetadataDir, "result.json")

		fakeDockerRegistry = ghttp.NewServer()
	})

	dockerPidExists := func() bool {
		_, err := os.Stat("/var/run/docker.pid")
		return err == nil
	}

	AfterEach(func() {
		Eventually(dockerPidExists).Should(BeFalse())
		os.RemoveAll(outputMetadataDir)
	})

	JustBeforeEach(func() {
		args := []string{"-dockerDaemonExecutablePath", dockerDaemonExecutablePath,
			"-outputMetadataJSONFilename", outputMetadataJSONFilename}

		if len(dockerImageURL) > 0 {
			args = append(args, "-dockerImageURL", dockerImageURL)
		}
		if len(dockerRef) > 0 {
			args = append(args, "-dockerRef", dockerRef)
		}
		if len(dockerRegistryAddresses) > 0 {
			args = append(args, "-dockerRegistryAddresses", dockerRegistryAddresses)
		}
		if len(insecureDockerRegistries) > 0 {
			args = append(args, "-insecureDockerRegistries", insecureDockerRegistries)
		}
		if cacheDockerImage {
			args = append(args, "-cacheDockerImage")
		}

		builderCmd = exec.Command(builderPath, args...)

		builderCmd.Env = os.Environ()
	})

	buildDockerImageURL := func() string {
		parts, err := url.Parse(fakeDockerRegistry.URL())
		Expect(err).NotTo(HaveOccurred())
		return fmt.Sprintf("docker://%s/some-repo", parts.Host)
	}

	Context("when running the main", func() {

		Context("when signalled", func() {
			var session *gexec.Session

			BeforeEach(func() {
				dockerImageURL = buildDockerImageURL()

				parts, err := url.Parse(fakeDockerRegistry.URL())
				Expect(err).NotTo(HaveOccurred())
				dockerRegistryAddresses = parts.Host

				setupFakeDockerRegistry()
				fakeDockerRegistry.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/v1/images/id-1/json"),
						http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
							w.Header().Add("X-Docker-Size", "789")
							w.Write([]byte(`{"id":"layer-1","parent":"parent-1","Config":{"Cmd":["-bazbot","-foobar"],"Entrypoint":["/dockerapp","-t"],"WorkingDir":"/workdir"}}`))

							// give the tests time to send signals, while the builder is "working"
							time.Sleep(2 * time.Second)
						}),
					),
				)
			})

			Context("and builder is interrupted", func() {
				JustBeforeEach(func() {
					session = setupBuilder()
					Eventually(session.Out).Should(gbytes.Say("Staging process started ..."))
					session.Interrupt()
				})

				It("processes the signal and exits", func() {
					Eventually(session).Should(gexec.Exit(2))
				})
			})

			Context("and docker is killed", func() {
				JustBeforeEach(func() {
					session = setupBuilder()
					Eventually(session.Out).Should(gbytes.Say("Staging process started ..."))

					cmd := exec.Command("/usr/bin/killall", "docker")
					cmd.Env = os.Environ()
					err := cmd.Run()
					Expect(err).NotTo(HaveOccurred())

					os.Remove("/var/run/docker.sock")
				})

				It("processes the signal and exits", func() {
					Eventually(session).Should(gexec.Exit(2))
				})

			})
		})

	})
})
<commit_msg>Add docker credentials to builder parameters<commit_after>package docker_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"time"

	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/gexec"
	"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega/ghttp"
)

var _ = Describe("Building", func() {
	var (
		builderCmd                 *exec.Cmd
		dockerRef                  string
		dockerImageURL             string
		dockerRegistryAddresses    string
		insecureDockerRegistries   string
		dockerDaemonExecutablePath string
		cacheDockerImage           bool
		dockerLoginServer          string
		dockerUser                 string
		dockerPassword             string
		dockerEmail                string
		outputMetadataDir          string
		outputMetadataJSONFilename string
		fakeDockerRegistry         *ghttp.Server
	)

	setupBuilder := func() *gexec.Session {
		session, err := gexec.Start(
			builderCmd,
			GinkgoWriter,
			GinkgoWriter,
		)
		Expect(err).NotTo(HaveOccurred())

		return session
	}

	setupFakeDockerRegistry := func() {
		fakeDockerRegistry.AppendHandlers(
			ghttp.VerifyRequest("GET", "/v1/_ping"),
			ghttp.CombineHandlers(
				ghttp.VerifyRequest("GET", "/v1/repositories/some-repo/images"),
				http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					w.Header().Set("X-Docker-Token", "token-1,token-2")
					w.Write([]byte(`[
                            {"id": "id-1", "checksum": "sha-1"},
                            {"id": "id-2", "checksum": "sha-2"},
                            {"id": "id-3", "checksum": "sha-3"}
                        ]`))
				}),
			),
		)

		fakeDockerRegistry.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest("GET", "/v1/repositories/library/some-repo/tags"),
				http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					w.Write([]byte(`{
                            "latest": "id-1",
                            "some-other-tag": "id-2"
                        }`))
				}),
			),
		)
	}

	BeforeEach(func() {
		var err error

		dockerRef = ""
		dockerImageURL = ""
		dockerRegistryAddresses = ""
		insecureDockerRegistries = ""
		dockerDaemonExecutablePath = "/usr/bin/docker"
		cacheDockerImage = true
		dockerLoginServer = ""
		dockerUser = ""
		dockerPassword = ""
		dockerEmail = ""

		outputMetadataDir, err = ioutil.TempDir("", "building-result")
		Expect(err).NotTo(HaveOccurred())

		outputMetadataJSONFilename = path.Join(outputMetadataDir, "result.json")

		fakeDockerRegistry = ghttp.NewServer()
	})

	dockerPidExists := func() bool {
		_, err := os.Stat("/var/run/docker.pid")
		return err == nil
	}

	AfterEach(func() {
		Eventually(dockerPidExists).Should(BeFalse())
		os.RemoveAll(outputMetadataDir)
	})

	JustBeforeEach(func() {
		args := []string{"-dockerDaemonExecutablePath", dockerDaemonExecutablePath,
			"-outputMetadataJSONFilename", outputMetadataJSONFilename}

		if len(dockerImageURL) > 0 {
			args = append(args, "-dockerImageURL", dockerImageURL)
		}
		if len(dockerRef) > 0 {
			args = append(args, "-dockerRef", dockerRef)
		}
		if len(dockerRegistryAddresses) > 0 {
			args = append(args, "-dockerRegistryAddresses", dockerRegistryAddresses)
		}
		if len(insecureDockerRegistries) > 0 {
			args = append(args, "-insecureDockerRegistries", insecureDockerRegistries)
		}
		if cacheDockerImage {
			args = append(args, "-cacheDockerImage")
		}
		if len(dockerLoginServer) > 0 {
			args = append(args, "-dockerLoginServer", dockerLoginServer)
		}
		if len(dockerUser) > 0 {
			args = append(args, "-dockerUser", dockerUser)
		}
		if len(dockerPassword) > 0 {
			args = append(args, "-dockerPassword", dockerPassword)
		}
		if len(dockerEmail) > 0 {
			args = append(args, "-dockerEmail", dockerEmail)
		}

		builderCmd = exec.Command(builderPath, args...)

		builderCmd.Env = os.Environ()
	})

	buildDockerImageURL := func() string {
		parts, err := url.Parse(fakeDockerRegistry.URL())
		Expect(err).NotTo(HaveOccurred())
		return fmt.Sprintf("docker://%s/some-repo", parts.Host)
	}

	Context("when running the main", func() {

		Context("when signalled", func() {
			var session *gexec.Session

			BeforeEach(func() {
				dockerImageURL = buildDockerImageURL()

				parts, err := url.Parse(fakeDockerRegistry.URL())
				Expect(err).NotTo(HaveOccurred())
				dockerRegistryAddresses = parts.Host

				setupFakeDockerRegistry()
				fakeDockerRegistry.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/v1/images/id-1/json"),
						http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
							w.Header().Add("X-Docker-Size", "789")
							w.Write([]byte(`{"id":"layer-1","parent":"parent-1","Config":{"Cmd":["-bazbot","-foobar"],"Entrypoint":["/dockerapp","-t"],"WorkingDir":"/workdir"}}`))

							// give the tests time to send signals, while the builder is "working"
							time.Sleep(2 * time.Second)
						}),
					),
				)
			})

			Context("and builder is interrupted", func() {
				JustBeforeEach(func() {
					session = setupBuilder()
					Eventually(session.Out).Should(gbytes.Say("Staging process started ..."))
					session.Interrupt()
				})

				It("processes the signal and exits", func() {
					Eventually(session).Should(gexec.Exit(2))
				})
			})

			Context("and docker is killed", func() {
				JustBeforeEach(func() {
					session = setupBuilder()
					Eventually(session.Out).Should(gbytes.Say("Staging process started ..."))

					cmd := exec.Command("/usr/bin/killall", "docker")
					cmd.Env = os.Environ()
					err := cmd.Run()
					Expect(err).NotTo(HaveOccurred())

					os.Remove("/var/run/docker.sock")
				})

				It("processes the signal and exits", func() {
					Eventually(session).Should(gexec.Exit(2))
				})
			})

		})
	})
})
<|endoftext|>


<commit_before>package golang

import (
	"io/ioutil"
	"path/filepath"
	"runtime"
	"testing"

	api "github.com/goreleaser/goreleaser/build"
	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/testlib"
	"github.com/stretchr/testify/assert"
)

var runtimeTarget = runtime.GOOS + "_" + runtime.GOARCH

func TestBuild(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "foo",
				Goos: []string{
					"linux",
					"windows",
					"darwin",
				},
				Goarch: []string{
					"amd64",
					"arm",
				},
				Goarm: []string{
					"6",
				},
			},
		},
	}
	var ctx = context.New(config)
	var build = Default.Default(ctx.Config.Builds[0])
	assert.ElementsMatch(t, build.Targets, []string{
		"linux_amd64",
		"darwin_amd64",
		"windows_amd64",
		"linux_arm_6",
	})
	for _, target := range build.Targets {
		var err = Default.Build(ctx, build, api.Options{
			Target: target,
			Name:   build.Binary,
			Path:   filepath.Join(folder, "dist", target, build.Binary),
		})
		assert.NoError(t, err)
	}

	assert.Len(t, ctx.Artifacts.List(), len(build.Targets))
}

func TestBuildFailed(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Flags: "-flag-that-dont-exists-to-force-failure",
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
		Target: "darwin_amd64",
	})
	assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
	assert.Empty(t, ctx.Artifacts.List())
}

func TestRunInvalidLdflags(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "nametest",
				Flags:   "-v",
				Ldflags: "-s -w -X main.version={{.Version}",
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
		Target: runtimeTarget,
	})
	assert.EqualError(t, err, `template: ldflags:1: unexpected "}" in operand`)
}

func TestRunPipeWithoutMainFunc(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeMainWithoutMainFunc(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "no-main",
				Hooks:  config.Hooks{},
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	t.Run("empty", func(t *testing.T) {
		ctx.Config.Builds[0].Main = ""
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `build for no-main does not contain a main function`)
	})
	t.Run("not main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "foo.go"
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `could not open foo.go: stat foo.go: no such file or directory`)
	})
	t.Run("glob", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "."
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `build for no-main does not contain a main function`)
	})
	t.Run("fixed main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "main.go"
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `build for no-main does not contain a main function`)
	})
}

func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "foo.go"),
		[]byte("package main\nfunc main() {println(0)}"),
		0644,
	))
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "foo",
				Hooks:  config.Hooks{},
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	t.Run("empty", func(t *testing.T) {
		ctx.Config.Builds[0].Main = ""
		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}))
	})
	t.Run("foo.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "foo.go"
		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}))
	})
	t.Run("glob", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "."
		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}))
	})
}

// FIXME: probably should be refactored
func TestRunPipeWithInvalidOS(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Lang:  "go",
				Flags: "-v",
				Goos: []string{
					"windows",
				},
				Goarch: []string{
					"arm",
				},
			},
		},
	}
	assert.NoError(t, Default.Build(context.New(config), config.Builds[0], api.Options{
		Target: "windows_arm",
	}))
}

func TestLdFlagsFullTemplate(t *testing.T) {
	var config = config.Project{
		Builds: []config.Build{
			{
				Ldflags: `-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}"`,
			},
		},
	}
	var ctx = &context.Context{
		Git: context.GitInfo{
			CurrentTag: "v1.2.3",
			Commit:     "123",
		},
		Version: "1.2.3",
		Config:  config,
		Env:     map[string]string{"FOO": "123"},
	}
	flags, err := ldflags(ctx, ctx.Config.Builds[0])
	assert.NoError(t, err)
	assert.Contains(t, flags, "-s -w")
	assert.Contains(t, flags, "-X main.version=1.2.3")
	assert.Contains(t, flags, "-X main.tag=v1.2.3")
	assert.Contains(t, flags, "-X main.commit=123")
	assert.Contains(t, flags, "-X main.date=")
	assert.Contains(t, flags, `-X "main.foo=123"`)
}

func TestInvalidTemplate(t *testing.T) {
	for template, eerr := range map[string]string{
		"{{ .Nope }":    `template: ldflags:1: unexpected "}" in operand`,
		"{{.Env.NOPE}}": `template: ldflags:1:6: executing "ldflags" at <.Env.NOPE>: map has no entry for key "NOPE"`,
	} {
		t.Run(template, func(tt *testing.T) {
			var config = config.Project{
				Builds: []config.Build{
					{Ldflags: template},
				},
			}
			var ctx = &context.Context{
				Config: config,
			}
			flags, err := ldflags(ctx, ctx.Config.Builds[0])
			assert.EqualError(tt, err, eerr)
			assert.Empty(tt, flags)
		})
	}
}

//
// Helpers
//

func writeMainWithoutMainFunc(t *testing.T, folder string) {
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "main.go"),
		[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
		0644,
	))
}

func writeGoodMain(t *testing.T, folder string) {
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "main.go"),
		[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
		0644,
	))
}

func assertContainsError(t *testing.T, err error, s string) {
	assert.Error(t, err)
	assert.Contains(t, err.Error(), s)
}
<commit_msg>test: skip for now<commit_after>package golang

import (
	"io/ioutil"
	"path/filepath"
	"runtime"
	"testing"

	api "github.com/goreleaser/goreleaser/build"
	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/testlib"
	"github.com/stretchr/testify/assert"
)

var runtimeTarget = runtime.GOOS + "_" + runtime.GOARCH

func TestBuild(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "foo",
				Goos: []string{
					"linux",
					"windows",
					"darwin",
				},
				Goarch: []string{
					"amd64",
					"arm",
				},
				Goarm: []string{
					"6",
				},
			},
		},
	}
	var ctx = context.New(config)
	var build = Default.Default(ctx.Config.Builds[0])
	assert.ElementsMatch(t, build.Targets, []string{
		"linux_amd64",
		"darwin_amd64",
		"windows_amd64",
		"linux_arm_6",
	})
	for _, target := range build.Targets {
		var err = Default.Build(ctx, build, api.Options{
			Target: target,
			Name:   build.Binary,
			Path:   filepath.Join(folder, "dist", target, build.Binary),
		})
		assert.NoError(t, err)
	}

	assert.Len(t, ctx.Artifacts.List(), len(build.Targets))
}

func TestBuildFailed(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Flags: "-flag-that-dont-exists-to-force-failure",
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
		Target: "darwin_amd64",
	})
	assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
	assert.Empty(t, ctx.Artifacts.List())
}

func TestRunInvalidLdflags(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary:  "nametest",
				Flags:   "-v",
				Ldflags: "-s -w -X main.version={{.Version}",
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
		Target: runtimeTarget,
	})
	assert.EqualError(t, err, `template: ldflags:1: unexpected "}" in operand`)
}

func TestRunPipeWithoutMainFunc(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	writeMainWithoutMainFunc(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "no-main",
				Hooks:  config.Hooks{},
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	t.Run("empty", func(t *testing.T) {
		ctx.Config.Builds[0].Main = ""
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `build for no-main does not contain a main function`)
	})
	t.Run("not main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "foo.go"
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `could not open foo.go: stat foo.go: no such file or directory`)
	})
	t.Run("glob", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "."
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `build for no-main does not contain a main function`)
	})
	t.Run("fixed main.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "main.go"
		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}), `build for no-main does not contain a main function`)
	})
}

func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
	folder, back := testlib.Mktmp(t)
	defer back()
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "foo.go"),
		[]byte("package main\nfunc main() {println(0)}"),
		0644,
	))
	var config = config.Project{
		Builds: []config.Build{
			{
				Binary: "foo",
				Hooks:  config.Hooks{},
				Targets: []string{
					runtimeTarget,
				},
			},
		},
	}
	var ctx = context.New(config)
	t.Run("empty", func(t *testing.T) {
		ctx.Config.Builds[0].Main = ""
		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}))
	})
	t.Run("foo.go", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "foo.go"
		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}))
	})
	t.Run("glob", func(t *testing.T) {
		ctx.Config.Builds[0].Main = "."
		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
			Target: runtimeTarget,
		}))
	})
}

func TestRunPipeWithInvalidOS(t *testing.T) {
	// FIXME: probably should be refactored
	t.SkipNow()
	folder, back := testlib.Mktmp(t)
	defer back()
	writeGoodMain(t, folder)
	var config = config.Project{
		Builds: []config.Build{
			{
				Lang:  "go",
				Flags: "-v",
				Goos: []string{
					"windows",
				},
				Goarch: []string{
					"arm",
				},
			},
		},
	}
	assert.NoError(t, Default.Build(context.New(config), config.Builds[0], api.Options{
		Target: "windows_arm",
	}))
}

func TestLdFlagsFullTemplate(t *testing.T) {
	var config = config.Project{
		Builds: []config.Build{
			{
				Ldflags: `-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}"`,
			},
		},
	}
	var ctx = &context.Context{
		Git: context.GitInfo{
			CurrentTag: "v1.2.3",
			Commit:     "123",
		},
		Version: "1.2.3",
		Config:  config,
		Env:     map[string]string{"FOO": "123"},
	}
	flags, err := ldflags(ctx, ctx.Config.Builds[0])
	assert.NoError(t, err)
	assert.Contains(t, flags, "-s -w")
	assert.Contains(t, flags, "-X main.version=1.2.3")
	assert.Contains(t, flags, "-X main.tag=v1.2.3")
	assert.Contains(t, flags, "-X main.commit=123")
	assert.Contains(t, flags, "-X main.date=")
	assert.Contains(t, flags, `-X "main.foo=123"`)
}

func TestInvalidTemplate(t *testing.T) {
	for template, eerr := range map[string]string{
		"{{ .Nope }":    `template: ldflags:1: unexpected "}" in operand`,
		"{{.Env.NOPE}}": `template: ldflags:1:6: executing "ldflags" at <.Env.NOPE>: map has no entry for key "NOPE"`,
	} {
		t.Run(template, func(tt *testing.T) {
			var config = config.Project{
				Builds: []config.Build{
					{Ldflags: template},
				},
			}
			var ctx = &context.Context{
				Config: config,
			}
			flags, err := ldflags(ctx, ctx.Config.Builds[0])
			assert.EqualError(tt, err, eerr)
			assert.Empty(tt, flags)
		})
	}
}

//
// Helpers
//

func writeMainWithoutMainFunc(t *testing.T, folder string) {
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "main.go"),
		[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
		0644,
	))
}

func writeGoodMain(t *testing.T, folder string) {
	assert.NoError(t, ioutil.WriteFile(
		filepath.Join(folder, "main.go"),
		[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
		0644,
	))
}

func assertContainsError(t *testing.T, err error, s string) {
	assert.Error(t, err)
	assert.Contains(t, err.Error(), s)
}
<|endoftext|>


<commit_before>package organization

import (
	"github.com/pivotalservices/cf-mgmt/cloudcontroller"
	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaac"
	"github.com/pivotalservices/cf-mgmt/utils"
)

//Manager -
type Manager interface {
	FindOrg(orgName string) (org *cloudcontroller.Org, err error)
	CreateOrgs(configFile string) (err error)
	UpdateOrgUsers(configDir, ldapBindPassword string) (err error)
	CreateQuotas(configDir string) (err error)
	GetOrgGUID(orgName string) (orgGUID string, err error)
}

//Resources -
type Resources struct {
	Resource []*Resource `json:"resources"`
}

//Resource -
type Resource struct {
	MetaData MetaData `json:"metadata"`
	Entity   Entity   `json:"entity"`
}

//MetaData -
type MetaData struct {
	GUID string `json:"guid"`
}

//InputOrgs -
type InputOrgs struct {
	Orgs []string `yaml:"orgs"`
}

//Contains -
func (s *InputOrgs) Contains(orgName string) bool {
	set := make(map[string]bool)
	for _, v := range s.Orgs {
		set[v] = true
	}
	_, ok := set[orgName]
	return ok
}

//InputUpdateOrgs -
type InputUpdateOrgs struct {
	Org                     string   `yaml:"org"`
	BillingManagerGroup     string   `yaml:"org-billingmanager-group,omitempty"`
	ManagerGroup            string   `yaml:"org-manager-group,omitempty"`
	AuditorGroup            string   `yaml:"org-auditor-group,omitempty"`
	BillingManager          UserMgmt `yaml:"org-billingmanager"`
	Manager                 UserMgmt `yaml:"org-manager"`
	Auditor                 UserMgmt `yaml:"org-auditor"`
	EnableOrgQuota          bool     `yaml:"enable-org-quota"`
	MemoryLimit             int      `yaml:"memory-limit"`
	InstanceMemoryLimit     int      `yaml:"instance-memory-limit"`
	TotalRoutes             int      `yaml:"total-routes"`
	TotalServices           int      `yaml:"total-services"`
	PaidServicePlansAllowed bool     `yaml:"paid-service-plans-allowed"`
}

func (i *InputUpdateOrgs) GetBillingManagerGroup() string {
	if i.BillingManager.LdapGroup != "" {
		return i.BillingManager.LdapGroup
	} else {
		return i.BillingManagerGroup
	}
}

func (i *InputUpdateOrgs) GetManagerGroup() string {
	if i.Manager.LdapGroup != "" {
		return i.Manager.LdapGroup
	} else {
		return i.ManagerGroup
	}
}

func (i *InputUpdateOrgs) GetAuditorGroup() string {
	if i.Manager.LdapGroup != "" {
		return i.Auditor.LdapGroup
	} else {
		return i.AuditorGroup
	}
}

type UserMgmt struct {
	LdapUser  []string `yaml:"ldap_users"`
	Users     []string `yaml:"users"`
	LdapGroup string   `yaml:"ldap_group"`
}

//Entity -
type Entity struct {
	Name string `json:"name"`
}

//Org -
type Org struct {
	AccessToken string `json:"access_token"`
}

//DefaultOrgManager -
type DefaultOrgManager struct {
	CloudController cloudcontroller.Manager
	UAACMgr         uaac.Manager
	UtilsMgr        utils.Manager
	LdapMgr         ldap.Manager
}
<commit_msg>small fix of copy-paste error in GetAuditorGroup<commit_after>package organization

import (
	"github.com/pivotalservices/cf-mgmt/cloudcontroller"
	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaac"
	"github.com/pivotalservices/cf-mgmt/utils"
)

//Manager -
type Manager interface {
	FindOrg(orgName string) (org *cloudcontroller.Org, err error)
	CreateOrgs(configFile string) (err error)
	UpdateOrgUsers(configDir, ldapBindPassword string) (err error)
	CreateQuotas(configDir string) (err error)
	GetOrgGUID(orgName string) (orgGUID string, err error)
}

//Resources -
type Resources struct {
	Resource []*Resource `json:"resources"`
}

//Resource -
type Resource struct {
	MetaData MetaData `json:"metadata"`
	Entity   Entity   `json:"entity"`
}

//MetaData -
type MetaData struct {
	GUID string `json:"guid"`
}

//InputOrgs -
type InputOrgs struct {
	Orgs []string `yaml:"orgs"`
}

//Contains -
func (s *InputOrgs) Contains(orgName string) bool {
	set := make(map[string]bool)
	for _, v := range s.Orgs {
		set[v] = true
	}
	_, ok := set[orgName]
	return ok
}

//InputUpdateOrgs -
type InputUpdateOrgs struct {
	Org                     string   `yaml:"org"`
	BillingManagerGroup     string   `yaml:"org-billingmanager-group,omitempty"`
	ManagerGroup            string   `yaml:"org-manager-group,omitempty"`
	AuditorGroup            string   `yaml:"org-auditor-group,omitempty"`
	BillingManager          UserMgmt `yaml:"org-billingmanager"`
	Manager                 UserMgmt `yaml:"org-manager"`
	Auditor                 UserMgmt `yaml:"org-auditor"`
	EnableOrgQuota          bool     `yaml:"enable-org-quota"`
	MemoryLimit             int      `yaml:"memory-limit"`
	InstanceMemoryLimit     int      `yaml:"instance-memory-limit"`
	TotalRoutes             int      `yaml:"total-routes"`
	TotalServices           int      `yaml:"total-services"`
	PaidServicePlansAllowed bool     `yaml:"paid-service-plans-allowed"`
}

func (i *InputUpdateOrgs) GetBillingManagerGroup() string {
	if i.BillingManager.LdapGroup != "" {
		return i.BillingManager.LdapGroup
	} else {
		return i.BillingManagerGroup
	}
}

func (i *InputUpdateOrgs) GetManagerGroup() string {
	if i.Manager.LdapGroup != "" {
		return i.Manager.LdapGroup
	} else {
		return i.ManagerGroup
	}
}

func (i *InputUpdateOrgs) GetAuditorGroup() string {
	if i.Auditor.LdapGroup != "" {
		return i.Auditor.LdapGroup
	} else {
		return i.AuditorGroup
	}
}

type UserMgmt struct {
	LdapUser  []string `yaml:"ldap_users"`
	Users     []string `yaml:"users"`
	LdapGroup string   `yaml:"ldap_group"`
}

//Entity -
type Entity struct {
	Name string `json:"name"`
}

//Org -
type Org struct {
	AccessToken string `json:"access_token"`
}

//DefaultOrgManager -
type DefaultOrgManager struct {
	CloudController cloudcontroller.Manager
	UAACMgr         uaac.Manager
	UtilsMgr        utils.Manager
	LdapMgr         ldap.Manager
}
<|endoftext|>