<commit_before>package cli

import (
	"fmt"
	"github.com/jkomoros/boardgame"
	"github.com/jroimartin/gocui"
	"reflect"
	"strings"
	"unicode"
)

type inputMode interface {
	//enterMode enters the specified mode, doing any init necessary. Do not
	//register keybindings (unless you're doing mouse)
	enterMode()

	//handleInput is where all input is routed.
	handleInput(key gocui.Key, ch rune, mode gocui.Modifier)
	//statusLine returns the text that should be displayed in the status line.
	statusLine() string
	//Whether or not the overlay should be visible
	showOverlay() bool
	//Returns overlay content
	overlayContent() *overlayContent
	//Which cell in overlayContent is currently selected. -1, -1 is "none"
	overlaySelectedCell() (row, col int)

	//Returns the title for overlay
	overlayTitle() string
}

type overlayContent [][]string

type columnAlignment int

const (
	alignLeft columnAlignment = iota
	alignRight
)

type modeBase struct {
	c *Controller
}

type modeNormal struct {
	modeBase
}

type modePickMove struct {
	modeBase
	content     *overlayContent
	currentLine int
}

type modeEditMove struct {
	modeBase
	content *overlayContent
	move    boardgame.Move
}

//Valid returns true if each row has the same number of columns
func (o overlayContent) Valid() bool {
	if len(o) < 1 {
		return true
	}
	numColumns := len(o[0])
	for _, row := range o {
		if len(row) != numColumns {
			return false
		}
	}
	return true
}

//Aligned returns true if all cells in a column have the same length
func (o overlayContent) Aligned() bool {
	if !o.Valid() {
		return false
	}
	if len(o) == 0 {
		return true
	}
	numColumns := len(o[0])
	for c := 0; c < numColumns; c++ {
		colLength := len(o[0][c])
		for _, row := range o {
			if len(row[c]) != colLength {
				return false
			}
		}
	}
	return true
}

//ColumnWidths verifies that evertyhing is aligned and then returns the column sizes
func (o *overlayContent) ColumnWidths() []int {
	if !o.Aligned() {
		o.DefaultPad()
	}

	row := (*o)[0]

	result := make([]int, len(row))

	for c := 0; c < len(row); c++ {
		result[c] = len(row[c])
	}

	return result
}

//DefaultPad goes through and makes sure that every column has the same
//lenght. Defaults to left-aligned for all.
func (o *overlayContent) DefaultPad() {
	if o.Aligned() {
		return
	}

	numColumns := len((*o)[0])

	alignments := make([]columnAlignment, numColumns)

	for i := 0; i < numColumns; i++ {
		alignments[i] = alignLeft
	}

	o.PadWithAlignment(alignments...)
}

//Pad verifies that each column in the overlayContent is the same size, with
//each column left or right aligned.
func (o *overlayContent) PadWithAlignment(alignments ...columnAlignment) {
	//TODO: implement
	if o.Aligned() {
		return
	}

	//Make sure the length of alignments lines up with Aligned.
	if len((*o)[0]) != len(alignments) {
		return
	}

	for c, alignment := range alignments {

		maxColLength := len((*o)[0][c])
		for r := 1; r < len((*o)); r++ {
			if len((*o)[r][c]) > maxColLength {
				maxColLength = len((*o)[r][c])
			}
		}

		for r, line := range *o {
			length := len(line[c])

			lengthToPad := maxColLength - length

			if alignment == alignLeft {

				(*o)[r][c] = (*o)[r][c] + strings.Repeat(" ", lengthToPad)

			} else if alignment == alignRight {
				(*o)[r][c] = strings.Repeat(" ", lengthToPad) + (*o)[r][c]
			}
		}
	}

}

//String returns overlay content rendered as a simple string with lines separated by \n
func (o *overlayContent) String() string {

	if !o.Aligned() {
		o.DefaultPad()
	}

	lines := make([]string, len(*o))

	for i, line := range *o {
		lines[i] = strings.Join(line, "")
	}

	return strings.Join(lines, "\n")

}

func newModeEditMove(c *Controller, move boardgame.Move) *modeEditMove {
	return &modeEditMove{
		modeBase{
			c,
		},
		nil,
		move,
	}
}

func (m *modeBase) enterMode() {

	m.c.gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
		return gocui.ErrQuit
	})

}

func (m *modeBase) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	//Currently there arent any base handlers. Quitting via Ctrl-C is handled
	//via gocui key bindings so we can exit cleanly.
}

func (m *modeBase) statusLine() string {
	return "Type 't' to toggle json or render output, 'm' to propose a move, Ctrl-C to quit"
}

func (m *modeBase) showOverlay() bool {
	return false
}

func (m *modeBase) overlayContent() *overlayContent {
	return nil
}

func (m *modeBase) overlayTitle() string {
	return ""
}

func (m *modeBase) overlaySelectedCell() (row, col int) {
	return -1, -1
}

func newModeNormal(c *Controller) *modeNormal {
	return &modeNormal{modeBase{c}}
}

func (m *modeNormal) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	handled := false
	switch key {
	case gocui.KeyArrowUp:
		m.c.ScrollUp()
		handled = true
	case gocui.KeyArrowDown:
		m.c.ScrollDown()
		handled = true
	}
	if handled {
		return
	}
	switch ch {
	case 't':
		m.c.ToggleRender()
		handled = true
	case 'm':
		m.c.StartProposingMove()
		handled = true
	}
	if handled {
		return
	}
	m.modeBase.handleInput(key, ch, mode)
}

func (m *modeNormal) enterMode() {

	c := m.c

	m.modeBase.enterMode()

	g := c.gui

	//TODO: can we skip setting key bindings for mouse?

	if err := g.SetKeybinding("", gocui.MouseWheelUp, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
		c.ScrollUp()
		return nil
	}); err != nil {
		panic(err)
	}

	if err := g.SetKeybinding("", gocui.MouseWheelDown, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
		c.ScrollDown()
		return nil
	}); err != nil {
		panic(err)
	}

}

func newModePickMove(c *Controller) *modePickMove {
	return &modePickMove{
		modeBase{
			c,
		},
		nil,
		0,
	}
}

func (m *modePickMove) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	handled := false
	switch key {
	case gocui.KeyArrowUp:
		m.MoveSelectionUp()
		handled = true
	case gocui.KeyArrowDown:
		m.MoveSelectionDown()
		handled = true
	case gocui.KeyEsc:
		m.c.CancelMode()
		handled = true
	case gocui.KeyEnter:
		m.PickCurrentlySelectedMoveToEdit()
	}
	if handled {
		return
	}
	m.modeBase.handleInput(key, ch, mode)
}

func (m *modePickMove) enterMode() {

	m.modeBase.enterMode()

}

func (m *modePickMove) statusLine() string {
	return "'Enter' to pick a move to edit. 'Esc' to cancel"
}

func (m *modePickMove) showOverlay() bool {
	return true
}

func (m *modePickMove) overlayContent() *overlayContent {

	if m.content == nil {

		moves := m.c.renderMoves()

		result := make(overlayContent, len(moves))

		for i := 0; i < len(moves); i++ {
			result[i] = []string{moves[i]}
		}

		m.content = &result

	}

	return m.content

}

func (m *modePickMove) overlayTitle() string {
	return "Pick Move To Propose"
}

func (m *modePickMove) overlaySelectedCell() (row, col int) {
	return m.currentLine, 0
}

func (m *modePickMove) MoveSelectionUp() {

	if m.currentLine == 0 {
		return
	}
	m.currentLine--
}

func (m *modePickMove) MoveSelectionDown() {

	if m.content == nil {
		return
	}

	if m.currentLine+1 >= len(*m.content) {
		return
	}
	m.currentLine++
}

func (m *modePickMove) PickCurrentlySelectedMoveToEdit() {

	move := m.c.game.Moves()[m.currentLine]

	m.c.PickMoveToEdit(move)
}

func (m *modeEditMove) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	handled := false
	switch key {
	case gocui.KeyEsc:
		//TODO: should this esc handler just be in baseMode?
		m.c.CancelMode()
		handled = true
	}
	if handled {
		return
	}
	m.modeBase.handleInput(key, ch, mode)
}

func (m *modeEditMove) enterMode() {
	m.modeBase.enterMode()

	//TODO:should the esc handler just be in baseMode?

}

func (m *modeEditMove) statusLine() string {
	return "this is where you edit the move, I guess. Or 'Esc' to cancel. I don't care."
}

func (m *modeEditMove) showOverlay() bool {
	return true
}

func moveFieldNameShouldBeIncluded(name string) bool {
	if len(name) < 1 {
		return false
	}

	firstChar := []rune(name)[0]

	if firstChar != unicode.ToUpper(firstChar) {
		//It was not upper case, thus private, thus should not be included.
		return false
	}

	return true
}

func (m *modeEditMove) overlayContent() *overlayContent {

	if m.content == nil {

		var result overlayContent

		s := reflect.ValueOf(m.move).Elem()
		typeOfT := s.Type()
		for i := 0; i < s.NumField(); i++ {
			f := s.Field(i)
			fieldName := typeOfT.Field(i).Name
			if !moveFieldNameShouldBeIncluded(fieldName) {
				continue
			}
			result = append(result, []string{fmt.Sprintf("%s (%s)", fieldName, f.Type()), ":", fmt.Sprintf("%v", f.Interface())})
		}

		if len(result) == 0 {
			//No fields!
			result = overlayContent{[]string{"No fields to modify"}}
		}

		m.content = &result

		m.content.PadWithAlignment(alignRight, alignLeft, alignLeft)
	}

	return m.content

}

func (m *modeEditMove) overlayTitle() string {
	return "Editing Move"
}
<commit_msg>Make it so in modeEditMove up/down select different cells. Part of #26.<commit_after>package cli

import (
	"fmt"
	"github.com/jkomoros/boardgame"
	"github.com/jroimartin/gocui"
	"reflect"
	"strings"
	"unicode"
)

type inputMode interface {
	//enterMode enters the specified mode, doing any init necessary. Do not
	//register keybindings (unless you're doing mouse)
	enterMode()

	//handleInput is where all input is routed.
	handleInput(key gocui.Key, ch rune, mode gocui.Modifier)
	//statusLine returns the text that should be displayed in the status line.
	statusLine() string
	//Whether or not the overlay should be visible
	showOverlay() bool
	//Returns overlay content
	overlayContent() *overlayContent
	//Which cell in overlayContent is currently selected. -1, -1 is "none"
	overlaySelectedCell() (row, col int)

	//Returns the title for overlay
	overlayTitle() string
}

type overlayContent [][]string

type columnAlignment int

const (
	alignLeft columnAlignment = iota
	alignRight
)

type modeBase struct {
	c *Controller
}

type modeNormal struct {
	modeBase
}

type modePickMove struct {
	modeBase
	content     *overlayContent
	currentLine int
}

type modeEditMove struct {
	modeBase
	content     *overlayContent
	move        boardgame.Move
	currentLine int
}

//Valid returns true if each row has the same number of columns
func (o overlayContent) Valid() bool {
	if len(o) < 1 {
		return true
	}
	numColumns := len(o[0])
	for _, row := range o {
		if len(row) != numColumns {
			return false
		}
	}
	return true
}

//Aligned returns true if all cells in a column have the same length
func (o overlayContent) Aligned() bool {
	if !o.Valid() {
		return false
	}
	if len(o) == 0 {
		return true
	}
	numColumns := len(o[0])
	for c := 0; c < numColumns; c++ {
		colLength := len(o[0][c])
		for _, row := range o {
			if len(row[c]) != colLength {
				return false
			}
		}
	}
	return true
}

