<commit_before>package codegen

import (
	"strings"
	"text/template"

	"github.com/jkomoros/boardgame"
)

var headerTemplate *template.Template
var structHeaderTemplate *template.Template
var typedPropertyTemplate *template.Template
var readerTemplate *template.Template
var readSetterTemplate *template.Template
var readSetConfigurerTemplate *template.Template
var enumHeaderTemplate *template.Template
var enumDelegateTemplate *template.Template
var enumItemTemplate *template.Template

func init() {
	funcMap := template.FuncMap{
		"withimmutable": withImmutable,
		"verbfortype":   verbForType,
		"firstLetter":   firstLetter,
	}

	headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(headerTemplateText))
	structHeaderTemplate = template.Must(template.New("structHeader").Funcs(funcMap).Parse(structHeaderTemplateText))
	typedPropertyTemplate = template.Must(template.New("typedProperty").Funcs(funcMap).Parse(typedPropertyTemplateText))
	readerTemplate = template.Must(template.New("reader").Funcs(funcMap).Parse(readerTemplateText))
	readSetterTemplate = template.Must(template.New("readsetter").Funcs(funcMap).Parse(readSetterTemplateText))
	readSetConfigurerTemplate = template.Must(template.New("readsetconfigurer").Funcs(funcMap).Parse(readSetConfigurerTemplateText))
	enumHeaderTemplate = template.Must(template.New("enumheader").Funcs(funcMap).Parse(enumHeaderTemplateText))
	enumDelegateTemplate = template.Must(template.New("enumdelegate").Funcs(funcMap).Parse(enumDelegateTemplateText))
	enumItemTemplate = template.Must(template.New("enumitem").Parse(enumItemTemplateText))
}

func withImmutable(in boardgame.PropertyType) string {
	var result string
	if in.IsInterface() {
		result = "Immutable"
	}
	result += in.Key()
	return result
}

func verbForType(in boardgame.PropertyType) string {
	if in.IsInterface() {
		return "Configure"
	}
	return "Set"
}

func firstLetter(in string) string {

	if in == "" {
		return ""
	}

	return strings.ToLower(in[:1])
}

const headerTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help certain structs
 * implement boardgame.PropertyReader and friends. It was generated
 * by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}
`

const importText = `import (
	"errors"

	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/enum"
)

`

const structHeaderTemplateText = `// Implementation for {{.StructName}}

var {{.ReaderName}}Props = map[string]boardgame.PropertyType{
	{{range $key, $value := .Fields -}}
		"{{$key}}": boardgame.{{$value.Type.String}},
	{{end}}
}

type {{.ReaderName}} struct {
	data *{{.StructName}}
}

func ({{.FirstLetter}} *{{.ReaderName}}) Props() map[string]boardgame.PropertyType {
	return {{.ReaderName}}Props
}

func ({{.FirstLetter}} *{{.ReaderName}}) Prop(name string) (interface{}, error) {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return nil, errors.New("No such property with that name: " + name)
	}

	{{$firstLetter := .FirstLetter}}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		return {{$firstLetter}}.{{withimmutable $type}}Prop(name)
	{{end}}
	}

	return nil, errors.New("Unexpected property type: " + propType.String())
}

{{if .OutputReadSetter -}}

func ({{.FirstLetter}} *{{.ReaderName}}) PropMutable(name string) bool {
	switch name {
		{{range $key, $val := .Fields -}}
	case "{{$key}}":
		return {{$val.Mutable}}
		{{end -}}
	}

	return false
}

func ({{.FirstLetter}} *{{.ReaderName}}) SetProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	{{if $type.IsInterface -}}
	case boardgame.Type{{$type.Key}}:
		return errors.New("SetProp does not allow setting mutable types; use ConfigureProp instead")
	{{- else -}}
	case boardgame.Type{{$type.Key}}:
		val, ok := value.({{$type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
	{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}

{{if .OutputReadSetConfigurer -}}
func ({{.FirstLetter}} *{{.ReaderName}}) ConfigureProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		{{if $type.IsInterface -}}
		if {{$firstLetter}}.PropMutable(name) {
			//Mutable variant
			val, ok := value.({{$type.MutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.MutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
		}
		//Immutable variant
		val, ok := value.({{$type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type}}{{withimmutable $type}}Prop(name, val)
		{{- else -}}
			val, ok := value.({{$type.ImmutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
		{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}
`

const typedPropertyTemplateText = `func ({{.FirstLetter}} *{{.ReaderName}}) {{withimmutable .PropType}}Prop(name string) ({{.PropType.ImmutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				return {{$firstLetter}}.data.{{.Name}}, nil
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{if .OutputReadSetConfigurer -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- else -}}
				return boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{withimmutable .PropType}}Prop(name string, value {{.PropType.ImmutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return boardgame.ErrPropertyImmutable
			{{- else -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{withimmutable .PropType}} prop: " + name)

}

{{end}}
{{end}}

{{if .OutputReadSetter -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) {{.PropType.Key}}Prop(name string) ({{.PropType.MutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{$zeroValue := .PropType.ZeroValue}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return {{$firstLetter}}.data.{{.Name}}, nil
			{{- else -}}
				return {{$zeroValue}}, boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{else}}
func ({{.FirstLetter}} *{{.ReaderName}}) Set{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				{{$firstLetter}}.data.{{.Name}} = value
				return nil
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{end}}
{{end}}
`

const readerTemplateText = `//Reader returns an autp-generated boardgame.PropertyReader for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) Reader() boardgame.PropertyReader {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetterTemplateText = `//ReadSetter returns an autp-generated boardgame.PropertyReadSetter for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetter() boardgame.PropertyReadSetter {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetConfigurerTemplateText = `//ReadSetConfigurer returns an autp-generated boardgame.PropertyReadSetConfigurer for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetConfigurer() boardgame.PropertyReadSetConfigurer {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`
const enumHeaderTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help configure enums.
 * It was generated by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}

import (
	"github.com/jkomoros/boardgame/enum"
)

var enums = enum.NewSet()

`

const enumDelegateTemplateText = `//ConfigureEnums simply returns enums, the auto-generated Enums variable. This
//is output because {{.delegateName}} appears to be a struct that implements
//boardgame.GameDelegate, and does not already have a ConfigureEnums
//explicitly defined.
func ({{firstLetter .delegateName}} *{{.delegateName}}) ConfigureEnums() *enum.Set {
	return enums
}

`

const enumItemTemplateText = `{{if .firstNewKey}}
//Implicitly created constants for {{.prefix}}
const (
	{{.firstNewKey}} = iota - 9223372036854775808
{{range .restNewKeys -}}
	{{.}}
{{- end -}}
)

{{ end -}}
//{{.prefix}}Enum is the enum.Enum for {{.prefix}}
var {{.prefix}}Enum = enums.MustAdd{{if .parents}}Tree{{end}}("{{.prefix}}", map[int]string{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .values -}}
	{{$name}}: "{{$value}}",
	{{end}}
{{if .parents -}} }, map[int]int{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .parents -}}
	{{$name}}: {{$value}},
	{{end}}
{{end -}}
})

`
<commit_msg>rename withImmutable --> immutableKey to reflect better what it does now that it's used in fewer cases. Part of #746.<commit_after>package codegen

import (
	"strings"
	"text/template"

	"github.com/jkomoros/boardgame"
)

var headerTemplate *template.Template
var structHeaderTemplate *template.Template
var typedPropertyTemplate *template.Template
var readerTemplate *template.Template
var readSetterTemplate *template.Template
var readSetConfigurerTemplate *template.Template
var enumHeaderTemplate *template.Template
var enumDelegateTemplate *template.Template
var enumItemTemplate *template.Template

func init() {
	funcMap := template.FuncMap{
		"immutablekey": immutableKey,
		"verbfortype":  verbForType,
		"firstLetter":  firstLetter,
	}

	headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(headerTemplateText))
	structHeaderTemplate = template.Must(template.New("structHeader").Funcs(funcMap).Parse(structHeaderTemplateText))
	typedPropertyTemplate = template.Must(template.New("typedProperty").Funcs(funcMap).Parse(typedPropertyTemplateText))
	readerTemplate = template.Must(template.New("reader").Funcs(funcMap).Parse(readerTemplateText))
	readSetterTemplate = template.Must(template.New("readsetter").Funcs(funcMap).Parse(readSetterTemplateText))
	readSetConfigurerTemplate = template.Must(template.New("readsetconfigurer").Funcs(funcMap).Parse(readSetConfigurerTemplateText))
	enumHeaderTemplate = template.Must(template.New("enumheader").Funcs(funcMap).Parse(enumHeaderTemplateText))
	enumDelegateTemplate = template.Must(template.New("enumdelegate").Funcs(funcMap).Parse(enumDelegateTemplateText))
	enumItemTemplate = template.Must(template.New("enumitem").Parse(enumItemTemplateText))
}

func immutableKey(in boardgame.PropertyType) string {
	var result string
	if in.IsInterface() {
		result = "Immutable"
	}
	result += in.Key()
	return result
}

func verbForType(in boardgame.PropertyType) string {
	if in.IsInterface() {
		return "Configure"
	}
	return "Set"
}

func firstLetter(in string) string {

	if in == "" {
		return ""
	}

	return strings.ToLower(in[:1])
}

const headerTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help certain structs
 * implement boardgame.PropertyReader and friends. It was generated
 * by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}
`

const importText = `import (
	"errors"

	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/enum"
)

`

const structHeaderTemplateText = `// Implementation for {{.StructName}}

var {{.ReaderName}}Props = map[string]boardgame.PropertyType{
	{{range $key, $value := .Fields -}}
		"{{$key}}": boardgame.{{$value.Type.String}},
	{{end}}
}

type {{.ReaderName}} struct {
	data *{{.StructName}}
}

func ({{.FirstLetter}} *{{.ReaderName}}) Props() map[string]boardgame.PropertyType {
	return {{.ReaderName}}Props
}

func ({{.FirstLetter}} *{{.ReaderName}}) Prop(name string) (interface{}, error) {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return nil, errors.New("No such property with that name: " + name)
	}

	{{$firstLetter := .FirstLetter}}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		return {{$firstLetter}}.{{immutablekey $type}}Prop(name)
	{{end}}
	}

	return nil, errors.New("Unexpected property type: " + propType.String())
}

{{if .OutputReadSetter -}}

func ({{.FirstLetter}} *{{.ReaderName}}) PropMutable(name string) bool {
	switch name {
		{{range $key, $val := .Fields -}}
	case "{{$key}}":
		return {{$val.Mutable}}
		{{end -}}
	}

	return false
}

func ({{.FirstLetter}} *{{.ReaderName}}) SetProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	{{if $type.IsInterface -}}
	case boardgame.Type{{$type.Key}}:
		return errors.New("SetProp does not allow setting mutable types; use ConfigureProp instead")
	{{- else -}}
	case boardgame.Type{{$type.Key}}:
		val, ok := value.({{$type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
	{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}

{{if .OutputReadSetConfigurer -}}
func ({{.FirstLetter}} *{{.ReaderName}}) ConfigureProp(name string, value interface{}) error {
	props := {{.FirstLetter}}.Props()
	propType, ok := props[name]

	if !ok {
		return errors.New("No such property with that name: " + name)
	}

	switch propType {
	{{range $type := .PropertyTypes -}}
	case boardgame.Type{{$type.Key}}:
		{{if $type.IsInterface -}}
		if {{$firstLetter}}.PropMutable(name) {
			//Mutable variant
			val, ok := value.({{$type.MutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.MutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
		}
		//Immutable variant
		val, ok := value.({{$type.ImmutableGoType}})
		if !ok {
			return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
		}
		return {{$firstLetter}}.{{verbfortype $type}}{{immutablekey $type}}Prop(name, val)
		{{- else -}}
			val, ok := value.({{$type.ImmutableGoType}})
			if !ok {
				return errors.New("Provided value was not of type {{$type.ImmutableGoType}}")
			}
			return {{$firstLetter}}.{{verbfortype $type}}{{$type.Key}}Prop(name, val)
		{{- end}}
	{{end}}
	}

	return errors.New("Unexpected property type: " + propType.String())
}

{{end}}
`

const typedPropertyTemplateText = `func ({{.FirstLetter}} *{{.ReaderName}}) {{immutablekey .PropType}}Prop(name string) ({{.PropType.ImmutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				return {{$firstLetter}}.data.{{.Name}}, nil
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{if .OutputReadSetConfigurer -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- else -}}
				return boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

func ({{.FirstLetter}} *{{.ReaderName}}) Configure{{immutablekey .PropType}}Prop(name string, value {{.PropType.ImmutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return boardgame.ErrPropertyImmutable
			{{- else -}}
				{{if .UpConverter -}}
				slotValue := value.{{.UpConverter}}()
				if slotValue == nil {
					return errors.New("{{.Name}} couldn't be upconverted, returned nil")
				}
				{{$firstLetter}}.data.{{.Name}} = slotValue
				{{- else -}}
				{{$firstLetter}}.data.{{.Name}} = value
				{{- end}}
				return nil
			{{- end}}
		{{end}}
	}
	{{end}}

	return errors.New("No such {{immutablekey .PropType}} prop: " + name)

}

{{end}}
{{end}}

{{if .OutputReadSetter -}}
{{if .PropType.IsInterface -}}
func ({{.FirstLetter}} *{{.ReaderName}}) {{.PropType.Key}}Prop(name string) ({{.PropType.MutableGoType}}, error) {
	{{$firstLetter := .FirstLetter}}
	{{$zeroValue := .PropType.ZeroValue}}
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
			{{if .Mutable -}}
				return {{$firstLetter}}.data.{{.Name}}, nil
			{{- else -}}
				return {{$zeroValue}}, boardgame.ErrPropertyImmutable
			{{- end}}
		{{end}}
	}
	{{end}}

	return {{.PropType.ZeroValue}}, errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{else}}
func ({{.FirstLetter}} *{{.ReaderName}}) Set{{.PropType.Key}}Prop(name string, value {{.PropType.MutableGoType}}) error {
	{{if .NamesForType}}
	switch name {
		{{range .NamesForType -}}
			case "{{.Name}}":
				{{$firstLetter}}.data.{{.Name}} = value
				return nil
		{{end}}
	}
	{{end}}

	return errors.New("No such {{.PropType.Key}} prop: " + name)

}

{{end}}
{{end}}
`

const readerTemplateText = `//Reader returns an autp-generated boardgame.PropertyReader for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) Reader() boardgame.PropertyReader {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetterTemplateText = `//ReadSetter returns an autp-generated boardgame.PropertyReadSetter for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetter() boardgame.PropertyReadSetter {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`

const readSetConfigurerTemplateText = `//ReadSetConfigurer returns an autp-generated boardgame.PropertyReadSetConfigurer for {{.StructName}}
func ({{.FirstLetter}} *{{.StructName}}) ReadSetConfigurer() boardgame.PropertyReadSetConfigurer {
	return &{{.ReaderName}}{ {{.FirstLetter}} }
}

`
const enumHeaderTemplateText = `/************************************
 *
 * This file contains auto-generated methods to help configure enums.
 * It was generated by the codegen package via 'boardgame-util codegen'.
 *
 * DO NOT EDIT by hand.
 *
 ************************************/

package {{.packageName}}

import (
	"github.com/jkomoros/boardgame/enum"
)

var enums = enum.NewSet()

`

const enumDelegateTemplateText = `//ConfigureEnums simply returns enums, the auto-generated Enums variable. This
//is output because {{.delegateName}} appears to be a struct that implements
//boardgame.GameDelegate, and does not already have a ConfigureEnums
//explicitly defined.
func ({{firstLetter .delegateName}} *{{.delegateName}}) ConfigureEnums() *enum.Set {
	return enums
}

`

const enumItemTemplateText = `{{if .firstNewKey}}
//Implicitly created constants for {{.prefix}}
const (
	{{.firstNewKey}} = iota - 9223372036854775808
{{range .restNewKeys -}}
	{{.}}
{{- end -}}
)

{{ end -}}
//{{.prefix}}Enum is the enum.Enum for {{.prefix}}
var {{.prefix}}Enum = enums.MustAdd{{if .parents}}Tree{{end}}("{{.prefix}}", map[int]string{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .values -}}
	{{$name}}: "{{$value}}",
	{{end}}
{{if .parents -}} }, map[int]int{
	{{ $prefix := .prefix -}}
	{{range $name, $value := .parents -}}
	{{$name}}: {{$value}},
	{{end}}
{{end -}}
})

`
<|endoftext|>


<commit_before>package plg_backend_nop

import (
	. "github.com/mickael-kerjean/filestash/server/common"
	"io"
	"os"
)

func init() {
	Backend.Register("blackhole", BlackHole{})
}

type BlackHole struct{}

func (this BlackHole) Init(params map[string]string, app *App) (IBackend, error) {
	return BlackHole{}, nil
}

func (this BlackHole) LoginForm() Form {
	return Form{
		Elmnts: []FormElement{
			{
				Name:  "type",
				Type:  "hidden",
				Value: "blackhole",
			},
		},
	}
}

func (this BlackHole) Ls(path string) ([]os.FileInfo, error) {
	return []os.FileInfo{}, nil
}

func (this BlackHole) Cat(path string) (io.ReadCloser, error) {
	return nil, ErrNotImplemented
}

func (this BlackHole) Mkdir(path string) error {
	return nil
}

func (this BlackHole) Rm(path string) error {
	return ErrNotImplemented
}

func (this BlackHole) Mv(from, to string) error {
	return ErrNotImplemented
}

func (this BlackHole) Save(path string, content io.Reader) error {
	b := make([]byte, 32<<20) // 32MB
	for {
		_, err := content.Read(b)
		if err == io.EOF {
			break
		}
	}
	return nil
}

func (this BlackHole) Touch(path string) error {
	return nil
}
<commit_msg>improve (backend): cat from blackhole plugin<commit_after>package plg_backend_nop

import (
	. "github.com/mickael-kerjean/filestash/server/common"
	"io"
	"os"
	"strconv"
	"strings"
)

func init() {
	Backend.Register("blackhole", BlackHole{})
}

type LargeFile struct {
	Counter int
}

func (this *LargeFile) Read(p []byte) (n int, err error) {
	if this.Counter <= 0 {
		return 0, io.EOF
	}
	this.Counter = this.Counter - len(p)
	lenp := len(p)
	if lenp > 0 {
		p[0] = '_'
	}
	for i := 0; i < lenp; i += 100 {
		p[i] = '_'
	}
	return lenp, nil
}

func (this LargeFile) Close() error {
	return nil
}

type BlackHole struct{}

func (this BlackHole) Init(params map[string]string, app *App) (IBackend, error) {
	return BlackHole{}, nil
}

func (this BlackHole) LoginForm() Form {
	return Form{
		Elmnts: []FormElement{
			{
				Name:  "type",
				Type:  "hidden",
				Value: "blackhole",
			},
		},
	}
}

func (this BlackHole) Ls(path string) ([]os.FileInfo, error) {
	return []os.FileInfo{}, nil
}

func (this BlackHole) Cat(path string) (io.ReadCloser, error) {
	path = strings.TrimPrefix(path, "/")
	if strings.HasSuffix(path, ".dat") == false {
		return nil, ErrNotImplemented
	}
	path = strings.TrimSuffix(path, ".dat")
	order := 1
	if strings.HasSuffix(path, "K") {
		path = strings.TrimSuffix(path, "K")
		order = order * 1024
	} else if strings.HasSuffix(path, "M") {
		path = strings.TrimSuffix(path, "M")
		order = order * 1024 * 1024
	} else if strings.HasSuffix(path, "G") {
		path = strings.TrimSuffix(path, "G")
		order = order * 1024 * 1024 * 1024
	}
	i, err := strconv.Atoi(path)
	if err != nil {
		return nil, ErrNotImplemented
	}
	return &LargeFile{i * order}, nil
}

func (this BlackHole) Mkdir(path string) error {
	return nil
}

func (this BlackHole) Rm(path string) error {
	return ErrNotImplemented
}

func (this BlackHole) Mv(from, to string) error {
	return ErrNotImplemented
}

func (this BlackHole) Save(path string, content io.Reader) error {
	b := make([]byte, 32<<20) // 32MB
	for {
		_, err := content.Read(b)
		if err == io.EOF {
			break
		}
	}
	return nil
}

func (this BlackHole) Touch(path string) error {
	return nil
}
<|endoftext|>


<commit_before>package model_test

import (
	"context"
	"database/sql"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"testing"

	"github.com/piotrkowalczuk/pqt/example/app/internal/model"
)

var (
	testPostgresAddress string
	testPostgresDebug   bool
)

func TestMain(m *testing.M) {
	flag.BoolVar(&testPostgresDebug, "postgres.debug", getBoolEnvOr("PQT_POSTGRES_DEBUG", true), "if true, all queries will be logged")
	flag.StringVar(&testPostgresAddress, "postgres.address", getStringEnvOr("PQT_POSTGRES_ADDRESS", "postgres://postgres:@localhost/test?sslmode=disable"), "postgres database connection address")
	flag.Parse()

	os.Exit(m.Run())
}

func getStringEnvOr(env, or string) string {
	if v := os.Getenv(env); v != "" {
		return v
	}
	return or
}

func getBoolEnvOr(env string, or bool) bool {
	if v := os.Getenv(env); v != "" {
		f, err := strconv.ParseBool(v)
		if err != nil {
			return or
		}
		return f
	}
	return or
}

func join(arr []string, id int) string {
	arr2 := make([]string, 0, len(arr))

	for _, a := range arr {
		arr2 = append(arr2, fmt.Sprintf("t%d.%s", id, a))
	}
	return strings.Join(arr2, ", ")
}

type suite struct {
	db       *sql.DB
	news     *model.NewsRepositoryBase
	category *model.CategoryRepositoryBase
	comment  *model.CommentRepositoryBase
	pkg      *model.PackageRepositoryBase
	complete *model.CompleteRepositoryBase
}

func setup(t testing.TB) *suite {
	db, err := sql.Open("postgres", testPostgresAddress)
	if err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if _, err = db.Exec(model.SQL); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}

	return &suite{
		db: db,
		news: &model.NewsRepositoryBase{
			Table: model.TableNews,
			DB:    db,
			Debug: testPostgresDebug,
		},
		comment: &model.CommentRepositoryBase{
			Table: model.TableComment,
			DB:    db,
			Debug: testPostgresDebug,
		},
		category: &model.CategoryRepositoryBase{
			Table: model.TableCategory,
			DB:    db,
			Debug: testPostgresDebug,
		},
		complete: &model.CompleteRepositoryBase{
			Table: model.TableComplete,
			DB:    db,
			Debug: testPostgresDebug,
		},
	}
}

func (s *suite) teardown(t testing.TB) {
	if _, err := s.db.Exec("DROP SCHEMA example CASCADE"); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
}

func populateNews(t testing.TB, r *model.NewsRepositoryBase, nb int) {
	for i := 1; i <= nb; i++ {
		_, err := r.Insert(context.Background(), &model.NewsEntity{
			Title:    fmt.Sprintf("title-%d", i),
			Content:  fmt.Sprintf("content-%d", i),
			Lead:     sql.NullString{String: fmt.Sprintf("lead-%d", i), Valid: true},
			Continue: true,
		})
		if err != nil {
			t.Fatalf("unexpected error #%d: %s", i, err.Error())
		}
	}
}
<commit_msg>dynamic columns test fix #2<commit_after>package model_test

import (
	"context"
	"database/sql"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"testing"

	"github.com/piotrkowalczuk/pqt/example/app/internal/model"
)

var (
	testPostgresAddress string
	testPostgresDebug   bool
)

func TestMain(m *testing.M) {
	flag.BoolVar(&testPostgresDebug, "postgres.debug", getBoolEnvOr("PQT_POSTGRES_DEBUG", false), "if true, all queries will be logged")
	flag.StringVar(&testPostgresAddress, "postgres.address", getStringEnvOr("PQT_POSTGRES_ADDRESS", "postgres://postgres:@localhost/test?sslmode=disable"), "postgres database connection address")
	flag.Parse()

	os.Exit(m.Run())
}

func getStringEnvOr(env, or string) string {
	if v := os.Getenv(env); v != "" {
		return v
	}
	return or
}

func getBoolEnvOr(env string, or bool) bool {
	if v := os.Getenv(env); v != "" {
		f, err := strconv.ParseBool(v)
		if err != nil {
			return or
		}
		return f
	}
	return or
}

func join(arr []string, id int) string {
	arr2 := make([]string, 0, len(arr))

	for _, a := range arr {
		arr2 = append(arr2, fmt.Sprintf("t%d.%s", id, a))
	}
	return strings.Join(arr2, ", ")
}

type suite struct {
	db       *sql.DB
	news     *model.NewsRepositoryBase
	category *model.CategoryRepositoryBase
	comment  *model.CommentRepositoryBase
	pkg      *model.PackageRepositoryBase
	complete *model.CompleteRepositoryBase
}

func setup(t testing.TB) *suite {
	db, err := sql.Open("postgres", testPostgresAddress)
	if err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if _, err = db.Exec(model.SQL); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}

	return &suite{
		db: db,
		news: &model.NewsRepositoryBase{
			Table: model.TableNews,
			DB:    db,
			Debug: testPostgresDebug,
		},
		comment: &model.CommentRepositoryBase{
			Table: model.TableComment,
			DB:    db,
			Debug: testPostgresDebug,
		},
		category: &model.CategoryRepositoryBase{
			Table: model.TableCategory,
			DB:    db,
			Debug: testPostgresDebug,
		},
		complete: &model.CompleteRepositoryBase{
			Table: model.TableComplete,
			DB:    db,
			Debug: testPostgresDebug,
		},
	}
}

func (s *suite) teardown(t testing.TB) {
	if _, err := s.db.Exec("DROP SCHEMA example CASCADE"); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
}

func populateNews(t testing.TB, r *model.NewsRepositoryBase, nb int) {
	for i := 1; i <= nb; i++ {
		_, err := r.Insert(context.Background(), &model.NewsEntity{
			Title:    fmt.Sprintf("title-%d", i),
			Content:  fmt.Sprintf("content-%d", i),
			Lead:     sql.NullString{String: fmt.Sprintf("lead-%d", i), Valid: true},
			Continue: true,
		})
		if err != nil {
			t.Fatalf("unexpected error #%d: %s", i, err.Error())
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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 tfgcv

import (
	"testing"

	"github.com/forseti-security/config-validator/pkg/api/validator"
	structpb "github.com/golang/protobuf/ptypes/struct"
	"github.com/stretchr/testify/require"
	"google.golang.org/genproto/googleapis/cloud/asset/v1"
)

func TestProtoViaJSON(t *testing.T) {
	cases := []struct {
		name     string
		input    interface{}
		expected *validator.Asset
	}{
		{
			name:     "Nil",
			input:    nil,
			expected: &validator.Asset{},
		},
		{
			name:     "EmptyAssetMap",
			input:    map[string]interface{}{},
			expected: &validator.Asset{},
		},
		{
			name: "EmptyResourceMap",
			input: map[string]interface{}{
				"resource": map[string]interface{}{},
			},
			expected: &validator.Asset{
				Resource: &asset.Resource{},
			},
		},
		{
			name: "EmptyResourceDataMap",
			input: map[string]interface{}{
				"resource": map[string]interface{}{
					"data": map[string]interface{}{},
				},
			},
			expected: &validator.Asset{
				Resource: &asset.Resource{
					Data: &structpb.Struct{
						Fields: map[string]*structpb.Value{},
					},
				},
			},
		},
		{
			name: "ResourceMapEmptyValue",
			input: map[string]interface{}{
				"resource": map[string]interface{}{
					"data": map[string]interface{}{
						"abc": nil,
					},
				},
			},
			expected: &validator.Asset{
				Resource: &asset.Resource{
					Data: &structpb.Struct{
						Fields: map[string]*structpb.Value{
							"abc": {
								Kind: &structpb.Value_NullValue{},
							},
						},
					},
				},
			},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			msg := &validator.Asset{}
			if err := protoViaJSON(c.input, msg); err != nil {
				t.Error(err)
			}
			require.EqualValues(t, c.expected, msg)
		})
	}
}
<commit_msg>Switched proto comparison to use proto.Equal<commit_after>// Copyright 2019 Google LLC
//
// 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 tfgcv

import (
	"testing"

	"github.com/forseti-security/config-validator/pkg/api/validator"
	structpb "github.com/golang/protobuf/ptypes/struct"
	"github.com/golang/protobuf/proto"
	"github.com/stretchr/testify/require"
	"google.golang.org/genproto/googleapis/cloud/asset/v1"
)

func TestProtoViaJSON(t *testing.T) {
	cases := []struct {
		name     string
		input    interface{}
		expected *validator.Asset
	}{
		{
			name:     "Nil",
			input:    nil,
			expected: &validator.Asset{},
		},
		{
			name:     "EmptyAssetMap",
			input:    map[string]interface{}{},
			expected: &validator.Asset{},
		},
		{
			name: "EmptyResourceMap",
			input: map[string]interface{}{
				"resource": map[string]interface{}{},
			},
			expected: &validator.Asset{
				Resource: &asset.Resource{},
			},
		},
		{
			name: "EmptyResourceDataMap",
			input: map[string]interface{}{
				"resource": map[string]interface{}{
					"data": map[string]interface{}{},
				},
			},
			expected: &validator.Asset{
				Resource: &asset.Resource{
					Data: &structpb.Struct{
						Fields: map[string]*structpb.Value{},
					},
				},
			},
		},
		{
			name: "ResourceMapEmptyValue",
			input: map[string]interface{}{
				"resource": map[string]interface{}{
					"data": map[string]interface{}{
						"abc": nil,
					},
				},
			},
			expected: &validator.Asset{
				Resource: &asset.Resource{
					Data: &structpb.Struct{
						Fields: map[string]*structpb.Value{
							"abc": {
								Kind: &structpb.Value_NullValue{},
							},
						},
					},
				},
			},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			msg := &validator.Asset{}
			if err := protoViaJSON(c.input, msg); err != nil {
				t.Error(err)
			}
			require.True(t, proto.Equal(c.expected, msg))
		})
	}
}
<|endoftext|>


<commit_before>package util

import (
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "job"
    "os"
    "sssh"
    "time"
)

// Get configurations form $HOME/.seshrc
type s3hrc struct {
    User    string
    Keyfile string
}

func Gets3hrc() (conf map[string]string, err error) {
    conf = make(map[string]string)
    fn := os.Getenv("HOME") + "/.seshrc"
    if _, err = os.Stat(fn); os.IsNotExist(err) {
        return conf, err
    }
    if buf, err := ioutil.ReadFile(fn); err != nil {
        return conf, err
    } else {
        rc := &s3hrc{}
        err = json.Unmarshal(buf, rc)
        if err != nil {
            return conf, err
        }
        conf["user"] = rc.User
        conf["keyfile"] = rc.Keyfile
        return conf, err
    }
}

// Hook for per task state changed
func report(output io.Writer, host string) {
    output.Write([]byte(fmt.Sprintf("\033[33m========== %s ==========\033[0m\n", host)))
}
func SerialRun(config map[string]interface{}, host_arr []string) error {
    user, _ := config["User"].(string)
    pwd, _ := config["Password"].(string)
    keyfile, _ := config["Keyfile"].(string)
    cmd, _ := config["Cmd"].(string)
    printer, _ := config["Output"].(io.Writer)

    mgr, _ := job.NewManager()

    for _, h := range host_arr {
        s3h := sssh.NewS3h(h, user, pwd, keyfile, cmd, printer, mgr)
        go func() {
            if _, err := mgr.Receive(-1); err == nil {
                report(s3h.Output, s3h.Host)
                mgr.Send(s3h.Host, map[string]interface{}{"FROM": "MASTER", "BODY": "CONTINUE"})
            } else {
                mgr.Send(s3h.Host, map[string]interface{}{"FROM": "MASTER", "BODY": "STOP"})
            }
        }()
        s3h.Work()
    }
    return nil
}
func ParallelRun(config map[string]interface{}, host_arr []string, tmpdir string) error {
    user, _ := config["User"].(string)
    pwd, _ := config["Password"].(string)
    keyfile, _ := config["Keyfile"].(string)
    cmd, _ := config["Cmd"].(string)
    printer, _ := config["Output"].(io.Writer)

    // Create master
    mgr, _ := job.NewManager()
    // Setup tmp directory for tmp files
    dir := fmt.Sprintf("%s/.s3h.%d", tmpdir, time.Now().Nanosecond())
    if err := os.Mkdir(dir, os.ModeDir|os.ModePerm); err != nil {
        return err
    }
    defer os.RemoveAll(dir)

    var tmpfiles []*os.File
    for _, h := range host_arr {
        file, _ := os.Create(fmt.Sprintf("%s/%s", dir, h))
        tmpfiles = append(tmpfiles, file)
        s3h := sssh.NewS3h(h, user, pwd, keyfile, cmd, file, mgr)
        go s3h.Work()
    }

    size := len(host_arr)
    for {
        data, _ := mgr.Receive(-1)
        info, _ := data.(map[string]interface{})
        if info["BODY"].(string) == "BEGIN" {
            report(info["TAG"].(*sssh.Sssh).Output, info["TAG"].(*sssh.Sssh).Host)
            mgr.Send(info["FROM"].(string), map[string]interface{}{"FROM": "MASTER", "BODY": "CONTINUE"})
        } else if info["BODY"].(string) == "END" {
            size -= 1
            if size == 0 {
                break
            }
        }
    }
    // close tmp files
    for _, f := range tmpfiles {
        f.Close()
    }
    for _, h := range host_arr {
        fn := fmt.Sprintf("%s/%s", dir, h)
        src, _ := os.Open(fn)
        io.Copy(printer, src)
        src.Close()
        os.Remove(fn)
    }
    return nil
}
func Interact(config map[string]interface{}, host string) {
    user, _ := config["User"].(string)
    pwd, _ := config["Password"].(string)
    keyfile, _ := config["Keyfile"].(string)
    cmd, _ := config["Cmd"].(string)
    printer, _ := config["Output"].(io.Writer)

    mgr, _ := job.NewManager()
    s3h := sssh.NewS3h(host, user, pwd, keyfile, cmd, printer, mgr)
    s3h.Login()
}
<commit_msg>clear tmp files before got interrupt or kill signal<commit_after>package util

import (
    "encoding/json"
    "fmt"
    "io"
    "io/ioutil"
    "job"
    "os"
    "os/signal"
    "sssh"
    "time"
)

// Get configurations form $HOME/.seshrc
type s3hrc struct {
    User    string
    Keyfile string
}

func Gets3hrc() (conf map[string]string, err error) {
    conf = make(map[string]string)
    fn := os.Getenv("HOME") + "/.seshrc"
    if _, err = os.Stat(fn); os.IsNotExist(err) {
        return conf, err
    }
    if buf, err := ioutil.ReadFile(fn); err != nil {
        return conf, err
    } else {
        rc := &s3hrc{}
        err = json.Unmarshal(buf, rc)
        if err != nil {
            return conf, err
        }
        conf["user"] = rc.User
        conf["keyfile"] = rc.Keyfile
        return conf, err
    }
}

// Hook for per task state changed
func report(output io.Writer, host string) {
    output.Write([]byte(fmt.Sprintf("\033[33m========== %s ==========\033[0m\n", host)))
}
func SerialRun(config map[string]interface{}, host_arr []string) error {
    user, _ := config["User"].(string)
    pwd, _ := config["Password"].(string)
    keyfile, _ := config["Keyfile"].(string)
    cmd, _ := config["Cmd"].(string)
    printer, _ := config["Output"].(io.Writer)

    mgr, _ := job.NewManager()

    for _, h := range host_arr {
        s3h := sssh.NewS3h(h, user, pwd, keyfile, cmd, printer, mgr)
        go func() {
            if _, err := mgr.Receive(-1); err == nil {
                report(s3h.Output, s3h.Host)
                mgr.Send(s3h.Host, map[string]interface{}{"FROM": "MASTER", "BODY": "CONTINUE"})
            } else {
                mgr.Send(s3h.Host, map[string]interface{}{"FROM": "MASTER", "BODY": "STOP"})
            }
        }()
        s3h.Work()
    }
    return nil
}
func ParallelRun(config map[string]interface{}, host_arr []string, tmpdir string) error {
    user, _ := config["User"].(string)
    pwd, _ := config["Password"].(string)
    keyfile, _ := config["Keyfile"].(string)
    cmd, _ := config["Cmd"].(string)
    printer, _ := config["Output"].(io.Writer)

    // Create master
    mgr, _ := job.NewManager()
    // Setup tmp directory for tmp files
    dir := fmt.Sprintf("%s/.s3h.%d", tmpdir, time.Now().Nanosecond())
    if err := os.Mkdir(dir, os.ModeDir|os.ModePerm); err != nil {
        return err
    }

    // Listen interrupt and kill signal, clear tmp files before exit.
    intqueue := make(chan os.Signal, 1)
    signal.Notify(intqueue, os.Interrupt, os.Kill)
    go func() {
        <-intqueue
        os.RemoveAll(dir)
        os.Exit(1)
    }()
    defer func() {
        signal.Stop(intqueue)
        os.RemoveAll(dir)
    }()

    var tmpfiles []*os.File
    for _, h := range host_arr {
        file, _ := os.Create(fmt.Sprintf("%s/%s", dir, h))
        tmpfiles = append(tmpfiles, file)
        s3h := sssh.NewS3h(h, user, pwd, keyfile, cmd, file, mgr)
        go s3h.Work()
    }

    size := len(host_arr)
    for {
        data, _ := mgr.Receive(-1)
        info, _ := data.(map[string]interface{})
        if info["BODY"].(string) == "BEGIN" {
            report(info["TAG"].(*sssh.Sssh).Output, info["TAG"].(*sssh.Sssh).Host)
            mgr.Send(info["FROM"].(string), map[string]interface{}{"FROM": "MASTER", "BODY": "CONTINUE"})
        } else if info["BODY"].(string) == "END" {
            size -= 1
            if size == 0 {
                break
            }
        }
    }
    // close tmp files
    for _, f := range tmpfiles {
        f.Close()
    }
    for _, h := range host_arr {
        fn := fmt.Sprintf("%s/%s", dir, h)
        src, _ := os.Open(fn)
        io.Copy(printer, src)
        src.Close()
        os.Remove(fn)
    }
    return nil
}
func Interact(config map[string]interface{}, host string) {
    user, _ := config["User"].(string)
    pwd, _ := config["Password"].(string)
    keyfile, _ := config["Keyfile"].(string)
    cmd, _ := config["Cmd"].(string)
    printer, _ := config["Output"].(io.Writer)

    mgr, _ := job.NewManager()
    s3h := sssh.NewS3h(host, user, pwd, keyfile, cmd, printer, mgr)
    s3h.Login()
}
<|endoftext|>


<commit_before>// Copyright 2015 Eleme Inc. All rights reserved.

/*

Package detector starts a tcp server to analyze the trendings of incoming
metrics and send them to alerter.

Detector Input protocol

Line based text, for example:

	timer.count_ps.get_user 1452674178 3.4

Detection Algorithms

A simple approach to detect anomalies is to set fixed thresholds, but
the problem is how large/small they should be. By this way, alertings
will be noisily and thresholds are also hard to maintain.

So we explore an automated way.

The well-known 3-sigma rule: http://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule.

States that nearly all values (99.7%) lie within 3 standard deviations of the mean in
a normal distribution.

That's to say: If the metric value deviates too much from average, it should be an anomaly!

	func IsAnomaly(value float64) bool {
		return math.Abs(value - mean) > 3 * stddev
	}

And we name the ratio of the distance to 3 times standard deviation as score:

	score = math.Abs(value - mean) / (3.0 * stddev)

If score > 1, that means the metric is currently anomalously trending up.

If score < -1, that means the metric is currently anomalously trending down.

If score is larger than -1 and less than 1, the metric is normal.

Detection State

How to get the mean and stddev? We may need to store history metrics on disk
and each time a metric comes in, we query all metrics from db, and compute the
mean and stddev via the traditional math formulas. That's too slow...

We use exponential weighted moving average/standard deviation,
https://en.wikipedia.org/wiki/Moving_average

	// f is a float number between 0 and 1.
	meanOld = mean
	mean = (1 - f) * mean + value * f
	stddev = math.Sqrt((1-f) * stddev * stddev + f * (value - meanOld) * (value - mean))

The recursive formulas above make mean and stddev follow the metric trending. By this way,
we just need to store 2 numbers for detection and the compution is much faster.

*/
package detector
<commit_msg>Update detector doc<commit_after>// Copyright 2015 Eleme Inc. All rights reserved.

/*

Package detector is a tcp server to detect anomalies.

Detector Input protocol

Line based text, for example:

	timer.count_ps.get_user 1452674178 3.4

Detection Algorithms

A simple approach to detect anomalies is to set fixed thresholds, but
the problem is how large/small they should be. By this way, alertings
will be noisily and thresholds are also hard to maintain.

So we explore an automated way.

The well-known 3-sigma rule: http://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule.

States that nearly all values (99.7%) lie within 3 standard deviations of the mean in
a normal distribution.

That's to say: If the metric value deviates too much from average, it should be an anomaly!

	func IsAnomaly(value float64) bool {
		return math.Abs(value - mean) > 3 * stddev
	}

And we name the ratio of the distance to 3 times standard deviation as score:

	score = math.Abs(value - mean) / (3.0 * stddev)

If score > 1, that means the metric is currently anomalously trending up.

If score < -1, that means the metric is currently anomalously trending down.

If score is larger than -1 and less than 1, the metric is normal.

*/
package detector
<|endoftext|>


<commit_before>// +build integration

package cloudfront_test

import (
	"regexp"
	"testing"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/cloudfront"
	"github.com/stretchr/testify/assert"
)

func assertMatches(t *testing.T, regex, expected string) {
	if !regexp.MustCompile(regex).Match([]byte(expected)) {
		t.Errorf("%q\n\tdoes not match /%s/", expected, regex)
	}
}

func TestListDistributions(t *testing.T) {
	client := cloudfront.New(nil)
	resp, err := client.ListDistributions(nil)

	assert.Nil(t, err)
	assert.True(t, *resp.DistributionList.Quantity >= 0)
}

func TestCreateDistribution(t *testing.T) {
	client := cloudfront.New(nil)
	_, serr := client.CreateDistribution(&cloudfront.CreateDistributionInput{
		DistributionConfig: &cloudfront.DistributionConfig{
			CallerReference: aws.String("ID1"),
			Enabled:         aws.True(),
			Comment:         aws.String("A comment"),
			Origins:         &cloudfront.Origins{Quantity: aws.Integer(0)},
		},
	})

	assert.NotNil(t, serr)
	err := aws.Error(serr)
	assert.Equal(t, "MalformedXML", err.Code)
	assertMatches(t, "2 validation errors detected", err.Message)
}
<commit_msg>Refactor integration test<commit_after>// +build integration

package cloudfront_test

import (
	"regexp"
	"testing"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/cloudfront"
	"github.com/stretchr/testify/assert"
)

func assertMatches(t *testing.T, regex, expected string) {
	if !regexp.MustCompile(regex).Match([]byte(expected)) {
		t.Errorf("%q\n\tdoes not match /%s/", expected, regex)
	}
}

func TestListDistributions(t *testing.T) {
	client := cloudfront.New(nil)
	resp, err := client.ListDistributions(nil)

	assert.Nil(t, err)
	assert.True(t, *resp.DistributionList.Quantity >= 0)
}

func TestCreateDistribution(t *testing.T) {
	client := cloudfront.New(nil)
	_, serr := client.CreateDistribution(&cloudfront.CreateDistributionInput{
		DistributionConfig: &cloudfront.DistributionConfig{
			CallerReference: aws.String("ID1"),
			Enabled:         aws.True(),
			Comment:         aws.String("A comment"),
			Origins:         &cloudfront.Origins{Quantity: aws.Integer(0)},
		},
	})

	err := aws.Error(serr)
	assert.NotNil(t, err)
	assert.Equal(t, "MalformedXML", err.Code)
	assertMatches(t, "2 validation errors detected", err.Message)
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	// The type *T is a pointer to value T. Its zero value is nil.
	// Pointer's value refers directly to (or "points to") another value
	// stored elsewhere in the computer memory using its address.
	// NOTE: Value of an uninitialized pointer is nil.
	// NOTE: Go does not support pointer arithmatic like C or C++

	// In Go a pointer is represented using the * (asterisk) character
	// followed by the type of the stored value
	var p *int             //declares a pointer to an int
	fmt.Printf("%#v\n", p) // p is unintialized (no memory has been allocated) hence its value is nil.

	// Dereferencing a nil pointer will lead to run-time error and your program will crash.
	// To see this in action uncomment the next line
	//*p = 24

	// It is easy to check if the pointers value is nil
	if p == nil {
		fmt.Printf("p is a nil pointer\n")
	}

	p = new(int) // p is initialized to zero value of int, 0 (zero)
	fmt.Printf("%#v\n", *p)
	*p = 24 // set p
	fmt.Printf("%#v\n", *p)

	n := "Marry Jane"
	// To get pointer to a value use address-of operator "&"
	np := &n // np points to n
	fmt.Printf("%v\n", np)

	// * (asterisk) is also used to “dereference” pointer variables.
	// Dereferencing a pointer gives us access to the value the pointer points to.
	fmt.Printf("%s\n", *np) //read n through pointer np

	*np = "John Doe" //set n through pointer np
	fmt.Printf("%s\n", n)

	// In practice it is very rare that you want to use a pointer to primitive types.
	// The values of reference type consists of header which contains
	// a referrence to data structure and other meta data. These types are
	// designed to be shared, when you pass a reference type to function
	// only the a copy of the header is passed to the function not the underlying
	// data structure, hence there is no benifit use or create a pointer to
	// values of reference type.

	// More often pointers to a value of struct type are created and passed
	// to function to mutate their state.
	// For more detailed discussion see this excellent blog post:
	// http://www.goinggo.net/2014/12/using-pointers-in-go.html

	// Array and Pointers
	var pToArray *[2]int // declare a pointer to an array of two int
	var ArrayOfP [2]*int // declare an array of two pointers to int

	pToArray = new([2]int)
	for i := 0; i < 2; i++ {
		ArrayOfP[i] = new(int)
		*ArrayOfP[i] = i * 2
		(*pToArray)[i] = i + 2
	}
	fmt.Printf("pToArray = %v (%T)\n", pToArray, pToArray)
	fmt.Printf("ArrayOfP = %v (%T)\n", ArrayOfP, ArrayOfP) // we get memory address
	for i := 0; i < len(ArrayOfP); i++ {
		// by dereferencing the each element we can read the value stored
		fmt.Printf("Elements of ArrayOfP = %v (%T)\n", *ArrayOfP[i], *ArrayOfP[i])
	}
}
<commit_msg>fix typos<commit_after>package main

import "fmt"

func main() {
	// The type *T is a pointer to value T. Its zero value is nil.
	// Pointer's value refers directly to (or "points to") another value
	// stored elsewhere in the computer memory using its address.
	// NOTE: value of an uninitialized pointer is nil.
	// NOTE: Go does not support pointer arithmetic like C or C++

	// In Go a pointer is represented using the * (asterisk) character
	// followed by the type of the stored value
	var p *int             //declares a pointer to an int
	fmt.Printf("%#v\n", p) // p is unintialized (no memory has been allocated) hence its value is nil.

	// Dereferencing a nil pointer will lead to run-time error and your program will crash.
	// To see this in action uncomment the next line
	//*p = 24

	// It is easy to check if the pointers value is nil
	if p == nil {
		fmt.Printf("p is a nil pointer\n")
	}

	p = new(int) // p is initialized to zero value of int, 0 (zero)
	fmt.Printf("%#v\n", *p)
	*p = 24 // set p
	fmt.Printf("%#v\n", *p)

	n := "Marry Jane"
	// To get pointer to a value use address-of operator "&"
	np := &n // np points to n
	fmt.Printf("%v\n", np)

	// * (asterisk) is also used to “dereference” pointer variables.
	// Dereferencing a pointer gives us access to the value the pointer points to.
	fmt.Printf("%s\n", *np) //read n through pointer np

	*np = "John Doe" //set n through pointer np
	fmt.Printf("%s\n", n)

	// In practice it is very rare that you want to use a pointer to primitive types.
	// The values of reference type consists of header which contains
	// a referrence to data structure and other meta data. These types are
	// designed to be shared, when you pass a reference type to function
	// only the a copy of the header is passed to the function not the underlying
	// data structure, hence there is no benifit use or create a pointer to
	// values of reference type.

	// More often pointers to a value of struct type are created and passed
	// to function to mutate their state.
	// For more detailed discussion see this excellent blog post:
	// http://www.goinggo.net/2014/12/using-pointers-in-go.html

	// Array and Pointers
	var pToArray *[2]int // declare a pointer to an array of two int
	var ArrayOfP [2]*int // declare an array of two pointers to int

	pToArray = new([2]int)
	for i := 0; i < 2; i++ {
		ArrayOfP[i] = new(int)
		*ArrayOfP[i] = i * 2
		(*pToArray)[i] = i + 2
	}
	fmt.Printf("pToArray = %v (%T)\n", pToArray, pToArray)
	fmt.Printf("ArrayOfP = %v (%T)\n", ArrayOfP, ArrayOfP) // we get memory address
	for i := 0; i < len(ArrayOfP); i++ {
		// by dereferencing the each element we can read the value stored
		fmt.Printf("Elements of ArrayOfP = %v (%T)\n", *ArrayOfP[i], *ArrayOfP[i])
	}
}
<|endoftext|>


<commit_before>package doc

import (
	"errors"
	"github.com/sqs/gorp"
	"net/url"
	"sourcegraph.com/sourcegraph/repo"
	"sourcegraph.com/sourcegraph/vcsfs"
)

var ErrNoReadme = errors.New("no readme found in repository")

// GetFormattedReadme returns repo's HTML-formatted readme, or an empty string
// and ErrNoReadme if the repository has no README.
func GetFormattedReadme(dbh gorp.SqlExecutor, repo *repo.Repository) (formattedReadme string, err error) {
	for _, rd := range readmeNames {
		cloneURL, err := url.Parse(repo.CloneURL)
		if err != nil {
			return "", err
		}
		src, err := vcsfs.GetFile(repo.VCS, cloneURL, repo.RevSpecOrDefault(), rd.name)
		if err == nil {
			return ToHTML(rd.fmt, string(src))
		}
	}
	return "", ErrNoReadme
}

var readmeNames = []struct {
	name string
	fmt  Format
}{
	{"README.md", Markdown},
	{"ReadMe.md", Markdown},
	{"Readme.md", Markdown},
	{"readme.md", Markdown},
	{"README.markdown", Markdown},
	{"ReadMe.markdown", Markdown},
	{"readme.markdown", Markdown},
	{"README", Text},
	{"ReadMe", Text},
	{"Readme", Text},
	{"readme", Text},
	{"README.rdoc", Text},
	{"README.txt", Text},
	{"ReadMe.txt", Text},
	{"readme.txt", Text},
	{"README.rst", ReStructuredText},
	{"ReadMe.rst", ReStructuredText},
	{"Readme.rst", ReStructuredText},
	{"readme.rst", ReStructuredText},
}
<commit_msg>batch file getting for readme<commit_after>package doc

import (
	"errors"
	"github.com/sqs/gorp"
	"net/url"
	"path/filepath"
	"sourcegraph.com/sourcegraph/repo"
	"sourcegraph.com/sourcegraph/vcsfs"
	"strings"
)

var ErrNoReadme = errors.New("no readme found in repository")

// GetFormattedReadme returns repo's HTML-formatted readme, or an empty string
// and ErrNoReadme if the repository has no README.
func GetFormattedReadme(dbh gorp.SqlExecutor, repo *repo.Repository) (formattedReadme string, err error) {
	cloneURL, err := url.Parse(repo.CloneURL)
	if err != nil {
		return "", err
	}
	src, path, err := vcsfs.GetFirstExistingFile(repo.VCS, cloneURL, repo.RevSpecOrDefault(), readmeNames)
	if err != nil {
		return "", ErrNoReadme
	}
	return ToHTML(readmeFormats[strings.ToLower(filepath.Ext(path))], string(src))
}

var readmeNames = []string{
	"README.md",
	"ReadMe.md",
	"Readme.md",
	"readme.md",
	"README.markdown",
	"ReadMe.markdown",
	"readme.markdown",
	"README",
	"ReadMe",
	"Readme",
	"readme",
	"README.rdoc",
	"README.txt",
	"ReadMe.txt",
	"readme.txt",
	"README.rst",
	"ReadMe.rst",
	"Readme.rst",
	"readme.rst",
}

var readmeFormats = map[string]Format{
	".md":       Markdown,
	".markdown": Markdown,
	".mdown":    Markdown,
	".rdoc":     Markdown, // TODO(sqs): actually implement RDoc
	".txt":      Text,
	".ascii":    Text,
	".rst":      ReStructuredText,
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main(){
fmt.Println( executable() )
}<commit_msg>Removed filePath.go<commit_after><|endoftext|>


<commit_before>package dev_test

import (
	"testing"

	qc "github.com/relab/gorums/dev"
)

func TestEqualGlobalConfigurationIDs(t *testing.T) {
	// Equal set of addresses, but different order.
	addrsOne := []string{"localhost:8080", "localhost:8081", "localhost:8082"}
	addrsTwo := []string{"localhost:8081", "localhost:8082", "localhost:8080"}

	mgrOne, err := qc.NewManager(addrsOne, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}
	mgrTwo, err := qc.NewManager(addrsTwo, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}

	ids := mgrOne.NodeIDs()
	qspec := NewMajorityQSpec(len(ids))

	// Create a configuration in each manager using all nodes.
	// Global ids should be equal.
	configOne, err := mgrOne.NewConfiguration(ids, qspec)
	if err != nil {
		t.Fatalf("error creating config one: %v", err)
	}
	configTwo, err := mgrTwo.NewConfiguration(ids, qspec)
	if err != nil {
		t.Fatalf("error creating config two: %v", err)
	}
	if configOne.ID() != configTwo.ID() {
		t.Errorf("global configuration ids differ, %d != %d",
			configOne.ID(), configTwo.ID())
	}
}

func TestCreateConfiguration(t *testing.T) {
	addrs := []string{"localhost:8080", "localhost:8081", "localhost:8082"}
	mgr, err := qc.NewManager(addrs, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}

	ids := mgr.NodeIDs()
	qspec := NewMajorityQSpec(len(ids))

	config, err := mgr.NewConfiguration(ids, qspec)
	if err != nil {
		t.Errorf("got error creating configuration, want none (%v)", err)
	}

	cids := config.NodeIDs()
	if !equal(cids, ids) {
		t.Errorf("ids from Manager (got %v) and ids from configuration containing all nodes (got %v) should be equal",
			ids, cids)
	}

	_, size := mgr.Size()
	if size != 1 {
		t.Errorf("got #%d configurations from Manager, want %d", size, 1)
	}
}

func equal(a, b []uint32) bool {
	if len(a) != len(b) {
		return false
	}
	for i, x := range a {
		if x != b[i] {
			return false
		}
	}
	return true
}
<commit_msg>dev/mgr_test.go: Add test for duplicated node ids in new configs<commit_after>package dev_test

import (
	"testing"

	qc "github.com/relab/gorums/dev"
)

func TestEqualGlobalConfigurationIDsDifferentOrder(t *testing.T) {
	// Equal set of addresses, but different order.
	addrsOne := []string{"localhost:8080", "localhost:8081", "localhost:8082"}
	addrsTwo := []string{"localhost:8081", "localhost:8082", "localhost:8080"}

	mgrOne, err := qc.NewManager(addrsOne, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}
	mgrTwo, err := qc.NewManager(addrsTwo, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}

	ids := mgrOne.NodeIDs()
	qspec := NewMajorityQSpec(len(ids))

	// Create a configuration in each manager using all nodes.
	// Global ids should be equal.
	configOne, err := mgrOne.NewConfiguration(ids, qspec)
	if err != nil {
		t.Fatalf("error creating config one: %v", err)
	}
	configTwo, err := mgrTwo.NewConfiguration(ids, qspec)
	if err != nil {
		t.Fatalf("error creating config two: %v", err)
	}
	if configOne.ID() != configTwo.ID() {
		t.Errorf("global configuration ids differ, %d != %d",
			configOne.ID(), configTwo.ID())
	}
}

func TestEqualGlobalConfigurationIDsDuplicateID(t *testing.T) {
	addrs := []string{"localhost:8080", "localhost:8081", "localhost:8082"}

	mgr, err := qc.NewManager(addrs, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}

	ids := mgr.NodeIDs()

	// Create a configuration with all ids available in the manager.
	configOne, err := mgr.NewConfiguration(ids, &MajorityQSpec{})
	if err != nil {
		t.Fatalf("error creating config one: %v", err)
	}
	// Create a configuration with all ids available in the manager and a duplicate.
	configTwo, err := mgr.NewConfiguration(append(ids, ids[0]), &MajorityQSpec{})
	if err != nil {
		t.Fatalf("error creating config two: %v", err)
	}

	// Global ids should be equal.
	if configOne.ID() != configTwo.ID() {
		t.Errorf("global configuration ids differ, %d != %d",
			configOne.ID(), configTwo.ID())
	}
}

func TestCreateConfiguration(t *testing.T) {
	addrs := []string{"localhost:8080", "localhost:8081", "localhost:8082"}
	mgr, err := qc.NewManager(addrs, qc.WithNoConnect())
	if err != nil {
		t.Fatal(err)
	}

	ids := mgr.NodeIDs()
	qspec := NewMajorityQSpec(len(ids))

	config, err := mgr.NewConfiguration(ids, qspec)
	if err != nil {
		t.Errorf("got error creating configuration, want none (%v)", err)
	}

	cids := config.NodeIDs()
	if !equal(cids, ids) {
		t.Errorf("ids from Manager (got %v) and ids from configuration containing all nodes (got %v) should be equal",
			ids, cids)
	}

	_, size := mgr.Size()
	if size != 1 {
		t.Errorf("got #%d configurations from Manager, want %d", size, 1)
	}
}

func equal(a, b []uint32) bool {
	if len(a) != len(b) {
		return false
	}
	for i, x := range a {
		if x != b[i] {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package digraph

import (
	"log"
	"testing"
)

// TestAddVertex verifies that the AddVertex method is working properly
func TestAddVertex(t *testing.T) {
	log.Println("TestAddVertex()")

	// Create a digraph
	graph := New()

	// Create a table of tests and expected error results
	var tests = []struct{
		vertex interface{}
		result error
	}{
		// Add vertices which do not exist
		{1, nil},
		{2, nil},
		{3, nil},
		// Add vertices which already exist
		{1, ErrVertexExists},
		{2, ErrVertexExists},
		{3, ErrVertexExists},
	}

	// Iterate test table, check results
	for _, test := range tests {
		if err := graph.AddVertex(test.vertex); err != test.result {
			t.Fatalf("graph.AddVertex(%d) - unexpected result: %s", test.vertex, err.Error())
		}
	}
}

// TestAddEdge verifies that the AddEdge method is working properly
func TestAddEdge(t *testing.T) {
	log.Println("TestAddEdge()")

	// Create a digraph, add root vertex
	graph := New()

	// Create a table of tests and expected error results
	var tests = []struct {
		source interface{}
		target interface{}
		result error
	}{
		// Add edges which do not exist
		{1, 2, nil},
		{1, 3, nil},
		{2, 3, nil},
		{3, 4, nil},
		// Add edges which already exist
		{1, 2, ErrEdgeExists},
		{3, 4, ErrEdgeExists},
		// Add edges which create a cycle
		{1, 1, ErrCycle},
		{4, 1, ErrCycle},
	}

	// Iterate test table, check results
	for _, test := range tests {
		if err := graph.AddEdge(test.source, test.target); err != test.result {
			t.Fatalf("graph.AddEdge(%d, %d) - unexpected result: %s", test.source, test.target, err.Error())
		}
	}
}
<commit_msg>digraph_test, add TestDepthFirstSearch<commit_after>package digraph

import (
	"log"
	"testing"
)

// TestAddVertex verifies that the AddVertex method is working properly
func TestAddVertex(t *testing.T) {
	log.Println("TestAddVertex()")

	// Create a digraph
	graph := New()

	// Create a table of tests and expected error results
	var tests = []struct{
		vertex interface{}
		result error
	}{
		// Add vertices which do not exist
		{1, nil},
		{2, nil},
		{3, nil},
		// Add vertices which already exist
		{1, ErrVertexExists},
		{2, ErrVertexExists},
		{3, ErrVertexExists},
	}

	// Iterate test table, check results
	for _, test := range tests {
		if err := graph.AddVertex(test.vertex); err != test.result {
			t.Fatalf("graph.AddVertex(%d) - unexpected result: %s", test.vertex, err.Error())
		}
	}
}

// TestAddEdge verifies that the AddEdge method is working properly
func TestAddEdge(t *testing.T) {
	log.Println("TestAddEdge()")

	// Create a digraph
	graph := New()

	// Create a table of tests and expected error results
	var tests = []struct {
		source interface{}
		target interface{}
		result error
	}{
		// Add edges which do not exist
		{1, 2, nil},
		{1, 3, nil},
		{2, 3, nil},
		{3, 4, nil},
		// Add edges which already exist
		{1, 2, ErrEdgeExists},
		{3, 4, ErrEdgeExists},
		// Add edges which create a cycle
		{1, 1, ErrCycle},
		{4, 1, ErrCycle},
	}

	// Iterate test table, check results
	for _, test := range tests {
		if err := graph.AddEdge(test.source, test.target); err != test.result {
			t.Fatalf("graph.AddEdge(%d, %d) - unexpected result: %s", test.source, test.target, err.Error())
		}
	}
}

// TestDepthFirstSearch verifies that the DepthFirstSearch method is working properly
func TestDepthFirstSearch(t *testing.T) {
	log.Println("TestDepthFirstSearch()")

	// Create a digraph
	graph := New()

	// Generate some known paths
	var paths = []struct{
		source interface{}
		target interface{}
	}{
		{1, 2}, {1, 5},
		{2, 3}, {2, 5},
		{3, 4}, {3, 6},
		{4, 5}, {4, 6},
		{5, 6},
	}

	// Create edges
	for _, p := range paths {
		graph.AddEdge(p.source, p.target)
	}

	// Create a table of tests and expected boolean results
	var tests = []struct{
		source interface{}
		target interface{}
		result bool
	}{
		// Paths reachable between source and target
		{1, 2, true},
		{1, 4, true},
		{2, 6, true},
		// Paths NOT reachable between source and target
		{6, 3, false},
		{4, 1, false},
		{5, 2, false},
	}

	// Iterate test table, check results
	for _, test := range tests {
		if found := graph.DepthFirstSearch(test.source, test.target); found != test.result {
			t.Fatalf("graph.DepthFirstSearch(%d, %d) - unexpected result: %t", test.source, test.target, test.result)
		}
	}
}
<|endoftext|>


<commit_before>package dockerclient

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"sync/atomic"
)

var (
	ErrNotFound = errors.New("Not found")
)

type DockerClient struct {
	URL           *url.URL
	HTTPClient    *http.Client
	monitorEvents int32
}

type Callback func(*Event, ...interface{})

func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
	u, err := url.Parse(daemonUrl)
	if err != nil {
		return nil, err
	}
	if u.Scheme == "tcp" {
		u.Scheme = "http"
	}
	httpClient := newHTTPClient(u, tlsConfig)
	return &DockerClient{u, httpClient, 0}, nil
}

func (client *DockerClient) doRequest(method string, path string, body []byte) ([]byte, error) {
	b := bytes.NewBuffer(body)
	req, err := http.NewRequest(method, client.URL.String()+path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := client.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == 404 {
		return nil, ErrNotFound
	}
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("%s: %s", resp.Status, data)
	}
	return data, nil
}

func (client *DockerClient) ListContainers(all bool) ([]Container, error) {
	argAll := 0
	if all == true {
		argAll = 1
	}
	args := fmt.Sprintf("?all=%d", argAll)
	data, err := client.doRequest("GET", "/v1.10/containers/json"+args, nil)
	if err != nil {
		return nil, err
	}
	ret := []Container{}
	err = json.Unmarshal(data, &ret)
	if err != nil {
		return nil, err
	}
	return ret, nil
}

func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) {
	uri := fmt.Sprintf("/v1.10/containers/%s/json", id)
	data, err := client.doRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	info := &ContainerInfo{}
	err = json.Unmarshal(data, info)
	if err != nil {
		return nil, err
	}
	return info, nil
}

func (client *DockerClient) CreateContainer(config *ContainerConfig, name string) (string, error) {
	data, err := json.Marshal(config)
	if err != nil {
		return "", err
	}
	uri := "/v1.10/containers/create"

	if name != "" {
		v := url.Values{}
		v.Set("name", name)
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}
	data, err = client.doRequest("POST", uri, data)
	if err != nil {
		return "", err
	}
	result := &RespContainersCreate{}
	err = json.Unmarshal(data, result)
	if err != nil {
		return "", err
	}
	return result.Id, nil
}

func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
	data, err := json.Marshal(config)
	if err != nil {
		return err
	}
	uri := fmt.Sprintf("/v1.10/containers/%s/start", id)
	_, err = client.doRequest("POST", uri, data)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) StopContainer(id string, timeout int) error {
	uri := fmt.Sprintf("/v1.10/containers/%s/stop?t=%d", id, timeout)
	_, err := client.doRequest("POST", uri, nil)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) RestartContainer(id string, timeout int) error {
	uri := fmt.Sprintf("/v1.10/containers/%s/restart?t=%d", id, timeout)
	_, err := client.doRequest("POST", uri, nil)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) KillContainer(id string) error {
	uri := fmt.Sprintf("/v1.10/containers/%s/kill", id)
	_, err := client.doRequest("POST", uri, nil)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) StartMonitorEvents(cb Callback, args ...interface{}) {
	atomic.StoreInt32(&client.monitorEvents, 1)
	go client.getEvents(cb, args...)
}

func (client *DockerClient) getEvents(cb Callback, args ...interface{}) {
	uri := client.URL.String() + "/v1.10/events"
	resp, err := client.HTTPClient.Get(uri)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	for atomic.LoadInt32(&client.monitorEvents) > 0 {
		var event *Event
		if err := dec.Decode(&event); err != nil {
			log.Println(err)
			return
		}
		cb(event, args...)
	}
}

func (client *DockerClient) StopAllMonitorEvents() {
	atomic.StoreInt32(&client.monitorEvents, 0)
}

func (client *DockerClient) Version() (*Version, error) {
	data, err := client.doRequest("GET", "/v1.10/version", nil)
	if err != nil {
		return nil, err
	}
	version := &Version{}
	err = json.Unmarshal(data, version)
	if err != nil {
		return nil, err
	}
	return version, nil
}

func (client *DockerClient) PullImage(name, tag string) error {
	v := url.Values{}
	v.Set("fromImage", name)
	if tag != "" {
		v.Set("tag", tag)
	}
	_, err := client.doRequest("POST", "/v1.10/images/create?"+v.Encode(), nil)
	return err
}

func (client *DockerClient) RemoveContainer(id string) error {
	_, err := client.doRequest("DELETE", fmt.Sprintf("/v1.10/containers/%s", id), nil)
	return err
}

func (client *DockerClient) ListImages() ([]*Image, error) {
	data, err := client.doRequest("GET", "/v1.10/images/json", nil)
	if err != nil {
		return nil, err
	}

	var images []*Image
	if err := json.Unmarshal(data, &images); err != nil {
		return nil, err
	}

	return images, nil
}
<commit_msg>Add RemoveImage()<commit_after>package dockerclient

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"sync/atomic"
)

var (
	ErrNotFound = errors.New("Not found")
)

type DockerClient struct {
	URL           *url.URL
	HTTPClient    *http.Client
	monitorEvents int32
}

type Callback func(*Event, ...interface{})

func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
	u, err := url.Parse(daemonUrl)
	if err != nil {
		return nil, err
	}
	if u.Scheme == "tcp" {
		u.Scheme = "http"
	}
	httpClient := newHTTPClient(u, tlsConfig)
	return &DockerClient{u, httpClient, 0}, nil
}

func (client *DockerClient) doRequest(method string, path string, body []byte) ([]byte, error) {
	b := bytes.NewBuffer(body)
	req, err := http.NewRequest(method, client.URL.String()+path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err := client.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == 404 {
		return nil, ErrNotFound
	}
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("%s: %s", resp.Status, data)
	}
	return data, nil
}

func (client *DockerClient) ListContainers(all bool) ([]Container, error) {
	argAll := 0
	if all == true {
		argAll = 1
	}
	args := fmt.Sprintf("?all=%d", argAll)
	data, err := client.doRequest("GET", "/v1.10/containers/json"+args, nil)
	if err != nil {
		return nil, err
	}
	ret := []Container{}
	err = json.Unmarshal(data, &ret)
	if err != nil {
		return nil, err
	}
	return ret, nil
}

func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) {
	uri := fmt.Sprintf("/v1.10/containers/%s/json", id)
	data, err := client.doRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	info := &ContainerInfo{}
	err = json.Unmarshal(data, info)
	if err != nil {
		return nil, err
	}
	return info, nil
}

func (client *DockerClient) CreateContainer(config *ContainerConfig, name string) (string, error) {
	data, err := json.Marshal(config)
	if err != nil {
		return "", err
	}
	uri := "/v1.10/containers/create"

	if name != "" {
		v := url.Values{}
		v.Set("name", name)
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}
	data, err = client.doRequest("POST", uri, data)
	if err != nil {
		return "", err
	}
	result := &RespContainersCreate{}
	err = json.Unmarshal(data, result)
	if err != nil {
		return "", err
	}
	return result.Id, nil
}

func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
	data, err := json.Marshal(config)
	if err != nil {
		return err
	}
	uri := fmt.Sprintf("/v1.10/containers/%s/start", id)
	_, err = client.doRequest("POST", uri, data)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) StopContainer(id string, timeout int) error {
	uri := fmt.Sprintf("/v1.10/containers/%s/stop?t=%d", id, timeout)
	_, err := client.doRequest("POST", uri, nil)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) RestartContainer(id string, timeout int) error {
	uri := fmt.Sprintf("/v1.10/containers/%s/restart?t=%d", id, timeout)
	_, err := client.doRequest("POST", uri, nil)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) KillContainer(id string) error {
	uri := fmt.Sprintf("/v1.10/containers/%s/kill", id)
	_, err := client.doRequest("POST", uri, nil)
	if err != nil {
		return err
	}
	return nil
}

func (client *DockerClient) StartMonitorEvents(cb Callback, args ...interface{}) {
	atomic.StoreInt32(&client.monitorEvents, 1)
	go client.getEvents(cb, args...)
}

func (client *DockerClient) getEvents(cb Callback, args ...interface{}) {
	uri := client.URL.String() + "/v1.10/events"
	resp, err := client.HTTPClient.Get(uri)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	for atomic.LoadInt32(&client.monitorEvents) > 0 {
		var event *Event
		if err := dec.Decode(&event); err != nil {
			log.Println(err)
			return
		}
		cb(event, args...)
	}
}

func (client *DockerClient) StopAllMonitorEvents() {
	atomic.StoreInt32(&client.monitorEvents, 0)
}

func (client *DockerClient) Version() (*Version, error) {
	data, err := client.doRequest("GET", "/v1.10/version", nil)
	if err != nil {
		return nil, err
	}
	version := &Version{}
	err = json.Unmarshal(data, version)
	if err != nil {
		return nil, err
	}
	return version, nil
}

func (client *DockerClient) PullImage(name, tag string) error {
	v := url.Values{}
	v.Set("fromImage", name)
	if tag != "" {
		v.Set("tag", tag)
	}
	_, err := client.doRequest("POST", "/v1.10/images/create?"+v.Encode(), nil)
	return err
}

func (client *DockerClient) RemoveContainer(id string) error {
	_, err := client.doRequest("DELETE", fmt.Sprintf("/v1.10/containers/%s", id), nil)
	return err
}

func (client *DockerClient) ListImages() ([]*Image, error) {
	data, err := client.doRequest("GET", "/v1.10/images/json", nil)
	if err != nil {
		return nil, err
	}

	var images []*Image
	if err := json.Unmarshal(data, &images); err != nil {
		return nil, err
	}

	return images, nil
}

func (client *DockerClient) RemoveImage(name string) error {
	_, err := client.doRequest("DELETE", fmt.Sprintf("/v1.10/images/%s", name), nil)
	return err
}
<|endoftext|>


<commit_before>package auth_test

import (
	"crypto/rand"
	"crypto/rsa"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"time"

	"golang.org/x/oauth2"

	"github.com/dgrijalva/jwt-go"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
	"github.com/pivotal-golang/lager/lagertest"

	"github.com/concourse/atc/auth"
	"github.com/concourse/atc/auth/fakes"
)

var _ = Describe("OAuthCallbackHandler", func() {
	var (
		fakeProviderA *fakes.FakeProvider
		fakeProviderB *fakes.FakeProvider

		signingKey *rsa.PrivateKey

		server *httptest.Server
		client *http.Client
	)

	BeforeEach(func() {
		fakeProviderA = new(fakes.FakeProvider)
		fakeProviderB = new(fakes.FakeProvider)

		var err error
		signingKey, err = rsa.GenerateKey(rand.Reader, 1024)
		Expect(err).ToNot(HaveOccurred())

		handler, err := auth.NewOAuthHandler(
			lagertest.NewTestLogger("test"),
			auth.Providers{
				"a": fakeProviderA,
				"b": fakeProviderB,
			},
			signingKey,
		)
		Expect(err).ToNot(HaveOccurred())

		mux := http.NewServeMux()
		mux.Handle("/auth/", handler)
		mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "main page")
		}))

		server = httptest.NewServer(mux)

		client = &http.Client{
			Transport: &http.Transport{},
		}
	})

	keyFunc := func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}

		return signingKey.Public(), nil
	}

	Describe("GET /auth/:provider/callback", func() {
		var redirectTarget *ghttp.Server
		var request *http.Request
		var response *http.Response

		BeforeEach(func() {
			redirectTarget = ghttp.NewServer()
			redirectTarget.RouteToHandler("GET", "/", ghttp.RespondWith(http.StatusOK, "sup"))

			var err error

			request, err = http.NewRequest("GET", server.URL, nil)
			Expect(err).NotTo(HaveOccurred())
		})

		JustBeforeEach(func() {
			var err error

			response, err = client.Do(request)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("to a known provider", func() {
			BeforeEach(func() {
				request.URL.Path = "/auth/b/callback"
			})

			Context("when the request's state is valid", func() {
				BeforeEach(func() {
					token := jwt.New(auth.SigningMethod)
					token.Claims["exp"] = time.Now().Add(time.Hour).Unix()

					signedState, err := token.SignedString(signingKey)
					Expect(err).ToNot(HaveOccurred())

					request.URL.RawQuery = url.Values{
						"code":  {"some-code"},
						"state": {signedState},
					}.Encode()
				})

				Context("when exchanging the token succeeds", func() {
					var token *oauth2.Token
					var httpClient *http.Client

					BeforeEach(func() {
						token = &oauth2.Token{AccessToken: "some-access-token"}
						httpClient = &http.Client{}

						fakeProviderB.ExchangeReturns(token, nil)
						fakeProviderB.ClientReturns(httpClient)
					})

					It("generated the OAuth token using the request's code", func() {
						Expect(fakeProviderB.ExchangeCallCount()).To(Equal(1))
						_, code := fakeProviderB.ExchangeArgsForCall(0)
						Expect(code).To(Equal("some-code"))
					})

					Context("when the token is verified", func() {
						BeforeEach(func() {
							fakeProviderB.VerifyReturns(true, nil)
						})

						It("responds OK", func() {
							Expect(response.StatusCode).To(Equal(http.StatusOK))
							Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("ok\n")))
						})

						It("verifies using the provider's HTTP client", func() {
							Expect(fakeProviderB.ClientCallCount()).To(Equal(1))
							_, clientToken := fakeProviderB.ClientArgsForCall(0)
							Expect(clientToken).To(Equal(token))

							Expect(fakeProviderB.VerifyCallCount()).To(Equal(1))
							client := fakeProviderB.VerifyArgsForCall(0)
							Expect(client).To(Equal(httpClient))
						})

						It("sets the ATC-Authorization cookie to a signed token that expires in 1 day", func() {
							cookies := response.Cookies()
							Expect(cookies).To(HaveLen(1))

							cookie := cookies[0]
							Expect(cookie.Name).To(Equal(auth.CookieName))
							Expect(cookie.Expires).To(BeTemporally("~", time.Now().Add(auth.CookieAge), 5*time.Second))

							Expect(cookie.Value).To(MatchRegexp(`^Bearer .*`))

							token, err := jwt.Parse(strings.Replace(cookie.Value, "Bearer ", "", -1), keyFunc)
							Expect(err).ToNot(HaveOccurred())

							Expect(token.Claims["exp"]).To(BeNumerically("==", cookie.Expires.Unix()))
							Expect(token.Valid).To(BeTrue())
						})

						Context("when a redirect URI is in the state", func() {
							BeforeEach(func() {
								token := jwt.New(auth.SigningMethod)
								token.Claims["exp"] = time.Now().Add(time.Hour).Unix()
								token.Claims["redirect"] = "/"

								signedState, err := token.SignedString(signingKey)
								Expect(err).ToNot(HaveOccurred())

								request.URL.RawQuery = url.Values{
									"code":  {"some-code"},
									"state": {signedState},
								}.Encode()
							})

							It("redirects to it", func() {
								Expect(response.StatusCode).To(Equal(http.StatusOK))
								Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("main page\n")))
							})
						})

						Context("when a blank redirect URI is in the state", func() {
							BeforeEach(func() {
								token := jwt.New(auth.SigningMethod)
								token.Claims["exp"] = time.Now().Add(time.Hour).Unix()
								token.Claims["redirect"] = ""

								signedState, err := token.SignedString(signingKey)
								Expect(err).ToNot(HaveOccurred())

								request.URL.RawQuery = url.Values{
									"code":  {"some-code"},
									"state": {signedState},
								}.Encode()
							})

							It("does not redirect", func() {
								Expect(response.StatusCode).To(Equal(http.StatusOK))
								Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("ok\n")))
							})
						})
					})
				})

				Context("when the token is not verified", func() {
					BeforeEach(func() {
						fakeProviderB.VerifyReturns(false, nil)
					})

					It("returns Unauthorized", func() {
						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
					})

					It("does not set a cookie", func() {
						Expect(response.Cookies()).To(BeEmpty())
					})
				})

				Context("when the token cannot be verified", func() {
					BeforeEach(func() {
						fakeProviderB.VerifyReturns(false, errors.New("nope"))
					})

					It("returns Internal Server Error", func() {
						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
					})

					It("does not set a cookie", func() {
						Expect(response.Cookies()).To(BeEmpty())
					})
				})
			})

			Context("when the request's state is bogus", func() {
				BeforeEach(func() {
					request.URL.RawQuery = url.Values{
						"code":  {"some-code"},
						"state": {"bogus-state"},
					}.Encode()
				})

				It("returns Unauthorized", func() {
					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
				})

				It("does not set a cookie", func() {
					Expect(response.Cookies()).To(BeEmpty())
				})

				It("does not set exchange the token", func() {
					Expect(fakeProviderB.ExchangeCallCount()).To(Equal(0))
				})
			})

			Context("when the request's state has expired", func() {
				BeforeEach(func() {
					token := jwt.New(auth.SigningMethod)
					token.Claims["exp"] = time.Now().Add(-time.Hour).Unix()

					signedState, err := token.SignedString(signingKey)
					Expect(err).ToNot(HaveOccurred())

					request.URL.RawQuery = url.Values{
						"code":  {"some-code"},
						"state": {signedState},
					}.Encode()
				})

				It("returns Unauthorized", func() {
					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
				})

				It("does not set a cookie", func() {
					Expect(response.Cookies()).To(BeEmpty())
				})

				It("does not set exchange the token", func() {
					Expect(fakeProviderB.ExchangeCallCount()).To(Equal(0))
				})
			})
		})

		Context("to an unknown provider", func() {
			BeforeEach(func() {
				request.URL.Path = "/auth/bogus/callback"
			})

			It("returns Not Found", func() {
				Expect(response.StatusCode).To(Equal(http.StatusNotFound))
			})
		})
	})
})
<commit_msg>fix unit test<commit_after>package auth_test