//ColumnWidths verifies that evertyhing is aligned and then returns the column sizes
func (o *overlayContent) ColumnWidths() []int {
	if !o.Aligned() {
		o.DefaultPad()
	}

	row := (*o)[0]

	result := make([]int, len(row))

	for c := 0; c < len(row); c++ {
		result[c] = len(row[c])
	}

	return result
}

//DefaultPad goes through and makes sure that every column has the same
//lenght. Defaults to left-aligned for all.
func (o *overlayContent) DefaultPad() {
	if o.Aligned() {
		return
	}

	numColumns := len((*o)[0])

	alignments := make([]columnAlignment, numColumns)

	for i := 0; i < numColumns; i++ {
		alignments[i] = alignLeft
	}

	o.PadWithAlignment(alignments...)
}

//Pad verifies that each column in the overlayContent is the same size, with
//each column left or right aligned.
func (o *overlayContent) PadWithAlignment(alignments ...columnAlignment) {
	//TODO: implement
	if o.Aligned() {
		return
	}

	//Make sure the length of alignments lines up with Aligned.
	if len((*o)[0]) != len(alignments) {
		return
	}

	for c, alignment := range alignments {

		maxColLength := len((*o)[0][c])
		for r := 1; r < len((*o)); r++ {
			if len((*o)[r][c]) > maxColLength {
				maxColLength = len((*o)[r][c])
			}
		}

		for r, line := range *o {
			length := len(line[c])

			lengthToPad := maxColLength - length

			if alignment == alignLeft {

				(*o)[r][c] = (*o)[r][c] + strings.Repeat(" ", lengthToPad)

			} else if alignment == alignRight {
				(*o)[r][c] = strings.Repeat(" ", lengthToPad) + (*o)[r][c]
			}
		}
	}

}

//String returns overlay content rendered as a simple string with lines separated by \n
func (o *overlayContent) String() string {

	if !o.Aligned() {
		o.DefaultPad()
	}

	lines := make([]string, len(*o))

	for i, line := range *o {
		lines[i] = strings.Join(line, "")
	}

	return strings.Join(lines, "\n")

}

func newModeEditMove(c *Controller, move boardgame.Move) *modeEditMove {
	return &modeEditMove{
		modeBase{
			c,
		},
		nil,
		move,
		0,
	}
}

func (m *modeBase) enterMode() {

	m.c.gui.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
		return gocui.ErrQuit
	})

}

func (m *modeBase) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	//Currently there arent any base handlers. Quitting via Ctrl-C is handled
	//via gocui key bindings so we can exit cleanly.
}

func (m *modeBase) statusLine() string {
	return "Type 't' to toggle json or render output, 'm' to propose a move, Ctrl-C to quit"
}

func (m *modeBase) showOverlay() bool {
	return false
}

func (m *modeBase) overlayContent() *overlayContent {
	return nil
}

func (m *modeBase) overlayTitle() string {
	return ""
}

func (m *modeBase) overlaySelectedCell() (row, col int) {
	return -1, -1
}

func newModeNormal(c *Controller) *modeNormal {
	return &modeNormal{modeBase{c}}
}

func (m *modeNormal) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	handled := false
	switch key {
	case gocui.KeyArrowUp:
		m.c.ScrollUp()
		handled = true
	case gocui.KeyArrowDown:
		m.c.ScrollDown()
		handled = true
	}
	if handled {
		return
	}
	switch ch {
	case 't':
		m.c.ToggleRender()
		handled = true
	case 'm':
		m.c.StartProposingMove()
		handled = true
	}
	if handled {
		return
	}
	m.modeBase.handleInput(key, ch, mode)
}

func (m *modeNormal) enterMode() {

	c := m.c

	m.modeBase.enterMode()

	g := c.gui

	//TODO: can we skip setting key bindings for mouse?

	if err := g.SetKeybinding("", gocui.MouseWheelUp, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
		c.ScrollUp()
		return nil
	}); err != nil {
		panic(err)
	}

	if err := g.SetKeybinding("", gocui.MouseWheelDown, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
		c.ScrollDown()
		return nil
	}); err != nil {
		panic(err)
	}

}

func newModePickMove(c *Controller) *modePickMove {
	return &modePickMove{
		modeBase{
			c,
		},
		nil,
		0,
	}
}

func (m *modePickMove) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	handled := false
	switch key {
	case gocui.KeyArrowUp:
		m.MoveSelectionUp()
		handled = true
	case gocui.KeyArrowDown:
		m.MoveSelectionDown()
		handled = true
	case gocui.KeyEsc:
		m.c.CancelMode()
		handled = true
	case gocui.KeyEnter:
		m.PickCurrentlySelectedMoveToEdit()
	}
	if handled {
		return
	}
	m.modeBase.handleInput(key, ch, mode)
}

func (m *modePickMove) enterMode() {

	m.modeBase.enterMode()

}

func (m *modePickMove) statusLine() string {
	return "'Enter' to pick a move to edit. 'Esc' to cancel"
}

func (m *modePickMove) showOverlay() bool {
	return true
}

func (m *modePickMove) overlayContent() *overlayContent {

	if m.content == nil {

		moves := m.c.renderMoves()

		result := make(overlayContent, len(moves))

		for i := 0; i < len(moves); i++ {
			result[i] = []string{moves[i]}
		}

		m.content = &result

	}

	return m.content

}

func (m *modePickMove) overlayTitle() string {
	return "Pick Move To Propose"
}

func (m *modePickMove) overlaySelectedCell() (row, col int) {
	return m.currentLine, 0
}

func (m *modePickMove) MoveSelectionUp() {

	if m.currentLine == 0 {
		return
	}
	m.currentLine--
}

func (m *modePickMove) MoveSelectionDown() {

	if m.content == nil {
		return
	}

	if m.currentLine+1 >= len(*m.content) {
		return
	}
	m.currentLine++
}

func (m *modePickMove) PickCurrentlySelectedMoveToEdit() {

	move := m.c.game.Moves()[m.currentLine]

	m.c.PickMoveToEdit(move)
}

func (m *modeEditMove) handleInput(key gocui.Key, ch rune, mode gocui.Modifier) {
	handled := false
	switch key {
	case gocui.KeyEsc:
		//TODO: should this esc handler just be in baseMode?
		m.c.CancelMode()
		handled = true
	case gocui.KeyArrowUp:
		//TODO: KeyArrowUp and KeyArrowDown should just be handled in a sub-mode
		m.MoveSelectionUp()
		handled = true
	case gocui.KeyArrowDown:
		m.MoveSelectionDown()
		handled = true
	}
	if handled {
		return
	}
	m.modeBase.handleInput(key, ch, mode)
}

func (m *modeEditMove) enterMode() {
	m.modeBase.enterMode()

	//TODO:should the esc handler just be in baseMode?

}

func (m *modeEditMove) statusLine() string {
	return "this is where you edit the move, I guess. Or 'Esc' to cancel. I don't care."
}

func (m *modeEditMove) showOverlay() bool {
	return true
}

func moveFieldNameShouldBeIncluded(name string) bool {
	if len(name) < 1 {
		return false
	}

	firstChar := []rune(name)[0]

	if firstChar != unicode.ToUpper(firstChar) {
		//It was not upper case, thus private, thus should not be included.
		return false
	}

	return true
}

func (m *modeEditMove) overlayContent() *overlayContent {

	if m.content == nil {

		var result overlayContent

		s := reflect.ValueOf(m.move).Elem()
		typeOfT := s.Type()
		for i := 0; i < s.NumField(); i++ {
			f := s.Field(i)
			fieldName := typeOfT.Field(i).Name
			if !moveFieldNameShouldBeIncluded(fieldName) {
				continue
			}
			result = append(result, []string{fmt.Sprintf("%s (%s)", fieldName, f.Type()), ":", fmt.Sprintf("%v", f.Interface())})
		}

		if len(result) == 0 {
			//No fields!
			result = overlayContent{[]string{"No fields to modify"}}
		}

		m.content = &result

		m.content.PadWithAlignment(alignRight, alignLeft, alignLeft)
	}

	return m.content

}

//TODO: pop these out into a base class
func (m *modeEditMove) MoveSelectionUp() {

	if m.currentLine == 0 {
		return
	}
	m.currentLine--
}

func (m *modeEditMove) MoveSelectionDown() {

	if m.content == nil {
		return
	}

	if m.currentLine+1 >= len(*m.content) {
		return
	}
	m.currentLine++
}

func (m *modeEditMove) overlaySelectedCell() (row, col int) {
	return m.currentLine, 2
}

func (m *modeEditMove) overlayTitle() string {
	return "Editing Move"
}
<|endoftext|>


<commit_before>package boardgame

import (
	"reflect"
	"sort"
	"testing"
)

func TestComponentChest(t *testing.T) {

	chest := &ComponentChest{}

	if chest.DeckNames() != nil {
		t.Error("We got a deck names array before we'd added anything")
	}

	//TODO: make real decks here
	deckOne := &Deck{}

	componentOne := &Component{
		Values: &testingComponent{
			"foo",
			1,
		},
	}

	deckOne.AddComponent(componentOne)

	componentTwo := &Component{
		Values: &testingComponent{
			"bar",
			2,
		},
	}

	deckOne.AddComponent(componentTwo)

	if deckOne.Components() != nil {
		t.Error("We got non-nil components before it was added to the chest")
	}

	chest.AddDeck("test", deckOne)

	if !reflect.DeepEqual(deckOne.Components(), []*Component{componentOne, componentTwo}) {
		t.Error("Deck gave back wrong items after being added to chest")
	}

	deckOne.AddComponent(&Component{
		Values: &testingComponent{
			"illegal",
			-1,
		},
	})

	if !reflect.DeepEqual(deckOne.Components(), []*Component{componentOne, componentTwo}) {
		t.Error("Deck allowed itself to be mutated after it was added to chest")
	}

	if chest.DeckNames() != nil {
		t.Error("We got decknames before we called freeze")
	}

	if chest.Deck("test") != nil {
		t.Error("We got a deck back before freeze was called")
	}

	deckTwo := &Deck{}

	deckTwo.AddComponent(&Component{
		Values: &testingComponent{
			"another",
			3,
		},
	})

	chest.AddDeck("other", deckTwo)

	chest.Finish()

	chest.AddDeck("shoulfail", deckOne)

	if chest.decks["shouldfail"] != nil {
		t.Fatal("We were able to add a deck after freezing")
	}

	sortedDeckNames := chest.DeckNames()

	sort.Strings(sortedDeckNames)

	expectedDeckNames := []string{"other", "test"}

	if !reflect.DeepEqual(sortedDeckNames, expectedDeckNames) {
		t.Error("Got unexpected decknames. got", sortedDeckNames, "wanted", expectedDeckNames)
	}

	if chest.Deck("test") != deckOne {
		t.Error("Got wrong value for deck one. Got", chest.Deck("test"), "wanted", deckOne)
	}

	if chest.Deck("other") != deckTwo {
		t.Error("Got wrong value for deck two. Got", chest.Deck("other"), "wanted", deckTwo)
	}

	if deckOne.name != "test" {
		t.Error("DeckOne didn't have its name set when added to the chest. Got", deckOne.name, "wanted test")
	}

	if deckTwo.name != "other" {
		t.Error("DeckTwo didn't have its name set when added to the chest. Got", deckTwo.name, "wanted other")
	}

	for i, c := range deckOne.Components() {
		if c.Address.Deck != deckOne.name {
			t.Error("At position", i, "deck name was not set correctly in component. Got", c.Address.Deck, "wanted", deckOne.name)
		}
		if c.Address.Index != i {
			t.Error("At position", i, "index was not set correctly in component. Got", c.Address.Index, "wanted", i)
		}
	}

}
<commit_msg>Fixed a typo in a test<commit_after>package boardgame

import (
	"reflect"
	"sort"
	"testing"
)

func TestComponentChest(t *testing.T) {

	chest := &ComponentChest{}

	if chest.DeckNames() != nil {
		t.Error("We got a deck names array before we'd added anything")
	}

	//TODO: make real decks here
	deckOne := &Deck{}

	componentOne := &Component{
		Values: &testingComponent{
			"foo",
			1,
		},
	}

	deckOne.AddComponent(componentOne)

	componentTwo := &Component{
		Values: &testingComponent{
			"bar",
			2,
		},
	}

	deckOne.AddComponent(componentTwo)

	if deckOne.Components() != nil {
		t.Error("We got non-nil components before it was added to the chest")
	}

	chest.AddDeck("test", deckOne)

	if !reflect.DeepEqual(deckOne.Components(), []*Component{componentOne, componentTwo}) {
		t.Error("Deck gave back wrong items after being added to chest")
	}

	deckOne.AddComponent(&Component{
		Values: &testingComponent{
			"illegal",
			-1,
		},
	})

	if !reflect.DeepEqual(deckOne.Components(), []*Component{componentOne, componentTwo}) {
		t.Error("Deck allowed itself to be mutated after it was added to chest")
	}

	if chest.DeckNames() != nil {
		t.Error("We got decknames before we called freeze")
	}

	if chest.Deck("test") != nil {
		t.Error("We got a deck back before freeze was called")
	}

	deckTwo := &Deck{}

	deckTwo.AddComponent(&Component{
		Values: &testingComponent{
			"another",
			3,
		},
	})

	chest.AddDeck("other", deckTwo)

	chest.Finish()

	chest.AddDeck("shouldfail", deckOne)

	if chest.decks["shouldfail"] != nil {
		t.Fatal("We were able to add a deck after freezing")
	}

	sortedDeckNames := chest.DeckNames()

	sort.Strings(sortedDeckNames)

	expectedDeckNames := []string{"other", "test"}

	if !reflect.DeepEqual(sortedDeckNames, expectedDeckNames) {
		t.Error("Got unexpected decknames. got", sortedDeckNames, "wanted", expectedDeckNames)
	}

	if chest.Deck("test") != deckOne {
		t.Error("Got wrong value for deck one. Got", chest.Deck("test"), "wanted", deckOne)
	}

	if chest.Deck("other") != deckTwo {
		t.Error("Got wrong value for deck two. Got", chest.Deck("other"), "wanted", deckTwo)
	}

	if deckOne.name != "test" {
		t.Error("DeckOne didn't have its name set when added to the chest. Got", deckOne.name, "wanted test")
	}

	if deckTwo.name != "other" {
		t.Error("DeckTwo didn't have its name set when added to the chest. Got", deckTwo.name, "wanted other")
	}

	for i, c := range deckOne.Components() {
		if c.Address.Deck != deckOne.name {
			t.Error("At position", i, "deck name was not set correctly in component. Got", c.Address.Deck, "wanted", deckOne.name)
		}
		if c.Address.Index != i {
			t.Error("At position", i, "index was not set correctly in component. Got", c.Address.Index, "wanted", i)
		}
	}

}
<|endoftext|>


<commit_before>package commands

import (
	"strings"

	"github.com/Shaked/getpocket/auth"
	"github.com/Shaked/getpocket/utils"
)

var (
	URLs = map[string]string{
		"Add":      "https://getpocket.com/v3/add",
		"Retrieve": "https://getpocket.com/v3/get",
		"Modify":   "https://getpocket.com/v3/send",
	}
)

type Item struct {
	Id             string                `json:"item_id"`
	NormalURL      string                `json:"normal_url"`
	ResolvedId     string                `json:"resolved_id"`
	ResolvedURL    string                `json:"resolved_url"`
	DomainId       string                `json:"domain_id"`
	OriginDomainId string                `json:"origin_domain_id"`
	ResponseCode   string                `json:"response_code"`
	MimeType       string                `json:"mime_type"`
	ContentLength  string                `json:"content_length"`
	Encoding       string                `json:"encoding"`
	DateResolved   string                `json:"date_resolved"`
	DatePublished  string                `json:"date_published"`
	Title          string                `json:"title"`
	Excerpt        string                `json:"excerpt"`
	WordCount      string                `json:"word_count"`
	HasImage       string                `json:"has_image"`
	HasVideo       string                `json:"has_video"`
	IsIndex        string                `json:"is_index"`
	Authors        map[string]ItemAuthor `json:"authors"`
	Images         map[string]ItemImage  `json:"images"`
	Videos         map[string]ItemVideo  `json:"videos"`
}

type ItemImage struct {
	Id      string `json:"item_id"`
	ImageId string `json:"image_id"`
	Src     string `json:"src"`
	Width   string `json:"width"`
	Height  string `json:"height"`
	Credit  string `json:"credit"`
}

type ItemVideo struct {
	Id      string `json:"item_id"`
	VideoId string `json:"video_id"`
	Src     string `json:"src"`
	Width   string `json:"width"`
	Height  string `json:"height"`
	Type    string `json:"credit"`
	Vid     string `json:"vid"`
	Length  string `json:"length"`
}

type ItemAuthor struct {
	Id   string `json:"author_id"`
	Name string `json:"name"`
	URL  string `json:"url"`
}

type Response interface{}
type Executable interface {
	exec(user *auth.User, consumerKey string, request utils.HttpRequest) (Response, error)
}

type Commands struct {
	user        *auth.User
	consumerKey string
	request     utils.HttpRequest
}

func New(user *auth.User, consumerKey string) *Commands {
	request := utils.NewRequest()
	return &Commands{
		user:        user,
		request:     request,
		consumerKey: consumerKey,
	}
}

func (c *Commands) Exec(command Executable) (Response, error) {
	resp, err := command.exec(c.user, c.consumerKey, c.request)
	return resp, err
}

//get pocket returns an empty array instead of an empty object.
func fixJSONArrayToObject(body []byte) []byte {
	newStr := string(body)
	newStr = strings.Replace(newStr, `"videos":[]`, `"videos":{}`, -1)
	newStr = strings.Replace(newStr, `"images":[]`, `"images":{}`, -1)
	newStr = strings.Replace(newStr, `"authors":[]`, `"authors":{}`, -1)
	newStr = strings.Replace(newStr, `"list":[]`, `"list":{}`, -1)
	return []byte(newStr)
}
<commit_msg>fix json key names of url and title.<commit_after>package commands

import (
	"strings"

	"github.com/Shaked/getpocket/auth"
	"github.com/Shaked/getpocket/utils"
)

var (
	URLs = map[string]string{
		"Add":      "https://getpocket.com/v3/add",
		"Retrieve": "https://getpocket.com/v3/get",
		"Modify":   "https://getpocket.com/v3/send",
	}
)

type Item struct {
	Id             string                `json:"item_id"`
	GivenURL       string                `json:"given_url"`
	ResolvedId     string                `json:"resolved_id"`
	ResolvedURL    string                `json:"resolved_url"`
	DomainId       string                `json:"domain_id"`
	OriginDomainId string                `json:"origin_domain_id"`
	ResponseCode   string                `json:"response_code"`
	MimeType       string                `json:"mime_type"`
	ContentLength  string                `json:"content_length"`
	Encoding       string                `json:"encoding"`
	DateResolved   string                `json:"date_resolved"`
	DatePublished  string                `json:"date_published"`
	GivenTitle     string                `json:"given_title"`
	Excerpt        string                `json:"excerpt"`
	WordCount      string                `json:"word_count"`
	HasImage       string                `json:"has_image"`
	HasVideo       string                `json:"has_video"`
	IsIndex        string                `json:"is_index"`
	Authors        map[string]ItemAuthor `json:"authors"`
	Images         map[string]ItemImage  `json:"images"`
	Videos         map[string]ItemVideo  `json:"videos"`
}

type ItemImage struct {
	Id      string `json:"item_id"`
	ImageId string `json:"image_id"`
	Src     string `json:"src"`
	Width   string `json:"width"`
	Height  string `json:"height"`
	Credit  string `json:"credit"`
}

type ItemVideo struct {
	Id      string `json:"item_id"`
	VideoId string `json:"video_id"`
	Src     string `json:"src"`
	Width   string `json:"width"`
	Height  string `json:"height"`
	Type    string `json:"credit"`
	Vid     string `json:"vid"`
	Length  string `json:"length"`
}

type ItemAuthor struct {
	Id   string `json:"author_id"`
	Name string `json:"name"`
	URL  string `json:"url"`
}

type Response interface{}
type Executable interface {
	exec(user *auth.User, consumerKey string, request utils.HttpRequest) (Response, error)
}

type Commands struct {
	user        *auth.User
	consumerKey string
	request     utils.HttpRequest
}

func New(user *auth.User, consumerKey string) *Commands {
	request := utils.NewRequest()
	return &Commands{
		user:        user,
		request:     request,
		consumerKey: consumerKey,
	}
}

func (c *Commands) Exec(command Executable) (Response, error) {
	resp, err := command.exec(c.user, c.consumerKey, c.request)
	return resp, err
}

//get pocket returns an empty array instead of an empty object.
func fixJSONArrayToObject(body []byte) []byte {
	newStr := string(body)
	newStr = strings.Replace(newStr, `"videos":[]`, `"videos":{}`, -1)
	newStr = strings.Replace(newStr, `"images":[]`, `"images":{}`, -1)
	newStr = strings.Replace(newStr, `"authors":[]`, `"authors":{}`, -1)
	newStr = strings.Replace(newStr, `"list":[]`, `"list":{}`, -1)
	return []byte(newStr)
}
<|endoftext|>


<commit_before>// Package cluster provides an elastic peer discovery and gossip layer.
//
// Ingest and store instances join the same cluster and know about each other.
// Store instances consume segments from each ingest instance, and broadcast
// queries to each store instance. In the future, ingest instances will share
// load information to potentially refuse connections and balance writes.
package cluster

import (
	"encoding/json"
	"io/ioutil"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	level "github.com/go-kit/kit/log/experimental_level"
	"github.com/hashicorp/memberlist"
	"github.com/pborman/uuid"
	"github.com/prometheus/client_golang/prometheus"
)

// Peer represents this node in the cluster.
type Peer struct {
	ml *memberlist.Memberlist
	d  *delegate
}

// PeerType enumerates the two types of nodes in the cluster.
// A peer may be one or the other.
type PeerType string

const (
	// PeerTypeIngest serves the ingest API.
	PeerTypeIngest PeerType = "ingest"

	// PeerTypeStore serves the store API.
	PeerTypeStore = "store"

	// PeerTypeIngestStore serves both ingest and store APIs.
	PeerTypeIngestStore = "ingeststore"
)

// NewPeer creates or joins a cluster with the existing peers.
// We will listen for cluster communications on the given addr:port.
// We advertise a PeerType HTTP API, reachable on apiPort.
func NewPeer(addr string, port int, existing []string, t PeerType, apiPort int, delegateInvocations *prometheus.CounterVec, logger log.Logger) (*Peer, error) {
	d := newDelegate(delegateInvocations, logger)
	conf := memberlistConfig(addr, port, d, d)
	ml, err := memberlist.Create(conf)
	if err != nil {
		return nil, err
	}
	d.init(conf.Name, t, ml.LocalNode().Addr.String(), apiPort, ml.NumMembers)
	n, err := ml.Join(existing)
	if err != nil {
		return nil, err
	}
	level.Debug(logger).Log("Join", n)
	return &Peer{
		ml: ml,
		d:  d,
	}, nil
}

// Leave the cluster, waiting up to timeout.
func (p *Peer) Leave(timeout time.Duration) error {
	return p.ml.Leave(timeout)
}

// Current API host:ports for the given type of node.
func (p *Peer) Current(t PeerType) []string {
	return p.d.current(t)
}