import (
	"crypto/rand"
	"crypto/rsa"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"time"

	"golang.org/x/oauth2"

	"github.com/dgrijalva/jwt-go"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
	"github.com/pivotal-golang/lager/lagertest"

	"github.com/concourse/atc/auth"
	"github.com/concourse/atc/auth/fakes"
)

var _ = Describe("OAuthCallbackHandler", func() {
	var (
		fakeProviderA *fakes.FakeProvider
		fakeProviderB *fakes.FakeProvider

		signingKey *rsa.PrivateKey

		server *httptest.Server
		client *http.Client
	)

	BeforeEach(func() {
		fakeProviderA = new(fakes.FakeProvider)
		fakeProviderB = new(fakes.FakeProvider)

		var err error
		signingKey, err = rsa.GenerateKey(rand.Reader, 1024)
		Expect(err).ToNot(HaveOccurred())

		handler, err := auth.NewOAuthHandler(
			lagertest.NewTestLogger("test"),
			auth.Providers{
				"a": fakeProviderA,
				"b": fakeProviderB,
			},
			signingKey,
		)
		Expect(err).ToNot(HaveOccurred())

		mux := http.NewServeMux()
		mux.Handle("/auth/", handler)
		mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "main page")
		}))

		server = httptest.NewServer(mux)

		client = &http.Client{
			Transport: &http.Transport{},
		}
	})

	keyFunc := func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}

		return signingKey.Public(), nil
	}

	Describe("GET /auth/:provider/callback", func() {
		var redirectTarget *ghttp.Server
		var request *http.Request
		var response *http.Response

		BeforeEach(func() {
			redirectTarget = ghttp.NewServer()
			redirectTarget.RouteToHandler("GET", "/", ghttp.RespondWith(http.StatusOK, "sup"))

			var err error

			request, err = http.NewRequest("GET", server.URL, nil)
			Expect(err).NotTo(HaveOccurred())
		})

		JustBeforeEach(func() {
			var err error

			response, err = client.Do(request)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("to a known provider", func() {
			BeforeEach(func() {
				request.URL.Path = "/auth/b/callback"
			})

			Context("when the request's state is valid", func() {
				BeforeEach(func() {
					token := jwt.New(auth.SigningMethod)
					token.Claims["exp"] = time.Now().Add(time.Hour).Unix()

					signedState, err := token.SignedString(signingKey)
					Expect(err).ToNot(HaveOccurred())

					request.URL.RawQuery = url.Values{
						"code":  {"some-code"},
						"state": {signedState},
					}.Encode()
				})

				Context("when exchanging the token succeeds", func() {
					var token *oauth2.Token
					var httpClient *http.Client

					BeforeEach(func() {
						token = &oauth2.Token{AccessToken: "some-access-token"}
						httpClient = &http.Client{}

						fakeProviderB.ExchangeReturns(token, nil)
						fakeProviderB.ClientReturns(httpClient)
					})

					It("generated the OAuth token using the request's code", func() {
						Expect(fakeProviderB.ExchangeCallCount()).To(Equal(1))
						_, code := fakeProviderB.ExchangeArgsForCall(0)
						Expect(code).To(Equal("some-code"))
					})

					Context("when the token is verified", func() {
						BeforeEach(func() {
							fakeProviderB.VerifyReturns(true, nil)
						})

						It("responds OK", func() {
							Expect(response.StatusCode).To(Equal(http.StatusOK))
							Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("ok\n")))
						})

						It("verifies using the provider's HTTP client", func() {
							Expect(fakeProviderB.ClientCallCount()).To(Equal(1))
							_, clientToken := fakeProviderB.ClientArgsForCall(0)
							Expect(clientToken).To(Equal(token))

							Expect(fakeProviderB.VerifyCallCount()).To(Equal(1))
							_, client := fakeProviderB.VerifyArgsForCall(0)
							Expect(client).To(Equal(httpClient))
						})

						It("sets the ATC-Authorization cookie to a signed token that expires in 1 day", func() {
							cookies := response.Cookies()
							Expect(cookies).To(HaveLen(1))

							cookie := cookies[0]
							Expect(cookie.Name).To(Equal(auth.CookieName))
							Expect(cookie.Expires).To(BeTemporally("~", time.Now().Add(auth.CookieAge), 5*time.Second))

							Expect(cookie.Value).To(MatchRegexp(`^Bearer .*`))

							token, err := jwt.Parse(strings.Replace(cookie.Value, "Bearer ", "", -1), keyFunc)
							Expect(err).ToNot(HaveOccurred())

							Expect(token.Claims["exp"]).To(BeNumerically("==", cookie.Expires.Unix()))
							Expect(token.Valid).To(BeTrue())
						})

						Context("when a redirect URI is in the state", func() {
							BeforeEach(func() {
								token := jwt.New(auth.SigningMethod)
								token.Claims["exp"] = time.Now().Add(time.Hour).Unix()
								token.Claims["redirect"] = "/"

								signedState, err := token.SignedString(signingKey)
								Expect(err).ToNot(HaveOccurred())

								request.URL.RawQuery = url.Values{
									"code":  {"some-code"},
									"state": {signedState},
								}.Encode()
							})

							It("redirects to it", func() {
								Expect(response.StatusCode).To(Equal(http.StatusOK))
								Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("main page\n")))
							})
						})

						Context("when a blank redirect URI is in the state", func() {
							BeforeEach(func() {
								token := jwt.New(auth.SigningMethod)
								token.Claims["exp"] = time.Now().Add(time.Hour).Unix()
								token.Claims["redirect"] = ""

								signedState, err := token.SignedString(signingKey)
								Expect(err).ToNot(HaveOccurred())

								request.URL.RawQuery = url.Values{
									"code":  {"some-code"},
									"state": {signedState},
								}.Encode()
							})

							It("does not redirect", func() {
								Expect(response.StatusCode).To(Equal(http.StatusOK))
								Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("ok\n")))
							})
						})
					})
				})

				Context("when the token is not verified", func() {
					BeforeEach(func() {
						fakeProviderB.VerifyReturns(false, nil)
					})

					It("returns Unauthorized", func() {
						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
					})

					It("does not set a cookie", func() {
						Expect(response.Cookies()).To(BeEmpty())
					})
				})

				Context("when the token cannot be verified", func() {
					BeforeEach(func() {
						fakeProviderB.VerifyReturns(false, errors.New("nope"))
					})

					It("returns Internal Server Error", func() {
						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
					})

					It("does not set a cookie", func() {
						Expect(response.Cookies()).To(BeEmpty())
					})
				})
			})

			Context("when the request's state is bogus", func() {
				BeforeEach(func() {
					request.URL.RawQuery = url.Values{
						"code":  {"some-code"},
						"state": {"bogus-state"},
					}.Encode()
				})

				It("returns Unauthorized", func() {
					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
				})

				It("does not set a cookie", func() {
					Expect(response.Cookies()).To(BeEmpty())
				})

				It("does not set exchange the token", func() {
					Expect(fakeProviderB.ExchangeCallCount()).To(Equal(0))
				})
			})

			Context("when the request's state has expired", func() {
				BeforeEach(func() {
					token := jwt.New(auth.SigningMethod)
					token.Claims["exp"] = time.Now().Add(-time.Hour).Unix()

					signedState, err := token.SignedString(signingKey)
					Expect(err).ToNot(HaveOccurred())

					request.URL.RawQuery = url.Values{
						"code":  {"some-code"},
						"state": {signedState},
					}.Encode()
				})

				It("returns Unauthorized", func() {
					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
				})

				It("does not set a cookie", func() {
					Expect(response.Cookies()).To(BeEmpty())
				})

				It("does not set exchange the token", func() {
					Expect(fakeProviderB.ExchangeCallCount()).To(Equal(0))
				})
			})
		})

		Context("to an unknown provider", func() {
			BeforeEach(func() {
				request.URL.Path = "/auth/bogus/callback"
			})

			It("returns Not Found", func() {
				Expect(response.StatusCode).To(Equal(http.StatusNotFound))
			})
		})
	})
})
<|endoftext|>