// Name returns the unique ID of this peer in the cluster.
func (p *Peer) Name() string {
	return p.ml.LocalNode().Name
}

// ClusterSize returns the total size of the cluster from this node's perspective.
func (p *Peer) ClusterSize() int {
	return p.ml.NumMembers()
}

// State returns a JSON-serializable dump of cluster state.
// Useful for debug.
func (p *Peer) State() map[string]interface{} {
	return map[string]interface{}{
		"self":     p.ml.LocalNode(),
		"members":  p.ml.Members(),
		"n":        p.ml.NumMembers(),
		"delegate": p.d.state(),
	}
}

func memberlistConfig(addr string, port int, delegate memberlist.Delegate, events memberlist.EventDelegate) *memberlist.Config {
	config := memberlist.DefaultLANConfig()
	config.Name = uuid.New()
	config.BindAddr = addr
	config.BindPort = port
	config.LogOutput = ioutil.Discard
	config.Delegate = delegate
	config.Events = events
	return config
}

// delegate manages gossiped data: the set of peers, their type, and API port.
// Clients must invoke init before the delegate can be used.
// Inspired by https://github.com/asim/memberlist/blob/master/memberlist.go
type delegate struct {
	mtx         sync.RWMutex
	bcast       *memberlist.TransmitLimitedQueue
	data        map[string]peerInfo
	invocations *prometheus.CounterVec // method
	logger      log.Logger
}

type peerInfo struct {
	Type    PeerType `json:"type"`
	APIAddr string   `json:"api_addr"`
	APIPort int      `json:"api_port"`
}

func newDelegate(invocations *prometheus.CounterVec, logger log.Logger) *delegate {
	return &delegate{
		bcast:       nil,
		data:        map[string]peerInfo{},
		invocations: invocations,
		logger:      logger,
	}
}

func (d *delegate) init(myName string, myType PeerType, apiAddr string, apiPort int, numNodes func() int) {
	d.mtx.Lock()
	defer d.mtx.Unlock()
	// As far as I can tell, it is only luck which ensures the d.bcast isn't
	// used (via GetBroadcasts) before we have a chance to create it here. But I
	// don't see a way to wire up the components (in NewPeer) that doesn't
	// involve this roundabout sort of initialization. Shrug!
	d.bcast = &memberlist.TransmitLimitedQueue{
		NumNodes:       numNodes,
		RetransmitMult: 3,
	}
	d.data[myName] = peerInfo{myType, apiAddr, apiPort}
}

func (d *delegate) current(t PeerType) (res []string) {
	for _, info := range d.state() {
		var (
			matchIngest      = t == PeerTypeIngest && (info.Type == PeerTypeIngest || info.Type == PeerTypeIngestStore)
			matchStore       = t == PeerTypeStore && (info.Type == PeerTypeStore || info.Type == PeerTypeIngestStore)
			matchIngestStore = t == PeerTypeIngestStore && info.Type == PeerTypeIngestStore
		)
		if matchIngest || matchStore || matchIngestStore {
			res = append(res, net.JoinHostPort(info.APIAddr, strconv.Itoa(info.APIPort)))
		}
	}
	return res
}

func (d *delegate) state() map[string]peerInfo {
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	return d.data
}

// NodeMeta is used to retrieve meta-data about the current node
// when broadcasting an alive message. It's length is limited to
// the given byte size. This metadata is available in the Node structure.
// Implements memberlist.Delegate.
func (d *delegate) NodeMeta(limit int) []byte {
	d.invocations.WithLabelValues("NodeMeta").Inc()
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	return []byte{} // no metadata
}

// NotifyMsg is called when a user-data message is received.
// Care should be taken that this method does not block, since doing
// so would block the entire UDP packet receive loop. Additionally, the byte
// slice may be modified after the call returns, so it should be copied if needed.
// Implements memberlist.Delegate.
func (d *delegate) NotifyMsg(b []byte) {
	d.invocations.WithLabelValues("NotifyMsg").Inc()
	if len(b) == 0 {
		return
	}
	var data map[string]peerInfo
	if err := json.Unmarshal(b, &data); err != nil {
		level.Error(d.logger).Log("method", "NotifyMsg", "b", strings.TrimSpace(string(b)), "err", err)
		return
	}
	d.mtx.Lock()
	defer d.mtx.Unlock()
	for k, v := range data {
		// Removing data is handled by NotifyLeave
		d.data[k] = v
	}
}

// GetBroadcasts is called when user data messages can be broadcast.
// It can return a list of buffers to send. Each buffer should assume an
// overhead as provided with a limit on the total byte size allowed.
// The total byte size of the resulting data to send must not exceed
// the limit. Care should be taken that this method does not block,
// since doing so would block the entire UDP packet receive loop.
// Implements memberlist.Delegate.
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
	d.invocations.WithLabelValues("GetBroadcasts").Inc()
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	if d.bcast == nil {
		panic("GetBroadcast before init")
	}
	return d.bcast.GetBroadcasts(overhead, limit)
}

// LocalState is used for a TCP Push/Pull. This is sent to
// the remote side in addition to the membership information. Any
// data can be sent here. See MergeRemoteState as well. The `join`
// boolean indicates this is for a join instead of a push/pull.
// Implements memberlist.Delegate.
func (d *delegate) LocalState(join bool) []byte {
	d.invocations.WithLabelValues("LocalState").Inc()
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	buf, err := json.Marshal(d.data)
	if err != nil {
		panic(err)
	}
	return buf
}

// MergeRemoteState is invoked after a TCP Push/Pull. This is the
// state received from the remote side and is the result of the
// remote side's LocalState call. The 'join'
// boolean indicates this is for a join instead of a push/pull.
// Implements memberlist.Delegate.
func (d *delegate) MergeRemoteState(buf []byte, join bool) {
	d.invocations.WithLabelValues("MergeRemoteState").Inc()
	if len(buf) == 0 {
		level.Debug(d.logger).Log("method", "MergeRemoteState", "join", join, "buf_sz", 0)
		return
	}
	var data map[string]peerInfo
	if err := json.Unmarshal(buf, &data); err != nil {
		level.Error(d.logger).Log("method", "MergeRemoteState", "err", err)
		return
	}
	d.mtx.Lock()
	defer d.mtx.Unlock()
	for k, v := range data {
		d.data[k] = v
	}
}

// NotifyJoin is invoked when a node is detected to have joined.
// The Node argument must not be modified.
// Implements memberlist.EventDelegate.
func (d *delegate) NotifyJoin(n *memberlist.Node) {
	d.invocations.WithLabelValues("NotifyJoin").Inc()
}

// NotifyUpdate is invoked when a node is detected to have updated, usually
// involving the meta data. The Node argument must not be modified.
// Implements memberlist.EventDelegate.
func (d *delegate) NotifyUpdate(n *memberlist.Node) {
	d.invocations.WithLabelValues("NotifyUpdate").Inc()
}

// NotifyLeave is invoked when a node is detected to have left.
// The Node argument must not be modified.
// Implements memberlist.EventDelegate.
func (d *delegate) NotifyLeave(n *memberlist.Node) {
	d.invocations.WithLabelValues("NotifyLeave").Inc()
	d.mtx.Lock()
	defer d.mtx.Unlock()
	delete(d.data, n.Name)
}
<commit_msg>Fix comment in package cluster<commit_after>// Package cluster provides an elastic peer discovery and gossip layer.
//
// Ingest and store instances join the same cluster and know about each other.
// Store instances consume segments from each ingest instance, and broadcast
// queries to each store instance. In the future, ingest instances will share
// load information to potentially refuse connections and balance writes.
package cluster

import (
	"encoding/json"
	"io/ioutil"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	level "github.com/go-kit/kit/log/experimental_level"
	"github.com/hashicorp/memberlist"
	"github.com/pborman/uuid"
	"github.com/prometheus/client_golang/prometheus"
)

// Peer represents this node in the cluster.
type Peer struct {
	ml *memberlist.Memberlist
	d  *delegate
}

// PeerType enumerates the types of nodes in the cluster.
type PeerType string

const (
	// PeerTypeIngest serves the ingest API.
	PeerTypeIngest PeerType = "ingest"

	// PeerTypeStore serves the store API.
	PeerTypeStore = "store"

	// PeerTypeIngestStore serves both ingest and store APIs.
	PeerTypeIngestStore = "ingeststore"
)

// NewPeer creates or joins a cluster with the existing peers.
// We will listen for cluster communications on the given addr:port.
// We advertise a PeerType HTTP API, reachable on apiPort.
func NewPeer(addr string, port int, existing []string, t PeerType, apiPort int, delegateInvocations *prometheus.CounterVec, logger log.Logger) (*Peer, error) {
	d := newDelegate(delegateInvocations, logger)
	conf := memberlistConfig(addr, port, d, d)
	ml, err := memberlist.Create(conf)
	if err != nil {
		return nil, err
	}
	d.init(conf.Name, t, ml.LocalNode().Addr.String(), apiPort, ml.NumMembers)
	n, err := ml.Join(existing)
	if err != nil {
		return nil, err
	}
	level.Debug(logger).Log("Join", n)
	return &Peer{
		ml: ml,
		d:  d,
	}, nil
}

// Leave the cluster, waiting up to timeout.
func (p *Peer) Leave(timeout time.Duration) error {
	return p.ml.Leave(timeout)
}

// Current API host:ports for the given type of node.
func (p *Peer) Current(t PeerType) []string {
	return p.d.current(t)
}

// Name returns the unique ID of this peer in the cluster.
func (p *Peer) Name() string {
	return p.ml.LocalNode().Name
}

// ClusterSize returns the total size of the cluster from this node's perspective.
func (p *Peer) ClusterSize() int {
	return p.ml.NumMembers()
}

// State returns a JSON-serializable dump of cluster state.
// Useful for debug.
func (p *Peer) State() map[string]interface{} {
	return map[string]interface{}{
		"self":     p.ml.LocalNode(),
		"members":  p.ml.Members(),
		"n":        p.ml.NumMembers(),
		"delegate": p.d.state(),
	}
}

func memberlistConfig(addr string, port int, delegate memberlist.Delegate, events memberlist.EventDelegate) *memberlist.Config {
	config := memberlist.DefaultLANConfig()
	config.Name = uuid.New()
	config.BindAddr = addr
	config.BindPort = port
	config.LogOutput = ioutil.Discard
	config.Delegate = delegate
	config.Events = events
	return config
}

// delegate manages gossiped data: the set of peers, their type, and API port.
// Clients must invoke init before the delegate can be used.
// Inspired by https://github.com/asim/memberlist/blob/master/memberlist.go
type delegate struct {
	mtx         sync.RWMutex
	bcast       *memberlist.TransmitLimitedQueue
	data        map[string]peerInfo
	invocations *prometheus.CounterVec // method
	logger      log.Logger
}

type peerInfo struct {
	Type    PeerType `json:"type"`
	APIAddr string   `json:"api_addr"`
	APIPort int      `json:"api_port"`
}

func newDelegate(invocations *prometheus.CounterVec, logger log.Logger) *delegate {
	return &delegate{
		bcast:       nil,
		data:        map[string]peerInfo{},
		invocations: invocations,
		logger:      logger,
	}
}

func (d *delegate) init(myName string, myType PeerType, apiAddr string, apiPort int, numNodes func() int) {
	d.mtx.Lock()
	defer d.mtx.Unlock()
	// As far as I can tell, it is only luck which ensures the d.bcast isn't
	// used (via GetBroadcasts) before we have a chance to create it here. But I
	// don't see a way to wire up the components (in NewPeer) that doesn't
	// involve this roundabout sort of initialization. Shrug!
	d.bcast = &memberlist.TransmitLimitedQueue{
		NumNodes:       numNodes,
		RetransmitMult: 3,
	}
	d.data[myName] = peerInfo{myType, apiAddr, apiPort}
}

func (d *delegate) current(t PeerType) (res []string) {
	for _, info := range d.state() {
		var (
			matchIngest      = t == PeerTypeIngest && (info.Type == PeerTypeIngest || info.Type == PeerTypeIngestStore)
			matchStore       = t == PeerTypeStore && (info.Type == PeerTypeStore || info.Type == PeerTypeIngestStore)
			matchIngestStore = t == PeerTypeIngestStore && info.Type == PeerTypeIngestStore
		)
		if matchIngest || matchStore || matchIngestStore {
			res = append(res, net.JoinHostPort(info.APIAddr, strconv.Itoa(info.APIPort)))
		}
	}
	return res
}

func (d *delegate) state() map[string]peerInfo {
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	return d.data
}

// NodeMeta is used to retrieve meta-data about the current node
// when broadcasting an alive message. It's length is limited to
// the given byte size. This metadata is available in the Node structure.
// Implements memberlist.Delegate.
func (d *delegate) NodeMeta(limit int) []byte {
	d.invocations.WithLabelValues("NodeMeta").Inc()
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	return []byte{} // no metadata
}

// NotifyMsg is called when a user-data message is received.
// Care should be taken that this method does not block, since doing
// so would block the entire UDP packet receive loop. Additionally, the byte
// slice may be modified after the call returns, so it should be copied if needed.
// Implements memberlist.Delegate.
func (d *delegate) NotifyMsg(b []byte) {
	d.invocations.WithLabelValues("NotifyMsg").Inc()
	if len(b) == 0 {
		return
	}
	var data map[string]peerInfo
	if err := json.Unmarshal(b, &data); err != nil {
		level.Error(d.logger).Log("method", "NotifyMsg", "b", strings.TrimSpace(string(b)), "err", err)
		return
	}
	d.mtx.Lock()
	defer d.mtx.Unlock()
	for k, v := range data {
		// Removing data is handled by NotifyLeave
		d.data[k] = v
	}
}

// GetBroadcasts is called when user data messages can be broadcast.
// It can return a list of buffers to send. Each buffer should assume an
// overhead as provided with a limit on the total byte size allowed.
// The total byte size of the resulting data to send must not exceed
// the limit. Care should be taken that this method does not block,
// since doing so would block the entire UDP packet receive loop.
// Implements memberlist.Delegate.
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
	d.invocations.WithLabelValues("GetBroadcasts").Inc()
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	if d.bcast == nil {
		panic("GetBroadcast before init")
	}
	return d.bcast.GetBroadcasts(overhead, limit)
}

// LocalState is used for a TCP Push/Pull. This is sent to
// the remote side in addition to the membership information. Any
// data can be sent here. See MergeRemoteState as well. The `join`
// boolean indicates this is for a join instead of a push/pull.
// Implements memberlist.Delegate.
func (d *delegate) LocalState(join bool) []byte {
	d.invocations.WithLabelValues("LocalState").Inc()
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	buf, err := json.Marshal(d.data)
	if err != nil {
		panic(err)
	}
	return buf
}

// MergeRemoteState is invoked after a TCP Push/Pull. This is the
// state received from the remote side and is the result of the
// remote side's LocalState call. The 'join'
// boolean indicates this is for a join instead of a push/pull.
// Implements memberlist.Delegate.
func (d *delegate) MergeRemoteState(buf []byte, join bool) {
	d.invocations.WithLabelValues("MergeRemoteState").Inc()
	if len(buf) == 0 {
		level.Debug(d.logger).Log("method", "MergeRemoteState", "join", join, "buf_sz", 0)
		return
	}
	var data map[string]peerInfo
	if err := json.Unmarshal(buf, &data); err != nil {
		level.Error(d.logger).Log("method", "MergeRemoteState", "err", err)
		return
	}
	d.mtx.Lock()
	defer d.mtx.Unlock()
	for k, v := range data {
		d.data[k] = v
	}
}

// NotifyJoin is invoked when a node is detected to have joined.
// The Node argument must not be modified.
// Implements memberlist.EventDelegate.
func (d *delegate) NotifyJoin(n *memberlist.Node) {
	d.invocations.WithLabelValues("NotifyJoin").Inc()
}

// NotifyUpdate is invoked when a node is detected to have updated, usually
// involving the meta data. The Node argument must not be modified.
// Implements memberlist.EventDelegate.
func (d *delegate) NotifyUpdate(n *memberlist.Node) {
	d.invocations.WithLabelValues("NotifyUpdate").Inc()
}

// NotifyLeave is invoked when a node is detected to have left.
// The Node argument must not be modified.
// Implements memberlist.EventDelegate.
func (d *delegate) NotifyLeave(n *memberlist.Node) {
	d.invocations.WithLabelValues("NotifyLeave").Inc()
	d.mtx.Lock()
	defer d.mtx.Unlock()
	delete(d.data, n.Name)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/spf13/cobra"
	"k8s.io/cli-runtime/pkg/genericclioptions"
)

func TestNormalizationFuncGlobalExistence(t *testing.T) {
	// This test can be safely deleted when we will not support multiple flag formats
	root := NewKubectlCommand(KubectlOptions{IOStreams: genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}})

	if root.Parent() != nil {
		t.Fatal("We expect the root command to be returned")
	}
	if root.GlobalNormalizationFunc() == nil {
		t.Fatal("We expect that root command has a global normalization function")
	}

	if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("root command seems to have a wrong normalization function")
	}

	sub := root
	for sub.HasSubCommands() {
		sub = sub.Commands()[0]
	}

	// In case of failure of this test check this PR: spf13/cobra#110
	if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("child and root commands should have the same normalization functions")
	}
}

func TestKubectlCommandHandlesPlugins(t *testing.T) {
	tests := []struct {
		name             string
		args             []string
		expectPlugin     string
		expectPluginArgs []string
		expectError      string
	}{
		{
			name:             "test that normal commands are able to be executed, when no plugin overshadows them",
			args:             []string{"kubectl", "get", "foo"},
			expectPlugin:     "",
			expectPluginArgs: []string{},
		},
		{
			name:             "test that a plugin executable is found based on command args",
			args:             []string{"kubectl", "foo", "--bar"},
			expectPlugin:     "plugin/testdata/kubectl-foo",
			expectPluginArgs: []string{"--bar"},
		},
		{
			name: "test that a plugin does not execute over an existing command by the same name",
			args: []string{"kubectl", "version"},
		},
		// The following tests make sure that commands added by Cobra cannot be shadowed by a plugin
		// See https://github.com/kubernetes/kubectl/issues/1116
		{
			name: "test that a plugin does not execute over Cobra's help command",
			args: []string{"kubectl", "help"},
		},
		{
			name: "test that a plugin does not execute over Cobra's __complete command",
			args: []string{"kubectl", cobra.ShellCompRequestCmd},
		},
		{
			name: "test that a plugin does not execute over Cobra's __completeNoDesc command",
			args: []string{"kubectl", cobra.ShellCompNoDescRequestCmd},
		},
		// The following tests make sure that commands added by Cobra cannot be shadowed by a plugin
		// even when a flag is specified first.  This can happen when using aliases.
		// See https://github.com/kubernetes/kubectl/issues/1119
		{
			name: "test that a flag does not break Cobra's help command",
			args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", "help"},
		},
		{
			name: "test that a flag does not break Cobra's __complete command",
			args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", cobra.ShellCompRequestCmd},
		},
		{
			name: "test that a flag does not break Cobra's __completeNoDesc command",
			args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", cobra.ShellCompNoDescRequestCmd},
		},
		// As for the previous tests, an alias could add a flag without using the = form.
		// We don't support this case as parsing the flags becomes quite complicated (flags
		// that take a value, versus flags that don't)
		// {
		// 	name: "test that a flag with a space does not break Cobra's help command",
		// 	args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", "help"},
		// },
		// {
		// 	name: "test that a flag with a space does not break Cobra's __complete command",
		// 	args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", cobra.ShellCompRequestCmd},
		// },
		// {
		// 	name: "test that a flag with a space does not break Cobra's __completeNoDesc command",
		// 	args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", cobra.ShellCompNoDescRequestCmd},
		// },
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			pluginsHandler := &testPluginHandler{
				pluginsDirectory: "plugin/testdata",
			}
			ioStreams, _, _, _ := genericclioptions.NewTestIOStreams()

			root := NewDefaultKubectlCommandWithArgs(KubectlOptions{PluginHandler: pluginsHandler, Arguments: test.args, IOStreams: ioStreams})
			if err := root.Execute(); err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			if pluginsHandler.err != nil && pluginsHandler.err.Error() != test.expectError {
				t.Fatalf("unexpected error: expected %q to occur, but got %q", test.expectError, pluginsHandler.err)
			}

			if pluginsHandler.executedPlugin != test.expectPlugin {
				t.Fatalf("unexpected plugin execution: expected %q, got %q", test.expectPlugin, pluginsHandler.executedPlugin)
			}

			if len(pluginsHandler.withArgs) != len(test.expectPluginArgs) {
				t.Fatalf("unexpected plugin execution args: expected %q, got %q", test.expectPluginArgs, pluginsHandler.withArgs)
			}
		})
	}
}

type testPluginHandler struct {
	pluginsDirectory string

	// execution results
	executedPlugin string
	withArgs       []string
	withEnv        []string

	err error
}

func (h *testPluginHandler) Lookup(filename string) (string, bool) {
	// append supported plugin prefix to the filename
	filename = fmt.Sprintf("%s-%s", "kubectl", filename)

	dir, err := os.Stat(h.pluginsDirectory)
	if err != nil {
		h.err = err
		return "", false
	}

	if !dir.IsDir() {
		h.err = fmt.Errorf("expected %q to be a directory", h.pluginsDirectory)
		return "", false
	}

	plugins, err := ioutil.ReadDir(h.pluginsDirectory)
	if err != nil {
		h.err = err
		return "", false
	}

	for _, p := range plugins {
		if p.Name() == filename {
			return fmt.Sprintf("%s/%s", h.pluginsDirectory, p.Name()), true
		}
	}

	h.err = fmt.Errorf("unable to find a plugin executable %q", filename)
	return "", false
}

func (h *testPluginHandler) Execute(executablePath string, cmdArgs, env []string) error {
	h.executedPlugin = executablePath
	h.withArgs = cmdArgs
	h.withEnv = env
	return nil
}

func TestKubectlCommandHeadersHooks(t *testing.T) {
	tests := map[string]struct {
		envVar    string
		addsHooks bool
	}{
		"empty environment variable; hooks added": {
			envVar:    "",
			addsHooks: true,
		},
		"random env var value; hooks added": {
			envVar:    "foo",
			addsHooks: true,
		},
		"true env var value; hooks added": {
			envVar:    "true",
			addsHooks: true,
		},
		"false env var value; hooks NOT added": {
			envVar:    "false",
			addsHooks: false,
		},
		"zero env var value; hooks NOT added": {
			envVar:    "0",
			addsHooks: false,
		},
	}

	for name, testCase := range tests {
		t.Run(name, func(t *testing.T) {
			cmds := &cobra.Command{}
			kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
			if kubeConfigFlags.WrapConfigFn != nil {
				t.Fatal("expected initial nil WrapConfigFn")
			}
			os.Setenv(kubectlCmdHeaders, testCase.envVar)
			addCmdHeaderHooks(cmds, kubeConfigFlags)
			// Valdidate whether the hooks were added.
			if testCase.addsHooks && kubeConfigFlags.WrapConfigFn == nil {
				t.Error("after adding kubectl command header, expecting non-nil WrapConfigFn")
			}
			if !testCase.addsHooks && kubeConfigFlags.WrapConfigFn != nil {
				t.Error("env var feature gate should have blocked setting WrapConfigFn")
			}
		})
	}
}
<commit_msg>fixed test to fail when you change the `expectPluginArgs` so that it is different than `args`<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/spf13/cobra"
	"k8s.io/cli-runtime/pkg/genericclioptions"
)

func TestNormalizationFuncGlobalExistence(t *testing.T) {
	// This test can be safely deleted when we will not support multiple flag formats
	root := NewKubectlCommand(KubectlOptions{IOStreams: genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}})

	if root.Parent() != nil {
		t.Fatal("We expect the root command to be returned")
	}
	if root.GlobalNormalizationFunc() == nil {
		t.Fatal("We expect that root command has a global normalization function")
	}

	if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("root command seems to have a wrong normalization function")
	}

	sub := root
	for sub.HasSubCommands() {
		sub = sub.Commands()[0]
	}

	// In case of failure of this test check this PR: spf13/cobra#110
	if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
		t.Fatal("child and root commands should have the same normalization functions")
	}
}

func TestKubectlCommandHandlesPlugins(t *testing.T) {
	tests := []struct {
		name             string
		args             []string
		expectPlugin     string
		expectPluginArgs []string
		expectError      string
	}{
		{
			name:             "test that normal commands are able to be executed, when no plugin overshadows them",
			args:             []string{"kubectl", "get", "foo"},
			expectPlugin:     "",
			expectPluginArgs: []string{},
		},
		{
			name:             "test that a plugin executable is found based on command args",
			args:             []string{"kubectl", "foo", "--bar"},
			expectPlugin:     "plugin/testdata/kubectl-foo",
			expectPluginArgs: []string{"--bar"},
		},
		{
			name: "test that a plugin does not execute over an existing command by the same name",
			args: []string{"kubectl", "version"},
		},
		// The following tests make sure that commands added by Cobra cannot be shadowed by a plugin
		// See https://github.com/kubernetes/kubectl/issues/1116
		{
			name: "test that a plugin does not execute over Cobra's help command",
			args: []string{"kubectl", "help"},
		},
		{
			name: "test that a plugin does not execute over Cobra's __complete command",
			args: []string{"kubectl", cobra.ShellCompRequestCmd},
		},
		{
			name: "test that a plugin does not execute over Cobra's __completeNoDesc command",
			args: []string{"kubectl", cobra.ShellCompNoDescRequestCmd},
		},
		// The following tests make sure that commands added by Cobra cannot be shadowed by a plugin
		// even when a flag is specified first.  This can happen when using aliases.
		// See https://github.com/kubernetes/kubectl/issues/1119
		{
			name: "test that a flag does not break Cobra's help command",
			args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", "help"},
		},
		{
			name: "test that a flag does not break Cobra's __complete command",
			args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", cobra.ShellCompRequestCmd},
		},
		{
			name: "test that a flag does not break Cobra's __completeNoDesc command",
			args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", cobra.ShellCompNoDescRequestCmd},
		},
		// As for the previous tests, an alias could add a flag without using the = form.
		// We don't support this case as parsing the flags becomes quite complicated (flags
		// that take a value, versus flags that don't)
		// {
		// 	name: "test that a flag with a space does not break Cobra's help command",
		// 	args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", "help"},
		// },
		// {
		// 	name: "test that a flag with a space does not break Cobra's __complete command",
		// 	args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", cobra.ShellCompRequestCmd},
		// },
		// {
		// 	name: "test that a flag with a space does not break Cobra's __completeNoDesc command",
		// 	args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", cobra.ShellCompNoDescRequestCmd},
		// },
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			pluginsHandler := &testPluginHandler{
				pluginsDirectory: "plugin/testdata",
			}
			ioStreams, _, _, _ := genericclioptions.NewTestIOStreams()

			root := NewDefaultKubectlCommandWithArgs(KubectlOptions{PluginHandler: pluginsHandler, Arguments: test.args, IOStreams: ioStreams})
			if err := root.Execute(); err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			if pluginsHandler.err != nil && pluginsHandler.err.Error() != test.expectError {
				t.Fatalf("unexpected error: expected %q to occur, but got %q", test.expectError, pluginsHandler.err)
			}

			if pluginsHandler.executedPlugin != test.expectPlugin {
				t.Fatalf("unexpected plugin execution: expected %q, got %q", test.expectPlugin, pluginsHandler.executedPlugin)
			}

			if !cmp.Equal(pluginsHandler.withArgs, test.expectPluginArgs, cmpopts.EquateEmpty()) {
				t.Fatalf("unexpected plugin execution args: expected %q, got %q", test.expectPluginArgs, pluginsHandler.withArgs)
			}
		})
	}
}

type testPluginHandler struct {
	pluginsDirectory string

	// execution results
	executedPlugin string
	withArgs       []string
	withEnv        []string

	err error
}

func (h *testPluginHandler) Lookup(filename string) (string, bool) {
	// append supported plugin prefix to the filename
	filename = fmt.Sprintf("%s-%s", "kubectl", filename)

	dir, err := os.Stat(h.pluginsDirectory)
	if err != nil {
		h.err = err
		return "", false
	}

	if !dir.IsDir() {
		h.err = fmt.Errorf("expected %q to be a directory", h.pluginsDirectory)
		return "", false
	}

	plugins, err := ioutil.ReadDir(h.pluginsDirectory)
	if err != nil {
		h.err = err
		return "", false
	}

	for _, p := range plugins {
		if p.Name() == filename {
			return fmt.Sprintf("%s/%s", h.pluginsDirectory, p.Name()), true
		}
	}

	h.err = fmt.Errorf("unable to find a plugin executable %q", filename)
	return "", false
}

func (h *testPluginHandler) Execute(executablePath string, cmdArgs, env []string) error {
	h.executedPlugin = executablePath
	h.withArgs = cmdArgs
	h.withEnv = env
	return nil
}

func TestKubectlCommandHeadersHooks(t *testing.T) {
	tests := map[string]struct {
		envVar    string
		addsHooks bool
	}{
		"empty environment variable; hooks added": {
			envVar:    "",
			addsHooks: true,
		},
		"random env var value; hooks added": {
			envVar:    "foo",
			addsHooks: true,
		},
		"true env var value; hooks added": {
			envVar:    "true",
			addsHooks: true,
		},
		"false env var value; hooks NOT added": {
			envVar:    "false",
			addsHooks: false,
		},
		"zero env var value; hooks NOT added": {
			envVar:    "0",
			addsHooks: false,
		},
	}

	for name, testCase := range tests {
		t.Run(name, func(t *testing.T) {
			cmds := &cobra.Command{}
			kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
			if kubeConfigFlags.WrapConfigFn != nil {
				t.Fatal("expected initial nil WrapConfigFn")
			}
			os.Setenv(kubectlCmdHeaders, testCase.envVar)
			addCmdHeaderHooks(cmds, kubeConfigFlags)
			// Valdidate whether the hooks were added.
			if testCase.addsHooks && kubeConfigFlags.WrapConfigFn == nil {
				t.Error("after adding kubectl command header, expecting non-nil WrapConfigFn")
			}
			if !testCase.addsHooks && kubeConfigFlags.WrapConfigFn != nil {
				t.Error("env var feature gate should have blocked setting WrapConfigFn")
			}
		})
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// depsCmd represents the deps command
var depsCmd = &cobra.Command{
	Use:   "deps",
	Short: "Download and optionally update all abcweb dependencies",
	Long: `Download and optionally update all abcweb dependencies used by
your generated app or the abcweb tool by executing "go get" commands`,
	Example: "abcweb deps -u",
	// Needs to be a persistentPreRunE to override root's config.Initialize call
	// otherwise abcweb needs to be run from the abcweb project or the git rev-parse
	// will cause a fatal error.
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		viper.BindPFlags(cmd.Flags())
		return nil
	},
	RunE: depsCmdRun,
}

func init() {
	depsCmd.Flags().BoolP("update", "u", false, "Also update already installed dependencies")
	depsCmd.Flags().BoolP("verbose", "v", false, "Very noisy verbose output")
	depsCmd.Flags().BoolP("assets-only", "a", false, "Only install gulp.js dependencies")
	depsCmd.Flags().BoolP("go-only", "g", false, "Only install Go dependencies")

	RootCmd.AddCommand(depsCmd)
}

func depsCmdRun(cmd *cobra.Command, args []string) error {
	var err error

	goGetArgs := [][]string{
		{"go.uber.org/zap"},
		{"github.com/pkg/errors"},
		{"github.com/volatiletech/helpers/..."},
		{"github.com/go-chi/chi/..."},
		{"github.com/goware/cors"},
		{"github.com/unrolled/render"},
		{"github.com/volatiletech/mig/..."},
		{"github.com/spf13/pflag"},
		{"github.com/spf13/cobra"},
		{"github.com/spf13/viper"},
		{"github.com/volatiletech/refresh"},
		{"gopkg.in/nullbio/null.v6"},
		{"github.com/djherbis/times"},
		{"github.com/satori/go.uuid"},
		{"gopkg.in/redis.v5"},
		{"-t", "github.com/vattle/sqlboiler"},
		{"github.com/golang/dep/cmd/dep"},
	}

	npmInstallArgs := [][]string{
		{"gulpjs/gulp.git#4.0"},
	}

	prependArgs := []string{"get"}
	if viper.GetBool("update") {
		prependArgs = append(prependArgs, "-u")
	}
	if viper.GetBool("verbose") {
		prependArgs = append(prependArgs, "-v")
	}

	for i := 0; i < len(goGetArgs); i++ {
		goGetArgs[i] = append(prependArgs, goGetArgs[i]...)
	}

	if !viper.GetBool("go-only") {
		prependArgs = []string{"install", "--global"}
		if viper.GetBool("verbose") {
			prependArgs = append(prependArgs, "--verbose")
		}

		for i := 0; i < len(npmInstallArgs); i++ {
			npmInstallArgs[i] = append(prependArgs, npmInstallArgs[i]...)
		}

		fmt.Printf("Retrieving all Nodejs dependencies using \"npm install --global\":\n\n")

		_, err = exec.LookPath("npm")
		if err != nil {
			fmt.Printf(`Error: npm could not be found in your $PATH. If you have not already installed nodejs
and npm you must do so before proceeding. Please follow the instructions at:
https://docs.npmjs.com/getting-started/installing-node

If you receive permission related errors, please apply the following fix:
https://docs.npmjs.com/getting-started/fixing-npm-permissions
`)
			os.Exit(1)
		}

		for _, npmInstallArg := range npmInstallArgs {
			fmt.Printf("%s ... ", npmInstallArg[len(npmInstallArg)-1])

			exc := exec.Command("npm", npmInstallArg...)
			out, err := exc.CombinedOutput()

			if err != nil {
				fmt.Printf("ERROR\n\n")
			} else {
				fmt.Printf("SUCCESS\n\n")
			}

			if len(out) > 0 {
				fmt.Print(string(out))
			}

			if err != nil {
				fmt.Printf("%s\n\n", err)
				fmt.Printf(`Note: If you are receiving a permission related exit status or error, please apply the following fix:
https://docs.npmjs.com/getting-started/fixing-npm-permissions
`)
				os.Exit(1)
			}
		}
	}

	if !viper.GetBool("assets-only") {
		fmt.Printf("\nRetrieving all Go dependencies using \"go get\":\n\n")

		for _, goGetArg := range goGetArgs {
			fmt.Printf("%s ... ", goGetArg[len(goGetArg)-1])

			exc := exec.Command("go", goGetArg...)
			out, err := exc.CombinedOutput()

			if err != nil {
				fmt.Printf("ERROR\n\n")
			} else {
				fmt.Printf("SUCCESS\n")
			}

			if len(out) > 0 {
				fmt.Print(string(out))
			}

			if err != nil {
				fmt.Printf("%s\n\n", err)
				os.Exit(1)
			}
		}
	}

	fmt.Printf("\nAll dependencies successfully installed.\n\n")

	return err
}
<commit_msg>remove packages from deps command<commit_after>package cmd

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// depsCmd represents the deps command
var depsCmd = &cobra.Command{
	Use:   "deps",
	Short: "Download and optionally update all abcweb dependencies",
	Long: `Download and optionally update all abcweb dependencies used by
your generated app or the abcweb tool by executing "go get" commands`,
	Example: "abcweb deps -u",
	// Needs to be a persistentPreRunE to override root's config.Initialize call
	// otherwise abcweb needs to be run from the abcweb project or the git rev-parse
	// will cause a fatal error.
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		viper.BindPFlags(cmd.Flags())
		return nil
	},
	RunE: depsCmdRun,
}

func init() {
	depsCmd.Flags().BoolP("update", "u", false, "Also update already installed dependencies")
	depsCmd.Flags().BoolP("verbose", "v", false, "Very noisy verbose output")
	depsCmd.Flags().BoolP("assets-only", "a", false, "Only install gulp.js dependencies")
	depsCmd.Flags().BoolP("go-only", "g", false, "Only install Go dependencies")

	RootCmd.AddCommand(depsCmd)
}

func depsCmdRun(cmd *cobra.Command, args []string) error {
	var err error

	goGetArgs := [][]string{
		{"github.com/golang/dep/cmd/dep"},
	}

	npmInstallArgs := [][]string{
		{"gulpjs/gulp.git#4.0"},
	}

	prependArgs := []string{"get"}
	if viper.GetBool("update") {
		prependArgs = append(prependArgs, "-u")
	}
	if viper.GetBool("verbose") {
		prependArgs = append(prependArgs, "-v")
	}

	for i := 0; i < len(goGetArgs); i++ {
		goGetArgs[i] = append(prependArgs, goGetArgs[i]...)
	}

	if !viper.GetBool("go-only") {
		prependArgs = []string{"install", "--global"}
		if viper.GetBool("verbose") {
			prependArgs = append(prependArgs, "--verbose")
		}

		for i := 0; i < len(npmInstallArgs); i++ {
			npmInstallArgs[i] = append(prependArgs, npmInstallArgs[i]...)
		}

		fmt.Printf("Retrieving all Nodejs dependencies using \"npm install --global\":\n\n")

		_, err = exec.LookPath("npm")
		if err != nil {
			fmt.Printf(`Error: npm could not be found in your $PATH. If you have not already installed nodejs
and npm you must do so before proceeding. Please follow the instructions at:
https://docs.npmjs.com/getting-started/installing-node

If you receive permission related errors, please apply the following fix:
https://docs.npmjs.com/getting-started/fixing-npm-permissions
`)
			os.Exit(1)
		}

		for _, npmInstallArg := range npmInstallArgs {
			fmt.Printf("%s ... ", npmInstallArg[len(npmInstallArg)-1])

			exc := exec.Command("npm", npmInstallArg...)
			out, err := exc.CombinedOutput()

			if err != nil {
				fmt.Printf("ERROR\n\n")
			} else {
				fmt.Printf("SUCCESS\n\n")
			}

			if len(out) > 0 {
				fmt.Print(string(out))
			}

			if err != nil {
				fmt.Printf("%s\n\n", err)
				fmt.Printf(`Note: If you are receiving a permission related exit status or error, please apply the following fix:
https://docs.npmjs.com/getting-started/fixing-npm-permissions
`)
				os.Exit(1)
			}
		}
	}

	if !viper.GetBool("assets-only") {
		fmt.Printf("\nRetrieving all Go dependencies using \"go get\":\n\n")

		for _, goGetArg := range goGetArgs {
			fmt.Printf("%s ... ", goGetArg[len(goGetArg)-1])

			exc := exec.Command("go", goGetArg...)
			out, err := exc.CombinedOutput()

			if err != nil {
				fmt.Printf("ERROR\n\n")
			} else {
				fmt.Printf("SUCCESS\n")
			}

			if len(out) > 0 {
				fmt.Print(string(out))
			}

			if err != nil {
				fmt.Printf("%s\n\n", err)
				os.Exit(1)
			}
		}
	}

	fmt.Printf("\nAll dependencies successfully installed.\n\n")

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"time"

	"github.com/gorilla/mux"
)

const indexTemplStr = `<!doctype html>

<title>Plot Viewer</title>

	{{range $index, $p := .Plots}}

			<object data="/images/{{$index}}" type="image/svg+xml" style="pointer-events: none;"></object>

{{$p}}

	{{end}}

			<button id="open" onclick="openInNewTab()">Open in Tab</button>
			<button id="cp-png" onclick="openPNG()">Open PNG</button>
			<button id="exit" onclick="quit()">Quit</button>

		<object id="im-box" data="/images/0" type="image/svg+xml" style="pointer-events: none;">
		</object>

`

var indexTempl = template.Must(template.New("index").Parse(indexTemplStr))

type plotViewer struct {
	pdfPaths []string
	svgCache [][]byte
	pngCache [][]byte
}

func (v plotViewer) rootHandler(w http.ResponseWriter, r *http.Request) {
	err := indexTempl.Execute(w, struct {
		Plots []string
	}{
		v.pdfPaths,
	})
	if err != nil {
		log.Println("GET /: %v", err)
	}
}

func (v plotViewer) imageHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if id < 0 || id >= len(v.pdfPaths) {
		http.Error(w, "image id out of bounds", http.StatusBadRequest)
		return
	}

	if v.svgCache[id] == nil {
		out, err := exec.Command("pdf2svg", v.pdfPaths[id], "/dev/stdout").Output()
		if err != nil {
			http.Error(w, fmt.Sprintf("unable to convert to svg: %v", err), http.StatusInternalServerError)
			return
		}

		v.svgCache[id] = out
	}

	w.Header().Set("content-type", "image/svg+xml")
	w.Write(v.svgCache[id])
}

func (v plotViewer) pngHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if id < 0 || id >= len(v.pdfPaths) {
		http.Error(w, "image id out of bounds", http.StatusBadRequest)
		return
	}

	if v.pngCache[id] == nil {
		out, err := exec.Command("convert", "-density", "300", v.pdfPaths[id], "png:-").Output()
		if err != nil {
			http.Error(w, fmt.Sprintf("unable to convert to png: %v", err), http.StatusInternalServerError)
			return
		}

		v.pngCache[id] = out
	}

	w.Header().Set("content-type", "image/png")
	w.Write(v.pngCache[id])
}

type quitHandler struct {
	s *http.Server
}

func (h quitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx, _ := context.WithTimeout(r.Context(), 5*time.Second)
	h.s.Shutdown(ctx)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("vplots: ")
	if len(os.Args) < 2 {
		fmt.Fprintln(os.Stderr, "usage: vplots plot.pdf...")
		os.Exit(2)
	}

	v := plotViewer{
		pdfPaths: os.Args[1:],
		svgCache: make([][]byte, len(os.Args)-1),
		pngCache: make([][]byte, len(os.Args)-1),
	}

	r := mux.NewRouter()
	r.HandleFunc("/", v.rootHandler)
	r.HandleFunc("/images/{id:[0-9]+}", v.imageHandler)
	r.HandleFunc("/pngs/{id:[0-9]+}", v.pngHandler)

	s := &http.Server{
		Addr:    "127.0.0.1:6544",
		Handler: r,
	}
	r.Handle("/quit", quitHandler{s})
	go openURL(s.Addr)
	s.ListenAndServe()
}

func openURL(url string) {
	url = "http://" + url

	for tries := 5; tries > 5; tries-- {
		if _, err := http.Get(url); err == nil {
			break
		}
		time.Sleep(10 * time.Millisecond)
	}

	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "darwin":
		cmd = exec.Command("open", url)
	case "linux", "freebsd", "solaris":
		cmd = exec.Command("xdg-open", url)
	default:
		log.Printf("no known open command, connect to %s", url)
		return
	}
	if err := cmd.Start(); err != nil {
		log.Printf("open command failed, connect to %s: %v", url, err)
	}
}
<commit_msg>vplots: color selected image<commit_after>package main

import (
	"context"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"time"

	"github.com/gorilla/mux"
)

const indexTemplStr = `<!doctype html>

<title>Plot Viewer</title>

	{{range $index, $p := .Plots}}

			<object data="/images/{{$index}}" type="image/svg+xml" style="pointer-events: none;"></object>

{{$p}}

	{{end}}

			<button id="open" onclick="openInNewTab()">Open in Tab</button>
			<button id="cp-png" onclick="openPNG()">Open PNG</button>
			<button id="exit" onclick="quit()">Quit</button>

		<object id="im-box" data="/images/0" type="image/svg+xml" style="pointer-events: none;">
		</object>

`

var indexTempl = template.Must(template.New("index").Parse(indexTemplStr))

type plotViewer struct {
	pdfPaths []string
	svgCache [][]byte
	pngCache [][]byte
}

func (v plotViewer) rootHandler(w http.ResponseWriter, r *http.Request) {
	err := indexTempl.Execute(w, struct {
		Plots []string
	}{
		v.pdfPaths,
	})
	if err != nil {
		log.Println("GET /: %v", err)
	}
}

func (v plotViewer) imageHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if id < 0 || id >= len(v.pdfPaths) {
		http.Error(w, "image id out of bounds", http.StatusBadRequest)
		return
	}

	if v.svgCache[id] == nil {
		out, err := exec.Command("pdf2svg", v.pdfPaths[id], "/dev/stdout").Output()
		if err != nil {
			http.Error(w, fmt.Sprintf("unable to convert to svg: %v", err), http.StatusInternalServerError)
			return
		}

		v.svgCache[id] = out
	}

	w.Header().Set("content-type", "image/svg+xml")
	w.Write(v.svgCache[id])
}

func (v plotViewer) pngHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if id < 0 || id >= len(v.pdfPaths) {
		http.Error(w, "image id out of bounds", http.StatusBadRequest)
		return
	}

	if v.pngCache[id] == nil {
		out, err := exec.Command("convert", "-density", "300", v.pdfPaths[id], "png:-").Output()
		if err != nil {
			http.Error(w, fmt.Sprintf("unable to convert to png: %v", err), http.StatusInternalServerError)
			return
		}

		v.pngCache[id] = out
	}

	w.Header().Set("content-type", "image/png")
	w.Write(v.pngCache[id])
}

type quitHandler struct {
	s *http.Server
}

func (h quitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx, _ := context.WithTimeout(r.Context(), 5*time.Second)
	h.s.Shutdown(ctx)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("vplots: ")
	if len(os.Args) < 2 {
		fmt.Fprintln(os.Stderr, "usage: vplots plot.pdf...")
		os.Exit(2)
	}

	v := plotViewer{
		pdfPaths: os.Args[1:],
		svgCache: make([][]byte, len(os.Args)-1),
		pngCache: make([][]byte, len(os.Args)-1),
	}

	r := mux.NewRouter()
	r.HandleFunc("/", v.rootHandler)
	r.HandleFunc("/images/{id:[0-9]+}", v.imageHandler)
	r.HandleFunc("/pngs/{id:[0-9]+}", v.pngHandler)

	s := &http.Server{
		Addr:    "127.0.0.1:6544",
		Handler: r,
	}
	r.Handle("/quit", quitHandler{s})
	go openURL(s.Addr)
	s.ListenAndServe()
}

func openURL(url string) {
	url = "http://" + url

	for tries := 5; tries > 5; tries-- {
		if _, err := http.Get(url); err == nil {
			break
		}
		time.Sleep(10 * time.Millisecond)
	}

	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "darwin":
		cmd = exec.Command("open", url)
	case "linux", "freebsd", "solaris":
		cmd = exec.Command("xdg-open", url)
	default:
		log.Printf("no known open command, connect to %s", url)
		return
	}
	if err := cmd.Start(); err != nil {
		log.Printf("open command failed, connect to %s: %v", url, err)
	}
}
<|endoftext|>


<commit_before>package oauth

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/http/httputil"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kubernetes/test/e2e/framework"

	configv1 "github.com/openshift/api/config/v1"
	oauthv1 "github.com/openshift/api/oauth/v1"
	userv1 "github.com/openshift/api/user/v1"
	exutil "github.com/openshift/origin/test/extended/util"

	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
)

var authzToken, sha256AuthzToken = exutil.GenerateOAuthTokenPair()

var _ = g.Describe("[sig-auth][Feature:OAuthServer]", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("oauthclient-secret-with-plus")
	ctx := context.Background()

	g.Describe("ClientSecretWithPlus", func() {
		g.It(fmt.Sprintf("should create oauthclient"), func() {
			controlPlaneTopology, err := exutil.GetControlPlaneTopology(oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			if *controlPlaneTopology == configv1.ExternalTopologyMode {
				e2eskipper.Skipf("External clusters do not contain oauth-openshift routes")
			}

			g.By("create oauth client")
			oauthClient, err := oc.AdminOauthClient().OauthV1().OAuthClients().Create(ctx, &oauthv1.OAuthClient{
				ObjectMeta: metav1.ObjectMeta{
					Name: "oauth-client-with-plus",
				},
				Secret:            "secret+with+plus",
				RedirectURIs:      []string{"https://www.google.com"},
				GrantMethod:       oauthv1.GrantHandlerAuto,
				ScopeRestrictions: []oauthv1.ScopeRestriction{{ExactValues: []string{"user:full"}}},
			}, metav1.CreateOptions{})
			o.Expect(err).NotTo(o.HaveOccurred())
			oc.AddResourceToDelete(oauthv1.GroupVersion.WithResource("oauthclients"), oauthClient)

			g.By("create synthetic user")

			user, err := oc.AdminUserClient().UserV1().Users().Create(ctx, &userv1.User{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Name: "user-for-plus",
				},
				FullName: "fake user",
			}, metav1.CreateOptions{})
			o.Expect(err).NotTo(o.HaveOccurred())
			oc.AddResourceToDelete(userv1.GroupVersion.WithResource("users"), user)

			oauthDiscoveryData := getOAuthWellKnownData(oc)

			for _, createTokenReq := range []func(string) *http.Request{
				queryClientAuthRequest,
				bodyClientAuthRequest,
				headerClientAuthRequest,
			} {
				g.By("create synthetic authz token")
				oauthAuthorizeToken, err := oc.AdminOauthClient().OauthV1().OAuthAuthorizeTokens().Create(ctx, &oauthv1.OAuthAuthorizeToken{
					ObjectMeta: metav1.ObjectMeta{
						Name: sha256AuthzToken,
					},
					ClientName:          oauthClient.Name,
					ExpiresIn:           100000000,
					RedirectURI:         "https://www.google.com",
					Scopes:              []string{"user:full"},
					UserName:            user.Name,
					UserUID:             string(user.UID),
					CodeChallenge:       "",
					CodeChallengeMethod: "",
				}, metav1.CreateOptions{})
				o.Expect(err).NotTo(o.HaveOccurred())
				oc.AddResourceToDelete(oauthv1.GroupVersion.WithResource("oauthauthorizetokens"), oauthAuthorizeToken)

				g.By("querying for a token")

				tokenReq := createTokenReq(oauthDiscoveryData.TokenEndpoint)
				tokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
				requestDump, err := httputil.DumpRequest(tokenReq, true)
				o.Expect(err).NotTo(o.HaveOccurred())
				framework.Logf("%v", string(requestDump))

				// we don't really care if this URL is safe
				tr := &http.Transport{
					Proxy:           http.ProxyFromEnvironment,
					TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
				}
				client := &http.Client{Transport: tr}

				tokenResponse, err := client.Do(tokenReq)
				o.Expect(err).NotTo(o.HaveOccurred())
				response, err := httputil.DumpResponse(tokenResponse, true)
				o.Expect(err).NotTo(o.HaveOccurred())
				framework.Logf("%v", string(response))
				o.Expect(tokenResponse.StatusCode).To(o.Equal(http.StatusOK))
			}
		})
	})
})

func queryClientAuthRequest(tokenEndpoint string) *http.Request {
	req, err := http.NewRequest("POST", tokenEndpoint+"?"+
		"grant_type=authorization_code&"+
		"code="+authzToken+"&"+
		"client_id=oauth-client-with-plus&"+
		"client_secret=secret%2Bwith%2Bplus",
		nil)
	o.Expect(err).NotTo(o.HaveOccurred())

	return req
}

func bodyClientAuthRequest(tokenEndpoint string) *http.Request {
	req, err := http.NewRequest("POST", tokenEndpoint,
		bytes.NewBufferString("grant_type=authorization_code&"+
			"code="+authzToken+"&"+
			"client_id=oauth-client-with-plus&"+
			"client_secret=secret%2Bwith%2Bplus",
		),
	)
	o.Expect(err).NotTo(o.HaveOccurred())

	return req
}

func headerClientAuthRequest(tokenEndpoint string) *http.Request {
	req, err := http.NewRequest("POST", tokenEndpoint,
		bytes.NewBufferString("grant_type=authorization_code&"+
			"code="+authzToken,
		),
	)
	o.Expect(err).NotTo(o.HaveOccurred())

	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte("oauth-client-with-plus:secret+with+plus"))))
	return req
}
<commit_msg>Attempt multiple different token requests<commit_after>package oauth

import (
	"bytes"
	"context"
	"crypto/tls"
	"fmt"
	"net/http"
	"net/http/httputil"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kubernetes/test/e2e/framework"

	configv1 "github.com/openshift/api/config/v1"
	oauthv1 "github.com/openshift/api/oauth/v1"
	userv1 "github.com/openshift/api/user/v1"

	exutil "github.com/openshift/origin/test/extended/util"

	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
)

var authzToken, sha256AuthzToken = exutil.GenerateOAuthTokenPair()

var _ = g.Describe("[sig-auth][Feature:OAuthServer]", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("oauthclient-secret-with-plus")
	ctx := context.Background()

	g.Describe("ClientSecretWithPlus", func() {
		g.It(fmt.Sprintf("should create oauthclient"), func() {
			controlPlaneTopology, err := exutil.GetControlPlaneTopology(oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			if *controlPlaneTopology == configv1.ExternalTopologyMode {
				e2eskipper.Skipf("External clusters do not contain oauth-openshift routes")
			}

			g.By("create oauth client")
			oauthClient, err := oc.AdminOauthClient().OauthV1().OAuthClients().Create(ctx, &oauthv1.OAuthClient{
				ObjectMeta: metav1.ObjectMeta{
					Name: "oauth-client-with-plus",
				},
				Secret:            "secret+with+plus",
				RedirectURIs:      []string{"https://www.google.com"},
				GrantMethod:       oauthv1.GrantHandlerAuto,
				ScopeRestrictions: []oauthv1.ScopeRestriction{{ExactValues: []string{"user:full"}}},
			}, metav1.CreateOptions{})
			o.Expect(err).NotTo(o.HaveOccurred())
			oc.AddResourceToDelete(oauthv1.GroupVersion.WithResource("oauthclients"), oauthClient)

			g.By("create synthetic user")

			user, err := oc.AdminUserClient().UserV1().Users().Create(ctx, &userv1.User{
				TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{
					Name: "user-for-plus",
				},
				FullName: "fake user",
			}, metav1.CreateOptions{})
			o.Expect(err).NotTo(o.HaveOccurred())
			oc.AddResourceToDelete(userv1.GroupVersion.WithResource("users"), user)

			oauthDiscoveryData := getOAuthWellKnownData(oc)

			for _, createTokenReq := range []func(string) []*http.Request{
				queryClientAuthRequest,
				bodyClientAuthRequest,
				headerClientAuthRequest,
			} {
				g.By("create synthetic authz token")
				oauthAuthorizeToken, err := oc.AdminOauthClient().OauthV1().OAuthAuthorizeTokens().Create(ctx, &oauthv1.OAuthAuthorizeToken{
					ObjectMeta: metav1.ObjectMeta{
						Name: sha256AuthzToken,
					},
					ClientName:          oauthClient.Name,
					ExpiresIn:           100000000,
					RedirectURI:         "https://www.google.com",
					Scopes:              []string{"user:full"},
					UserName:            user.Name,
					UserUID:             string(user.UID),
					CodeChallenge:       "",
					CodeChallengeMethod: "",
				}, metav1.CreateOptions{})
				o.Expect(err).NotTo(o.HaveOccurred())
				oc.AddResourceToDelete(oauthv1.GroupVersion.WithResource("oauthauthorizetokens"), oauthAuthorizeToken)

				g.By("querying for a token")

				var reqErr []error
				var tokenResponse *http.Response
				for _, tokenReq := range createTokenReq(oauthDiscoveryData.TokenEndpoint) {
					tokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
					requestDump, err := httputil.DumpRequest(tokenReq, true)

					o.Expect(err).NotTo(o.HaveOccurred())
					framework.Logf("%v", string(requestDump))

					// we don't really care if this URL is safe
					tr := &http.Transport{
						Proxy:           http.ProxyFromEnvironment,
						TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
					}
					client := &http.Client{Transport: tr}

					tokenResponse, err = client.Do(tokenReq)
					// if there was an error then append the error and continue
					if err != nil {
						reqErr = append(reqErr, err)
						continue
					}
					// if there was no error then reset the error array and break
					reqErr = nil
					break
				}
				o.Expect(reqErr).To(o.BeNil())
				response, err := httputil.DumpResponse(tokenResponse, true)
				o.Expect(err).NotTo(o.HaveOccurred())
				framework.Logf("%v", string(response))
				o.Expect(tokenResponse.StatusCode).To(o.Equal(http.StatusOK))
			}
		})
	})
})

func queryClientAuthRequest(tokenEndpoint string) []*http.Request {
	req, err := http.NewRequest("POST", tokenEndpoint+"?"+
		"grant_type=authorization_code&"+
		"code="+authzToken+"&"+
		"client_id=oauth-client-with-plus&"+
		"client_secret=secret%2Bwith%2Bplus",
		nil)
	o.Expect(err).NotTo(o.HaveOccurred())

	return []*http.Request{req}
}

func bodyClientAuthRequest(tokenEndpoint string) []*http.Request {
	req, err := http.NewRequest("POST", tokenEndpoint,
		bytes.NewBufferString("grant_type=authorization_code&"+
			"code="+authzToken+"&"+
			"client_id=oauth-client-with-plus&"+
			"client_secret=secret%2Bwith%2Bplus",
		),
	)
	o.Expect(err).NotTo(o.HaveOccurred())

	return []*http.Request{req}
}

func headerClientAuthRequest(tokenEndpoint string) []*http.Request {
	reqNoURLEscape, err := http.NewRequest("POST", tokenEndpoint,
		bytes.NewBufferString("grant_type=authorization_code&"+
			"code="+authzToken,
		),
	)
	o.Expect(err).NotTo(o.HaveOccurred())
	reqNoURLEscape.SetBasicAuth("oauth-client-with-plus", "secret+with+plus")

	reqURLEscape, err := http.NewRequest("POST", tokenEndpoint,
		bytes.NewBufferString("grant_type=authorization_code&"+
			"code="+authzToken,
		),
	)
	o.Expect(err).NotTo(o.HaveOccurred())
	reqURLEscape.SetBasicAuth("oauth-client-with-plus", "secret%2Bwith%2Bplus")
	return []*http.Request{reqNoURLEscape, reqURLEscape}
}
<|endoftext|>