<commit_before>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/itsjamie/gin-cors"
	"github.com/jkomoros/boardgame"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
)

type Server struct {
	managers managerMap
	storage  StorageManager
	//We store the last error so that next time viewHandler is called we can
	//display it. Yes, this is a hack.
	lastErrorMessage string
	config           *ConfigMode
}

type Config struct {
	Dev  *ConfigMode
	Prod *ConfigMode
}

type ConfigMode struct {
	AllowedOrigins    string
	DefaultPort       string
	FirebaseProjectId string
	AdminUserIds      []string
	//This is a dangerous config. Only enable in Dev!
	DisableAdminChecking bool
}

type MoveForm struct {
	Name        string
	Description string
	Fields      []*MoveFormField
}

type MoveFormFieldType int

type MoveFormField struct {
	Name         string
	Type         boardgame.PropertyType
	DefaultValue interface{}
}

func (c *ConfigMode) Validate() error {
	if c.DefaultPort == "" {
		return errors.New("No default port provided")
	}
	//AllowedOrigins will just be default allow
	if c.AllowedOrigins == "" {
		log.Println("No AllowedOrigins found. Defaulting to '*'")
		c.AllowedOrigins = "*"
	}
	return nil
}

const (
	configFileName = "config.SECRET.json"
)

type managerMap map[string]*boardgame.GameManager

/*
NewServer returns a new server. Get it to run by calling Start(). storage
should be the same underlying storage manager that is in use for manager.

Use it like so:

	func main() {
		storage := server.NewDefaultStorageManager()
		defer storage.Close()
		server.NewServer(storage, mygame.NewManager(storage)).Start()
	}

*/
func NewServer(storage StorageManager, managers ...*boardgame.GameManager) *Server {
	result := &Server{
		managers: make(managerMap),
		storage:  storage,
	}

	for _, manager := range managers {
		name := manager.Delegate().Name()
		result.managers[name] = manager
	}

	return result

}

//gameAPISetup fetches the game configured in the URL and puts it in context.
func (s *Server) gameAPISetup(c *gin.Context) {

	id := s.getRequestGameId(c)

	gameName := s.getRequestGameName(c)

	manager := s.managers[gameName]

	if manager == nil {
		log.Println("Couldnt' find manager for", gameName)
		return
	}

	game := manager.Game(id)

	//TODO: figure out a way to return a meaningful error

	if game == nil {
		log.Println("Couldn't find game with id", id)
		return
	}

	if game.Name() != c.Param("name") {
		log.Println("The name of the game was not what we were expecting. Wanted", c.Param("name"), "got", game.Name())
		return
	}

	s.setGame(c, game)

	cookie := s.getRequestCookie(c)

	if cookie == "" {
		log.Println("No cookie set")
		return
	}

	user := s.storage.GetUserByCookie(cookie)

	if user == nil {
		log.Println("No user associated with that cookie")
		return
	}

	s.setUser(c, user)

	s.setAdminAllowed(c, s.calcAdminAllowed(user))

	userIds := s.storage.UserIdsForGame(id)

	if userIds == nil {
		log.Println("No userIds associated with game")
	}

	effectiveViewingAsPlayer, inGame := s.calcViewingAsPlayer(userIds, user)

	if !inGame {
		//We aren't yet in game, so we need to join it.

		if effectiveViewingAsPlayer == boardgame.ObserverPlayerIndex {
			//I guess there weren't any slots.
			log.Println("The user is not in the game, but there are no empty slots to join in as.")
		} else {
			if err := s.storage.SetPlayerForGame(id, int(effectiveViewingAsPlayer), user.Id); err != nil {
				log.Println("Tried to set this user as player", effectiveViewingAsPlayer, "but failed:", err)
			} else {
				log.Println("User joined game", id, "as player", effectiveViewingAsPlayer)
			}
		}
	}

	s.setViewingAsPlayer(c, effectiveViewingAsPlayer)

}

func (s *Server) gameStatusHandler(c *gin.Context) {
	//This handler is designed to be a very simple status marker for the
	//current version of the specific game. It will be hit hard by all
	//clients, repeatedly, so it should be very fast.

	//TODO: use memcache for this handler

	game := s.getGame(c)

	if game == nil {
		c.JSON(http.StatusOK, gin.H{
			//TODO: handle this kind of rendering somewhere central
			"Status": "Failure",
			"Error":  "Not Found",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"Status":  "Success",
		"Version": game.Version(),
	})
}

func (s *Server) newGameHandler(c *gin.Context) {

	manager := s.managers[c.PostForm("manager")]

	if manager == nil {
		//TODO: communicate the error back to the client in a sane way
		panic("Invalid manager" + c.PostForm("manager"))
	}

	game := boardgame.NewGame(manager)

	if err := game.SetUp(0); err != nil {
		//TODO: communicate the error state back to the client in a sane way
		panic(err)
	}

	c.JSON(http.StatusOK, gin.H{
		"Status":   "Success",
		"GameId":   game.Id(),
		"GameName": game.Name(),
	})
}

func (s *Server) listGamesHandler(c *gin.Context) {

	c.JSON(http.StatusOK, gin.H{
		"Status": "Success",
		"Games":  s.storage.ListGames(10),
	})
}

func (s *Server) listManagerHandler(c *gin.Context) {
	var managerNames []string
	for name, _ := range s.managers {
		managerNames = append(managerNames, name)
	}

	c.JSON(http.StatusOK, gin.H{
		"Status":   "Success",
		"Managers": managerNames,
	})
}

func (s *Server) gameViewHandler(c *gin.Context) {

	game := s.getGame(c)

	playerIndex := s.effectivePlayerIndex(c)

	s.gameView(c, game, playerIndex)

}

func (s *Server) gameView(c *gin.Context, game *boardgame.Game, playerIndex boardgame.PlayerIndex) {
	if game == nil {
		c.JSON(http.StatusOK, gin.H{
			//TODO: handle this kind of rendering somewhere central
			"Status": "Failure",
			"Error":  "Not Found",
		})
		return
	}

	if playerIndex == invalidPlayerIndex {
		c.JSON(http.StatusOK, gin.H{
			"Status": "Failure",
			"Error":  "Got invalid playerIndex",
		})
		return
	}

	args := gin.H{
		"Diagram":         game.CurrentState().SanitizedForPlayer(playerIndex).Diagram(),
		"Chest":           s.renderChest(game),
		"Forms":           s.generateForms(game),
		"Game":            game.JSONForPlayer(playerIndex),
		"Error":           s.lastErrorMessage,
		"Status":          "Success",
		"ViewingAsPlayer": playerIndex,
	}

	s.lastErrorMessage = ""

	c.JSON(http.StatusOK, args)
}

func (s *Server) moveHandler(c *gin.Context) {
	if c.Request.Method != http.MethodPost {
		panic("This can only be called as a post.")
	}

	obj, _ := c.Get("game")

	if obj == nil {
		c.JSON(http.StatusOK, gin.H{
			//TODO: handle this kind of rendering somewhere central
			"Status": "Failure",
			"Error":  "Not Found",
		})
		return
	}

	game := obj.(*boardgame.Game)

	if err := s.makeMove(c, game); err != nil {
		s.lastErrorMessage = err.Error()
	}

	c.JSON(http.StatusOK, gin.H{
		"Status": "Success",
		"Error":  s.lastErrorMessage,
	})
}

func (s *Server) makeMove(c *gin.Context, game *boardgame.Game) error {

	//This method is passed a context mainly just to get info from request.

	move := game.PlayerMoveByName(c.PostForm("MoveType"))

	if move == nil {
		return errors.New("Invalid MoveType")
	}

	//TODO: should we use gin's Binding to do this instead?

	for _, field := range formFields(move) {

		rawVal := c.PostForm(field.Name)

		switch field.Type {
		case boardgame.TypeInt:
			if rawVal == "" {
				return errors.New(fmt.Sprint("An int field had no value", field.Name))
			}
			num, err := strconv.Atoi(rawVal)
			if err != nil {
				return errors.New(fmt.Sprint("Couldn't set field", field.Name, err))
			}
			move.ReadSetter().SetProp(field.Name, num)
		case boardgame.TypeBool:
			if rawVal == "" {
				move.ReadSetter().SetProp(field.Name, false)
				continue
			}
			num, err := strconv.Atoi(rawVal)
			if err != nil {
				return errors.New(fmt.Sprint("Couldn't set field", field.Name, err))
			}
			if num == 1 {
				move.ReadSetter().SetProp(field.Name, true)
			} else {
				move.ReadSetter().SetProp(field.Name, false)
			}
		case boardgame.TypeIllegal:
			return errors.New(fmt.Sprint("Field", field.Name, "was an unknown value type"))
		}
	}

	obj, ok := c.Get("ViewingPlayerIndex")

	var proposer boardgame.PlayerIndex

	if ok {
		proposer = obj.(boardgame.PlayerIndex)
	}

	obj, ok = c.Get("adminAllowed")

	adminAllowed := false

	if ok {
		adminAllowed = obj.(bool)
	}

	if adminAllowed && c.PostForm("admin") == "1" {
		proposer = boardgame.AdminPlayerIndex
	}

	if err := <-game.ProposeMove(move, proposer); err != nil {
		return errors.New(fmt.Sprint("Applying move failed: ", err))
	}
	//TODO: it would be nice if we could show which fixup moves we made, too,
	//somehow.

	return nil
}

func (s *Server) generateForms(game *boardgame.Game) []*MoveForm {

	var result []*MoveForm

	for _, move := range game.PlayerMoves() {

		move.DefaultsForState(game.CurrentState())

		moveItem := &MoveForm{
			Name:        move.Name(),
			Description: move.Description(),
			Fields:      formFields(move),
		}
		result = append(result, moveItem)
	}

	return result
}

func formFields(move boardgame.Move) []*MoveFormField {

	var result []*MoveFormField

	for fieldName, fieldType := range move.ReadSetter().Props() {

		val, _ := move.ReadSetter().Prop(fieldName)

		result = append(result, &MoveFormField{
			Name:         fieldName,
			Type:         fieldType,
			DefaultValue: val,
		})

	}

	return result
}

func (s *Server) renderChest(game *boardgame.Game) map[string][]interface{} {
	//Substantially copied from cli.renderChest().

	deck := make(map[string][]interface{})

	for _, name := range game.Chest().DeckNames() {

		components := game.Chest().Deck(name).Components()

		values := make([]interface{}, len(components))

		for i, component := range components {
			values[i] = struct {
				Index  int
				Values interface{}
			}{
				i,
				component.Values,
			}
		}

		deck[name] = values
	}

	return deck
}

//Start is where you start the server, and it never returns until it's time to shut down.
func (s *Server) Start() {

	if _, err := os.Stat(configFileName); os.IsNotExist(err) {
		log.Println("Couldn't find a " + configFileName + " in current directory. This file is required. Copy a starter one from boardgame/server/api/config.SAMPLE.json")
		return
	}

	contents, err := ioutil.ReadFile(configFileName)

	if err != nil {
		log.Println("Couldn't read config file:", err)
		return
	}

	var config Config

	if err := json.Unmarshal(contents, &config); err != nil {
		log.Println("couldn't unmarshal config file:", err)
		return
	}

	log.Println("Environment Variables")
	//Dbug print out the current environment
	for _, config := range os.Environ() {
		log.Println("Environ:", config)
	}

	if v := os.Getenv("GIN_MODE"); v == "release" {
		log.Println("Using release mode config")
		s.config = config.Prod
	} else {
		log.Println("Using dev mode config")
		s.config = config.Dev
	}

	if err := s.config.Validate(); err != nil {
		log.Println("The provided config was not valid: ", err)
		return
	}

	router := gin.Default()

	//We have everything prefixed by /api just in case at some point we do
	//want to host both static and api on the same logical server.
	mainGroup := router.Group("/api")
	mainGroup.Use(cors.Middleware(cors.Config{
		Origins:        s.config.AllowedOrigins,
		RequestHeaders: "content-type, Origin",
		ExposedHeaders: "content-type",
		Methods:        "GET, POST",
		Credentials:    true,
	}))

	{
		mainGroup.GET("list/game", s.listGamesHandler)
		mainGroup.POST("new/game", s.newGameHandler)
		mainGroup.GET("list/manager", s.listManagerHandler)

		mainGroup.POST("auth/cookie", s.authCookieHandler)
		mainGroup.OPTIONS("auth/cookie", s.authCookieHandler)

		gameAPIGroup := mainGroup.Group("game/:name/:id")
		gameAPIGroup.Use(s.gameAPISetup)
		{
			gameAPIGroup.GET("view", s.gameViewHandler)
			gameAPIGroup.POST("move", s.moveHandler)
			gameAPIGroup.GET("status", s.gameStatusHandler)
		}
	}

	if p := os.Getenv("PORT"); p != "" {
		router.Run(":" + p)
	} else {
		router.Run(":" + s.config.DefaultPort)
	}

}
<commit_msg>Use s.getGame in moveHandler. Part of #92.<commit_after>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/itsjamie/gin-cors"
	"github.com/jkomoros/boardgame"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
)

type Server struct {
	managers managerMap
	storage  StorageManager
	//We store the last error so that next time viewHandler is called we can
	//display it. Yes, this is a hack.
	lastErrorMessage string
	config           *ConfigMode
}

type Config struct {
	Dev  *ConfigMode
	Prod *ConfigMode
}

type ConfigMode struct {
	AllowedOrigins    string
	DefaultPort       string
	FirebaseProjectId string
	AdminUserIds      []string
	//This is a dangerous config. Only enable in Dev!
	DisableAdminChecking bool
}

type MoveForm struct {
	Name        string
	Description string
	Fields      []*MoveFormField
}

type MoveFormFieldType int

type MoveFormField struct {
	Name         string
	Type         boardgame.PropertyType
	DefaultValue interface{}
}

func (c *ConfigMode) Validate() error {
	if c.DefaultPort == "" {
		return errors.New("No default port provided")
	}
	//AllowedOrigins will just be default allow
	if c.AllowedOrigins == "" {
		log.Println("No AllowedOrigins found. Defaulting to '*'")
		c.AllowedOrigins = "*"
	}
	return nil
}

const (
	configFileName = "config.SECRET.json"
)

type managerMap map[string]*boardgame.GameManager

/*
NewServer returns a new server. Get it to run by calling Start(). storage
should be the same underlying storage manager that is in use for manager.

Use it like so:

	func main() {
		storage := server.NewDefaultStorageManager()
		defer storage.Close()
		server.NewServer(storage, mygame.NewManager(storage)).Start()
	}

*/
func NewServer(storage StorageManager, managers ...*boardgame.GameManager) *Server {
	result := &Server{
		managers: make(managerMap),
		storage:  storage,
	}

	for _, manager := range managers {
		name := manager.Delegate().Name()
		result.managers[name] = manager
	}

	return result

}

//gameAPISetup fetches the game configured in the URL and puts it in context.
func (s *Server) gameAPISetup(c *gin.Context) {

	id := s.getRequestGameId(c)

	gameName := s.getRequestGameName(c)

	manager := s.managers[gameName]

	if manager == nil {
		log.Println("Couldnt' find manager for", gameName)
		return
	}

	game := manager.Game(id)

	//TODO: figure out a way to return a meaningful error

	if game == nil {
		log.Println("Couldn't find game with id", id)
		return
	}

	if game.Name() != c.Param("name") {
		log.Println("The name of the game was not what we were expecting. Wanted", c.Param("name"), "got", game.Name())
		return
	}

	s.setGame(c, game)

	cookie := s.getRequestCookie(c)

	if cookie == "" {
		log.Println("No cookie set")
		return
	}

	user := s.storage.GetUserByCookie(cookie)

	if user == nil {
		log.Println("No user associated with that cookie")
		return
	}

	s.setUser(c, user)

	s.setAdminAllowed(c, s.calcAdminAllowed(user))

	userIds := s.storage.UserIdsForGame(id)

	if userIds == nil {
		log.Println("No userIds associated with game")
	}

	effectiveViewingAsPlayer, inGame := s.calcViewingAsPlayer(userIds, user)

	if !inGame {
		//We aren't yet in game, so we need to join it.

		if effectiveViewingAsPlayer == boardgame.ObserverPlayerIndex {
			//I guess there weren't any slots.
			log.Println("The user is not in the game, but there are no empty slots to join in as.")
		} else {
			if err := s.storage.SetPlayerForGame(id, int(effectiveViewingAsPlayer), user.Id); err != nil {
				log.Println("Tried to set this user as player", effectiveViewingAsPlayer, "but failed:", err)
			} else {
				log.Println("User joined game", id, "as player", effectiveViewingAsPlayer)
			}
		}
	}

	s.setViewingAsPlayer(c, effectiveViewingAsPlayer)

}

func (s *Server) gameStatusHandler(c *gin.Context) {
	//This handler is designed to be a very simple status marker for the
	//current version of the specific game. It will be hit hard by all
	//clients, repeatedly, so it should be very fast.

	//TODO: use memcache for this handler

	game := s.getGame(c)

	if game == nil {
		c.JSON(http.StatusOK, gin.H{
			//TODO: handle this kind of rendering somewhere central
			"Status": "Failure",
			"Error":  "Not Found",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"Status":  "Success",
		"Version": game.Version(),
	})
}

func (s *Server) newGameHandler(c *gin.Context) {

	manager := s.managers[c.PostForm("manager")]

	if manager == nil {
		//TODO: communicate the error back to the client in a sane way
		panic("Invalid manager" + c.PostForm("manager"))
	}

	game := boardgame.NewGame(manager)

	if err := game.SetUp(0); err != nil {
		//TODO: communicate the error state back to the client in a sane way
		panic(err)
	}

	c.JSON(http.StatusOK, gin.H{
		"Status":   "Success",
		"GameId":   game.Id(),
		"GameName": game.Name(),
	})
}

func (s *Server) listGamesHandler(c *gin.Context) {

	c.JSON(http.StatusOK, gin.H{
		"Status": "Success",
		"Games":  s.storage.ListGames(10),
	})
}

func (s *Server) listManagerHandler(c *gin.Context) {
	var managerNames []string
	for name, _ := range s.managers {
		managerNames = append(managerNames, name)
	}

	c.JSON(http.StatusOK, gin.H{
		"Status":   "Success",
		"Managers": managerNames,
	})
}

func (s *Server) gameViewHandler(c *gin.Context) {

	game := s.getGame(c)

	playerIndex := s.effectivePlayerIndex(c)

	s.gameView(c, game, playerIndex)

}

func (s *Server) gameView(c *gin.Context, game *boardgame.Game, playerIndex boardgame.PlayerIndex) {
	if game == nil {
		c.JSON(http.StatusOK, gin.H{
			//TODO: handle this kind of rendering somewhere central
			"Status": "Failure",
			"Error":  "Not Found",
		})
		return
	}

	if playerIndex == invalidPlayerIndex {
		c.JSON(http.StatusOK, gin.H{
			"Status": "Failure",
			"Error":  "Got invalid playerIndex",
		})
		return
	}

	args := gin.H{
		"Diagram":         game.CurrentState().SanitizedForPlayer(playerIndex).Diagram(),
		"Chest":           s.renderChest(game),
		"Forms":           s.generateForms(game),
		"Game":            game.JSONForPlayer(playerIndex),
		"Error":           s.lastErrorMessage,
		"Status":          "Success",
		"ViewingAsPlayer": playerIndex,
	}

	s.lastErrorMessage = ""

	c.JSON(http.StatusOK, args)
}

func (s *Server) moveHandler(c *gin.Context) {
	if c.Request.Method != http.MethodPost {
		panic("This can only be called as a post.")
	}

	game := s.getGame(c)

	if game == nil {
		c.JSON(http.StatusOK, gin.H{
			//TODO: handle this kind of rendering somewhere central
			"Status": "Failure",
			"Error":  "Not Found",
		})
		return
	}

	if err := s.makeMove(c, game); err != nil {
		s.lastErrorMessage = err.Error()
	}

	c.JSON(http.StatusOK, gin.H{
		"Status": "Success",
		"Error":  s.lastErrorMessage,
	})
}

func (s *Server) makeMove(c *gin.Context, game *boardgame.Game) error {

	//This method is passed a context mainly just to get info from request.

	move := game.PlayerMoveByName(c.PostForm("MoveType"))

	if move == nil {
		return errors.New("Invalid MoveType")
	}

	//TODO: should we use gin's Binding to do this instead?

	for _, field := range formFields(move) {

		rawVal := c.PostForm(field.Name)

		switch field.Type {
		case boardgame.TypeInt:
			if rawVal == "" {
				return errors.New(fmt.Sprint("An int field had no value", field.Name))
			}
			num, err := strconv.Atoi(rawVal)
			if err != nil {
				return errors.New(fmt.Sprint("Couldn't set field", field.Name, err))
			}
			move.ReadSetter().SetProp(field.Name, num)
		case boardgame.TypeBool:
			if rawVal == "" {
				move.ReadSetter().SetProp(field.Name, false)
				continue
			}
			num, err := strconv.Atoi(rawVal)
			if err != nil {
				return errors.New(fmt.Sprint("Couldn't set field", field.Name, err))
			}
			if num == 1 {
				move.ReadSetter().SetProp(field.Name, true)
			} else {
				move.ReadSetter().SetProp(field.Name, false)
			}
		case boardgame.TypeIllegal:
			return errors.New(fmt.Sprint("Field", field.Name, "was an unknown value type"))
		}
	}

	obj, ok := c.Get("ViewingPlayerIndex")

	var proposer boardgame.PlayerIndex

	if ok {
		proposer = obj.(boardgame.PlayerIndex)
	}

	obj, ok = c.Get("adminAllowed")

	adminAllowed := false

	if ok {
		adminAllowed = obj.(bool)
	}

	if adminAllowed && c.PostForm("admin") == "1" {
		proposer = boardgame.AdminPlayerIndex
	}

	if err := <-game.ProposeMove(move, proposer); err != nil {
		return errors.New(fmt.Sprint("Applying move failed: ", err))
	}
	//TODO: it would be nice if we could show which fixup moves we made, too,
	//somehow.

	return nil
}

func (s *Server) generateForms(game *boardgame.Game) []*MoveForm {

	var result []*MoveForm

	for _, move := range game.PlayerMoves() {

		move.DefaultsForState(game.CurrentState())

		moveItem := &MoveForm{
			Name:        move.Name(),
			Description: move.Description(),
			Fields:      formFields(move),
		}
		result = append(result, moveItem)
	}

	return result
}

func formFields(move boardgame.Move) []*MoveFormField {

	var result []*MoveFormField

	for fieldName, fieldType := range move.ReadSetter().Props() {

		val, _ := move.ReadSetter().Prop(fieldName)

		result = append(result, &MoveFormField{
			Name:         fieldName,
			Type:         fieldType,
			DefaultValue: val,
		})

	}

	return result
}

func (s *Server) renderChest(game *boardgame.Game) map[string][]interface{} {
	//Substantially copied from cli.renderChest().

	deck := make(map[string][]interface{})

	for _, name := range game.Chest().DeckNames() {

		components := game.Chest().Deck(name).Components()

		values := make([]interface{}, len(components))

		for i, component := range components {
			values[i] = struct {
				Index  int
				Values interface{}
			}{
				i,
				component.Values,
			}
		}

		deck[name] = values
	}

	return deck
}

//Start is where you start the server, and it never returns until it's time to shut down.
func (s *Server) Start() {

	if _, err := os.Stat(configFileName); os.IsNotExist(err) {
		log.Println("Couldn't find a " + configFileName + " in current directory. This file is required. Copy a starter one from boardgame/server/api/config.SAMPLE.json")
		return
	}

	contents, err := ioutil.ReadFile(configFileName)

	if err != nil {
		log.Println("Couldn't read config file:", err)
		return
	}

	var config Config

	if err := json.Unmarshal(contents, &config); err != nil {
		log.Println("couldn't unmarshal config file:", err)
		return
	}

	log.Println("Environment Variables")
	//Dbug print out the current environment
	for _, config := range os.Environ() {
		log.Println("Environ:", config)
	}

	if v := os.Getenv("GIN_MODE"); v == "release" {
		log.Println("Using release mode config")
		s.config = config.Prod
	} else {
		log.Println("Using dev mode config")
		s.config = config.Dev
	}

	if err := s.config.Validate(); err != nil {
		log.Println("The provided config was not valid: ", err)
		return
	}

	router := gin.Default()

	//We have everything prefixed by /api just in case at some point we do
	//want to host both static and api on the same logical server.
	mainGroup := router.Group("/api")
	mainGroup.Use(cors.Middleware(cors.Config{
		Origins:        s.config.AllowedOrigins,
		RequestHeaders: "content-type, Origin",
		ExposedHeaders: "content-type",
		Methods:        "GET, POST",
		Credentials:    true,
	}))

	{
		mainGroup.GET("list/game", s.listGamesHandler)
		mainGroup.POST("new/game", s.newGameHandler)
		mainGroup.GET("list/manager", s.listManagerHandler)

		mainGroup.POST("auth/cookie", s.authCookieHandler)
		mainGroup.OPTIONS("auth/cookie", s.authCookieHandler)

		gameAPIGroup := mainGroup.Group("game/:name/:id")
		gameAPIGroup.Use(s.gameAPISetup)
		{
			gameAPIGroup.GET("view", s.gameViewHandler)
			gameAPIGroup.POST("move", s.moveHandler)
			gameAPIGroup.GET("status", s.gameStatusHandler)
		}
	}

	if p := os.Getenv("PORT"); p != "" {
		router.Run(":" + p)
	} else {
		router.Run(":" + s.config.DefaultPort)
	}

}
<|endoftext|>


<commit_before>// Copyright 2015 The Prometheus 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.

// +build !nocpu

package collector

import (
	"errors"
	"fmt"

	"github.com/prometheus/client_golang/prometheus"
)

/*
#cgo LDFLAGS:
#include <fcntl.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <kinfo.h>

static int mibs_set_up = 0;

static int mib_kern_cp_times[2];
static size_t mib_kern_cp_times_len = 2;

static const int mib_hw_ncpu[] = {CTL_HW, HW_NCPU};
static const size_t mib_hw_ncpu_len = 2;

static const int mib_kern_clockrate[] = {CTL_KERN, KERN_CLOCKRATE};
static size_t mib_kern_clockrate_len = 2;

// Setup method for MIBs not available as constants.
// Calls to this method must be synchronized externally.
int setupSysctlMIBs() {
	int ret = sysctlnametomib("kern.cputime", mib_kern_cp_times, &mib_kern_cp_times_len);
	if (ret == 0) mibs_set_up = 1;
	return ret;
}

int getCPUTimes(int *ncpu, struct kinfo_cputime *cputime, uint64_t *cpu_user) {
	// // Assert that mibs are set up through setupSysctlMIBs
	// if (!mibs_set_up) {
	// 	return -1;
	// }

	// // Retrieve number of cpu cores
	// size_t ncpu_size = sizeof(*ncpu);
	// if (sysctl(mib_hw_ncpu, mib_hw_ncpu_len, ncpu, &ncpu_size, NULL, 0) == -1 ||
	//     sizeof(*ncpu) != ncpu_size) {
	// 	return -1;
	// }

	size_t len;

	// Get number of cpu cores.
	int mib[2];
	mib[0] = CTL_HW;
	mib[1] = HW_NCPU;
	len = sizeof(*ncpu);
	if (sysctl(mib, 2, ncpu, &len, NULL, 0)) {
		return -1;
	}

	// Retrieve clockrate
	struct clockinfo clockrate;
	size_t clockrate_size = sizeof(clockrate);
	if (sysctl(mib_kern_clockrate, mib_kern_clockrate_len, &clockrate, &clockrate_size, NULL, 0) == -1 ||
	    sizeof(clockrate) != clockrate_size) {
		return -1;
	}

	// // Retrieve cp_times values
	// *cp_times_length = (*ncpu) * CPUSTATES;
        //
	// long cp_times[*cp_times_length];
	// size_t cp_times_size = sizeof(cp_times);

	// Get the cpu times.
	struct kinfo_cputime cp_t[*ncpu];
	bzero(cp_t, sizeof(struct kinfo_cputime)*(*ncpu));
	len = sizeof(cp_t[0])*(*ncpu);
	if (sysctlbyname("kern.cputime", &cp_t, &len, NULL, 0)) {
		return -1;
	}

	*cpu_user = cp_t[0].cp_user;
	*cputime = cp_t[0];
	// This results in outputting:
	// {1362514572 273421 845667973 12986861 17529717536 0 0 0 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]}
	// So, we have the first 5 numbers from the first cpu.
	// Need to figure out how to get the second cpu, i.e. cputime needs to be created with [2]

	// Compute absolute time for different CPU states
	// long cpufreq = clockrate.stathz > 0 ? clockrate.stathz : clockrate.hz;
	// *cpu_times = (double *) malloc(sizeof(double)*(len));
	// for (int i = 0; i < (len); i++) {
	// 	(*cpu_times)[i] = ((double) cp_t[i]) / cpufreq;
	// }

	return 0;

}

void freeCPUTimes(double *cpu_times) {
	free(cpu_times);
}

*/
import "C"

const maxCPUTimesLen = C.MAXCPU * C.CPUSTATES

type statCollector struct {
	cpu *prometheus.CounterVec
}

func init() {
	Factories["cpu"] = NewStatCollector
}

// Takes a prometheus registry and returns a new Collector exposing
// CPU stats.
func NewStatCollector() (Collector, error) {
	if C.setupSysctlMIBs() == -1 {
		return nil, errors.New("could not initialize sysctl MIBs")
	}
	return &statCollector{
		cpu: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "cpu_seconds_total",
				Help:      "Seconds the CPU spent in each mode.",
			},
			[]string{"cpu", "mode"},
		),
	}, nil
}

type kinfoCPUTime struct {
	cp_user, cp_nice, cp_sys, cp_intr, cp_idle uint64
}

// Expose CPU stats using sysctl.
func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) {

	// We want time spent per-cpu per CPUSTATE.
	// CPUSTATES (number of CPUSTATES) is defined as 5U.
	// Order: CP_USER | CP_NICE | CP_SYS | CP_IDLE | CP_INTR
	// sysctl kern.cp_times provides hw.ncpu * CPUSTATES long integers:
	//   hw.ncpu * (space-separated list of the above variables)
	//
	// Each value is a counter incremented at frequency
	//   kern.clockrate.(stathz | hz)
	//
	// Look into sys/kern/kern_clock.c for details.

	var ncpu C.int
	var cpuTimesC C.struct_kinfo_cputime
	var cpuTimesLength C.uint64_t

	if C.getCPUTimes(&ncpu, &cpuTimesC, &cpuTimesLength) == -1 {
		return errors.New("could not retrieve CPU times")
	}
	// defer C.freeCPUTimes(cpuTimesC)
	fmt.Println(cpuTimesC)
	fmt.Println(uint64(cpuTimesLength))
	return errors.New("early kill")
	if cpuTimesLength > maxCPUTimesLen {
		return errors.New("more CPU's than MAXCPU?")
	}

	// Convert C.double array to Go array (https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices).
	// cpuTimes := (*[maxCPUTimesLen]C.double)(unsafe.Pointer(cpuTimesC))[:cpuTimesLength:cpuTimesLength]
	//
	// for cpu := 0; cpu < int(ncpu); cpu++ {
	// 	base_idx := C.CPUSTATES * cpu
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "user"}).Set(float64(cpuTimes[base_idx+C.CP_USER]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "nice"}).Set(float64(cpuTimes[base_idx+C.CP_NICE]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "system"}).Set(float64(cpuTimes[base_idx+C.CP_SYS]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "interrupt"}).Set(float64(cpuTimes[base_idx+C.CP_INTR]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "idle"}).Set(float64(cpuTimes[base_idx+C.CP_IDLE]))
	// }

	c.cpu.Collect(ch)
	return err
}
<commit_msg>Creating slice from C-array<commit_after>// Copyright 2015 The Prometheus 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.

// +build !nocpu

package collector

import (
	"errors"
	"fmt"
	"unsafe"

	"github.com/prometheus/client_golang/prometheus"
)

/*
#cgo LDFLAGS:
#include <fcntl.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <kinfo.h>

static int mibs_set_up = 0;

static int mib_kern_cp_times[2];
static size_t mib_kern_cp_times_len = 2;

static const int mib_hw_ncpu[] = {CTL_HW, HW_NCPU};
static const size_t mib_hw_ncpu_len = 2;

static const int mib_kern_clockrate[] = {CTL_KERN, KERN_CLOCKRATE};
static size_t mib_kern_clockrate_len = 2;

// Setup method for MIBs not available as constants.
// Calls to this method must be synchronized externally.
int setupSysctlMIBs() {
	int ret = sysctlnametomib("kern.cputime", mib_kern_cp_times, &mib_kern_cp_times_len);
	if (ret == 0) mibs_set_up = 1;
	return ret;
}

int getCPUTimes(int *ncpu, struct kinfo_cputime *cputime, uint64_t *cpu_user) {
	// // Assert that mibs are set up through setupSysctlMIBs
	// if (!mibs_set_up) {
	// 	return -1;
	// }

	// // Retrieve number of cpu cores
	// size_t ncpu_size = sizeof(*ncpu);
	// if (sysctl(mib_hw_ncpu, mib_hw_ncpu_len, ncpu, &ncpu_size, NULL, 0) == -1 ||
	//     sizeof(*ncpu) != ncpu_size) {
	// 	return -1;
	// }

	size_t len;

	// Get number of cpu cores.
	int mib[2];
	mib[0] = CTL_HW;
	mib[1] = HW_NCPU;
	len = sizeof(*ncpu);
	if (sysctl(mib, 2, ncpu, &len, NULL, 0)) {
		return -1;
	}

	// Retrieve clockrate
	struct clockinfo clockrate;
	size_t clockrate_size = sizeof(clockrate);
	if (sysctl(mib_kern_clockrate, mib_kern_clockrate_len, &clockrate, &clockrate_size, NULL, 0) == -1 ||
	    sizeof(clockrate) != clockrate_size) {
		return -1;
	}

	// // Retrieve cp_times values
	// *cp_times_length = (*ncpu) * CPUSTATES;
        //
	// long cp_times[*cp_times_length];
	// size_t cp_times_size = sizeof(cp_times);

	// Get the cpu times.
	struct kinfo_cputime cp_t[*ncpu];
	bzero(cp_t, sizeof(struct kinfo_cputime)*(*ncpu));
	len = sizeof(cp_t[0])*(*ncpu);
	if (sysctlbyname("kern.cputime", &cp_t, &len, NULL, 0)) {
		return -1;
	}

	*cpu_user = cp_t[0].cp_user;
	*cputime = cp_t[0];
	// This results in outputting:
	// {1362514572 273421 845667973 12986861 17529717536 0 0 0 0 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]}
	// So, we have the first 5 numbers from the first cpu.
	// Need to figure out how to get the second cpu, i.e. cputime needs to be created with [2]

	// Compute absolute time for different CPU states
	// long cpufreq = clockrate.stathz > 0 ? clockrate.stathz : clockrate.hz;
	// *cpu_times = (double *) malloc(sizeof(double)*(len));
	// for (int i = 0; i < (len); i++) {
	// 	(*cpu_times)[i] = ((double) cp_t[i]) / cpufreq;
	// }

	return 0;

}

void freeCPUTimes(double *cpu_times) {
	free(cpu_times);
}

*/
import "C"

const maxCPUTimesLen = C.MAXCPU * C.CPUSTATES

type statCollector struct {
	cpu *prometheus.CounterVec
}

func init() {
	Factories["cpu"] = NewStatCollector
}

// Takes a prometheus registry and returns a new Collector exposing
// CPU stats.
func NewStatCollector() (Collector, error) {
	if C.setupSysctlMIBs() == -1 {
		return nil, errors.New("could not initialize sysctl MIBs")
	}
	return &statCollector{
		cpu: prometheus.NewCounterVec(
			prometheus.CounterOpts{
				Namespace: Namespace,
				Name:      "cpu_seconds_total",
				Help:      "Seconds the CPU spent in each mode.",
			},
			[]string{"cpu", "mode"},
		),
	}, nil
}

type kinfoCPUTime struct {
	cp_user, cp_nice, cp_sys, cp_intr, cp_idle uint64
}

// Expose CPU stats using sysctl.
func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) {

	// We want time spent per-cpu per CPUSTATE.
	// CPUSTATES (number of CPUSTATES) is defined as 5U.
	// Order: CP_USER | CP_NICE | CP_SYS | CP_IDLE | CP_INTR
	// sysctl kern.cp_times provides hw.ncpu * CPUSTATES long integers:
	//   hw.ncpu * (space-separated list of the above variables)
	//
	// Each value is a counter incremented at frequency
	//   kern.clockrate.(stathz | hz)
	//
	// Look into sys/kern/kern_clock.c for details.

	var ncpu C.int
	var cpuTimesC C.struct_kinfo_cputime
	var cpuTimesLength C.uint64_t

	if C.getCPUTimes(&ncpu, &cpuTimesC, &cpuTimesLength) == -1 {
		return errors.New("could not retrieve CPU times")
	}
	// TODO: Remember to free variables
	// defer C.freeCPUTimes(cpuTimesC)

	cpuTimes := (*[1 << 30]C.struct_kinfo_cputime)(unsafe.Pointer(&cpuTimesC))[:ncpu:ncpu]
	fmt.Println(cpuTimes)
	return errors.New("early kill")
	if cpuTimesLength > maxCPUTimesLen {
		return errors.New("more CPU's than MAXCPU?")
	}

	// Convert C.double array to Go array (https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices).
	// cpuTimes := (*[maxCPUTimesLen]C.double)(unsafe.Pointer(cpuTimesC))[:cpuTimesLength:cpuTimesLength]
	//
	// for cpu := 0; cpu < int(ncpu); cpu++ {
	// 	base_idx := C.CPUSTATES * cpu
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "user"}).Set(float64(cpuTimes[base_idx+C.CP_USER]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "nice"}).Set(float64(cpuTimes[base_idx+C.CP_NICE]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "system"}).Set(float64(cpuTimes[base_idx+C.CP_SYS]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "interrupt"}).Set(float64(cpuTimes[base_idx+C.CP_INTR]))
	// 	c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(cpu), "mode": "idle"}).Set(float64(cpuTimes[base_idx+C.CP_IDLE]))
	// }

	c.cpu.Collect(ch)
	return err
}
<|endoftext|>


<commit_before>package gerrit

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

const (
	// testGerritInstanceURL is a test instance url that won`t be called
	testGerritInstanceURL = "https://go-review.googlesource.com/"
)

var (
	// testMux is the HTTP request multiplexer used with the test server.
	testMux *http.ServeMux

	// testClient is the gerrit client being tested.
	testClient *Client

	// testServer is a test HTTP server used to provide mock API responses.
	testServer *httptest.Server
)

type testValues map[string]string

// setup sets up a test HTTP server along with a gerrit.Client that is configured to talk to that test server.
// Tests should register handlers on mux which provide mock responses for the API method being tested.
func setup() {
	// Test server
	testMux = http.NewServeMux()
	testServer = httptest.NewServer(testMux)

	// gerrit client configured to use test server
	testClient, _ = NewClient(testServer.URL, nil)
}

// teardown closes the test HTTP server.
func teardown() {
	testServer.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

func testFormValues(t *testing.T, r *http.Request, values testValues) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}

	r.ParseForm()
	if got := r.Form; !reflect.DeepEqual(got, want) {
		t.Errorf("Request parameters: %v, want %v", got, want)
	}
}

func TestNewClient_NoGerritInstance(t *testing.T) {
	mockData := []string{"", "://not-existing"}
	for _, data := range mockData {
		c, err := NewClient(data, nil)
		if c != nil {
			t.Errorf("NewClient return is not nil. Expected no client. Go %+v", c)
		}
		if err == nil {
			t.Error("No error occured by empty Gerrit Instance. Expected one.")
		}
	}
}

func TestNewClient_HttpClient(t *testing.T) {
	customHTTPClient := &http.Client{
		Timeout: 30 * time.Second,
	}
	mockData := []struct {
		HTTPClient         *http.Client
		ExpectedHTTPClient *http.Client
	}{
		{nil, http.DefaultClient},
		{customHTTPClient, customHTTPClient},
	}

	for _, mock := range mockData {
		c, err := NewClient("https://gerrit-review.googlesource.com/", mock.HTTPClient)
		if err != nil {
			t.Errorf("An error occured. Expected nil. Got %+v.", err)
		}
		if reflect.DeepEqual(c.client, mock.ExpectedHTTPClient) == false {
			t.Errorf("Wrong HTTP Client. Expected %+v. Got %+v", mock.ExpectedHTTPClient, c.client)
		}
	}
}

func TestNewClient_Services(t *testing.T) {
	c, err := NewClient("https://gerrit-review.googlesource.com/", nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	if c.Access == nil {
		t.Error("No AccessService found.")
	}
	if c.Accounts == nil {
		t.Error("No AccountsService found.")
	}
	if c.Changes == nil {
		t.Error("No ChangesService found.")
	}
	if c.Config == nil {
		t.Error("No ConfigService found.")
	}
	if c.Groups == nil {
		t.Error("No GroupsService found.")
	}
	if c.Plugins == nil {
		t.Error("No PluginsService found.")
	}
	if c.Projects == nil {
		t.Error("No ProjectsService found.")
	}
}

func TestNewRequest(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	inURL, outURL := "/foo", testGerritInstanceURL+"foo"
	inBody, outBody := &PermissionRuleInfo{Action: "ALLOW", Force: true, Min: 0, Max: 0}, `{"action":"ALLOW","force":true,"min":0,"max":0}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// Test that relative URL was expanded
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
	}

	// Test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want)
	}
}

func TestNewRequest_InvalidJSON(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	type T struct {
		A map[int]interface{}
	}
	_, err = c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %#v.", err)
	}
}

func testURLParseError(t *testing.T, err error) {
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

func TestNewRequest_BadURL(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	_, err = c.NewRequest("GET", ":", nil)
	testURLParseError(t, err)
}

// If a nil body is passed to gerrit.NewRequest, make sure that nil is also passed to http.NewRequest.
// In most cases, passing an io.Reader that returns no content is fine,
// since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
// However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
func TestNewRequest_EmptyBody(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest returned unexpected error: %v", err)
	}
	if req.Body != nil {
		t.Fatalf("constructed request contains a non-nil Body")
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `)]}'`+"\n"+`{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	testClient.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_ioWriter(t *testing.T) {
	setup()
	defer teardown()
	content := `)]}'` + "\n" + `{"A":"a"}`

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, content)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	var buf []byte
	actual := bytes.NewBuffer(buf)
	testClient.Do(req, actual)

	expected := []byte(content)
	if !reflect.DeepEqual(actual.Bytes(), expected) {
		t.Errorf("Response body = %v, want %v", actual, string(expected))
	}
}

func TestDo_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do() function.
// A redirect loop is pretty unlikely to occur within the Gerrit API, but does allow us to exercise the right code path.
func TestDo_RedirectLoop(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %#v.", err)
	}
}

func TestRemoveMagicPrefixLine(t *testing.T) {
	mockData := []struct {
		Current, Expected []byte
	}{
		{[]byte(`{"A":"a"}`), []byte(`{"A":"a"}`)},
		{[]byte(`)]}'` + "\n" + `{"A":"a"}`), []byte(`{"A":"a"}`)},
	}
	for _, mock := range mockData {
		body := RemoveMagicPrefixLine(mock.Current)
		if !reflect.DeepEqual(body, mock.Expected) {
			t.Errorf("Response body = %v, want %v", body, mock.Expected)
		}
	}
}
<commit_msg>Added unit test to check Auth service<commit_after>package gerrit

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

const (
	// testGerritInstanceURL is a test instance url that won`t be called
	testGerritInstanceURL = "https://go-review.googlesource.com/"
)

var (
	// testMux is the HTTP request multiplexer used with the test server.
	testMux *http.ServeMux

	// testClient is the gerrit client being tested.
	testClient *Client

	// testServer is a test HTTP server used to provide mock API responses.
	testServer *httptest.Server
)

type testValues map[string]string

// setup sets up a test HTTP server along with a gerrit.Client that is configured to talk to that test server.
// Tests should register handlers on mux which provide mock responses for the API method being tested.
func setup() {
	// Test server
	testMux = http.NewServeMux()
	testServer = httptest.NewServer(testMux)

	// gerrit client configured to use test server
	testClient, _ = NewClient(testServer.URL, nil)
}

// teardown closes the test HTTP server.
func teardown() {
	testServer.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

func testFormValues(t *testing.T, r *http.Request, values testValues) {
	want := url.Values{}
	for k, v := range values {
		want.Add(k, v)
	}

	r.ParseForm()
	if got := r.Form; !reflect.DeepEqual(got, want) {
		t.Errorf("Request parameters: %v, want %v", got, want)
	}
}

func TestNewClient_NoGerritInstance(t *testing.T) {
	mockData := []string{"", "://not-existing"}
	for _, data := range mockData {
		c, err := NewClient(data, nil)
		if c != nil {
			t.Errorf("NewClient return is not nil. Expected no client. Go %+v", c)
		}
		if err == nil {
			t.Error("No error occured by empty Gerrit Instance. Expected one.")
		}
	}
}

func TestNewClient_HttpClient(t *testing.T) {
	customHTTPClient := &http.Client{
		Timeout: 30 * time.Second,
	}
	mockData := []struct {
		HTTPClient         *http.Client
		ExpectedHTTPClient *http.Client
	}{
		{nil, http.DefaultClient},
		{customHTTPClient, customHTTPClient},
	}

	for _, mock := range mockData {
		c, err := NewClient("https://gerrit-review.googlesource.com/", mock.HTTPClient)
		if err != nil {
			t.Errorf("An error occured. Expected nil. Got %+v.", err)
		}
		if reflect.DeepEqual(c.client, mock.ExpectedHTTPClient) == false {
			t.Errorf("Wrong HTTP Client. Expected %+v. Got %+v", mock.ExpectedHTTPClient, c.client)
		}
	}
}

func TestNewClient_Services(t *testing.T) {
	c, err := NewClient("https://gerrit-review.googlesource.com/", nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	if c.Authentication == nil {
		t.Error("No AuthenticationService found.")
	}
	if c.Access == nil {
		t.Error("No AccessService found.")
	}
	if c.Accounts == nil {
		t.Error("No AccountsService found.")
	}
	if c.Changes == nil {
		t.Error("No ChangesService found.")
	}
	if c.Config == nil {
		t.Error("No ConfigService found.")
	}
	if c.Groups == nil {
		t.Error("No GroupsService found.")
	}
	if c.Plugins == nil {
		t.Error("No PluginsService found.")
	}
	if c.Projects == nil {
		t.Error("No ProjectsService found.")
	}
}

func TestNewRequest(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	inURL, outURL := "/foo", testGerritInstanceURL+"foo"
	inBody, outBody := &PermissionRuleInfo{Action: "ALLOW", Force: true, Min: 0, Max: 0}, `{"action":"ALLOW","force":true,"min":0,"max":0}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// Test that relative URL was expanded
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
	}

	// Test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want)
	}
}

func TestNewRequest_InvalidJSON(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	type T struct {
		A map[int]interface{}
	}
	_, err = c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %#v.", err)
	}
}

func testURLParseError(t *testing.T, err error) {
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

func TestNewRequest_BadURL(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	_, err = c.NewRequest("GET", ":", nil)
	testURLParseError(t, err)
}

// If a nil body is passed to gerrit.NewRequest, make sure that nil is also passed to http.NewRequest.
// In most cases, passing an io.Reader that returns no content is fine,
// since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
// However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
func TestNewRequest_EmptyBody(t *testing.T) {
	c, err := NewClient(testGerritInstanceURL, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest returned unexpected error: %v", err)
	}
	if req.Body != nil {
		t.Fatalf("constructed request contains a non-nil Body")
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `)]}'`+"\n"+`{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	testClient.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_ioWriter(t *testing.T) {
	setup()
	defer teardown()
	content := `)]}'` + "\n" + `{"A":"a"}`

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, content)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	var buf []byte
	actual := bytes.NewBuffer(buf)
	testClient.Do(req, actual)

	expected := []byte(content)
	if !reflect.DeepEqual(actual.Bytes(), expected) {
		t.Errorf("Response body = %v, want %v", actual, string(expected))
	}
}

func TestDo_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do() function.
// A redirect loop is pretty unlikely to occur within the Gerrit API, but does allow us to exercise the right code path.
func TestDo_RedirectLoop(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %#v.", err)
	}
}

func TestRemoveMagicPrefixLine(t *testing.T) {
	mockData := []struct {
		Current, Expected []byte
	}{
		{[]byte(`{"A":"a"}`), []byte(`{"A":"a"}`)},
		{[]byte(`)]}'` + "\n" + `{"A":"a"}`), []byte(`{"A":"a"}`)},
	}
	for _, mock := range mockData {
		body := RemoveMagicPrefixLine(mock.Current)
		if !reflect.DeepEqual(body, mock.Expected) {
			t.Errorf("Response body = %v, want %v", body, mock.Expected)
		}
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"io"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/ipfs/go-ipfs/commands"
)

type kvs map[string]interface{}
type words []string

func sameWords(a words, b words) bool {
	if len(a) != len(b) {
		return false
	}
	for i, w := range a {
		if w != b[i] {
			return false
		}
	}
	return true
}

func sameKVs(a kvs, b kvs) bool {
	if len(a) != len(b) {
		return false
	}
	for k, v := range a {
		if v != b[k] {
			return false
		}
	}
	return true
}

func TestSameWords(t *testing.T) {
	a := []string{"v1", "v2"}
	b := []string{"v1", "v2", "v3"}
	c := []string{"v2", "v3"}
	d := []string{"v2"}
	e := []string{"v2", "v3"}
	f := []string{"v2", "v1"}

	test := func(a words, b words, v bool) {
		if sameWords(a, b) != v {
			t.Errorf("sameWords('%v', '%v') != %v", a, b, v)
		}
	}

	test(a, b, false)
	test(a, a, true)
	test(a, c, false)
	test(b, c, false)
	test(c, d, false)
	test(c, e, true)
	test(b, e, false)
	test(a, b, false)
	test(a, f, false)
	test(e, f, false)
	test(f, f, true)
}

func TestOptionParsing(t *testing.T) {
	subCmd := &commands.Command{}
	cmd := &commands.Command{
		Options: []commands.Option{
			commands.StringOption("string", "s", "a string"),
			commands.BoolOption("bool", "b", "a bool"),
		},
		Subcommands: map[string]*commands.Command{
			"test": subCmd,
		},
	}

	testHelper := func(args string, expectedOpts kvs, expectedWords words, expectErr bool) {
		_, opts, input, _, err := parseOpts(strings.Split(args, " "), cmd)
		if expectErr {
			if err == nil {
				t.Errorf("Command line '%v' parsing should have failed", args)
			}
		} else if err != nil {
			t.Errorf("Command line '%v' failed to parse: %v", args, err)
		} else if !sameWords(input, expectedWords) || !sameKVs(opts, expectedOpts) {
			t.Errorf("Command line '%v':\n  parsed as  %v %v\n  instead of %v %v",
				args, opts, input, expectedOpts, expectedWords)
		}
	}

	testFail := func(args string) {
		testHelper(args, kvs{}, words{}, true)
	}

	test := func(args string, expectedOpts kvs, expectedWords words) {
		testHelper(args, expectedOpts, expectedWords, false)
	}

	test("-", kvs{}, words{"-"})
	testFail("-b -b")
	test("beep boop", kvs{}, words{"beep", "boop"})
	test("test beep boop", kvs{}, words{"beep", "boop"})
	testFail("-s")
	test("-s foo", kvs{"s": "foo"}, words{})
	test("-sfoo", kvs{"s": "foo"}, words{})
	test("-s=foo", kvs{"s": "foo"}, words{})
	test("-b", kvs{"b": true}, words{})
	test("-bs foo", kvs{"b": true, "s": "foo"}, words{})
	test("-sb", kvs{"s": "b"}, words{})
	test("-b foo", kvs{"b": true}, words{"foo"})
	test("--bool foo", kvs{"bool": true}, words{"foo"})
	testFail("--bool=foo")
	testFail("--string")
	test("--string foo", kvs{"string": "foo"}, words{})
	test("--string=foo", kvs{"string": "foo"}, words{})
	test("-- -b", kvs{}, words{"-b"})
	test("foo -b", kvs{"b": true}, words{"foo"})
	test("-b=false", kvs{"b": false}, words{})
	test("-b=true", kvs{"b": true}, words{})
	test("-b=false foo", kvs{"b": false}, words{"foo"})
	test("-b=true foo", kvs{"b": true}, words{"foo"})
	test("--bool=true foo", kvs{"bool": true}, words{"foo"})
	test("--bool=false foo", kvs{"bool": false}, words{"foo"})
	test("-b=FaLsE foo", kvs{"b": false}, words{"foo"})
	test("-b=TrUe foo", kvs{"b": true}, words{"foo"})
	test("-b true", kvs{"b": true}, words{"true"})
	test("-b false", kvs{"b": true}, words{"false"})
	test("-b --string foo bar", kvs{"b": true, "string": "foo"}, words{"bar"})
	test("-b=false --string bar", kvs{"b": false, "string": "bar"}, words{})
}

func TestArgumentParsing(t *testing.T) {
	rootCmd := &commands.Command{
		Subcommands: map[string]*commands.Command{
			"noarg": {},
			"onearg": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
				},
			},
			"twoargs": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, false, "another arg"),
				},
			},
			"variadic": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, true, "some arg"),
				},
			},
			"optional": {
				Arguments: []commands.Argument{
					commands.StringArg("b", false, true, "another arg"),
				},
			},
			"optionalsecond": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", false, false, "another arg"),
				},
			},
			"reversedoptional": {
				Arguments: []commands.Argument{
					commands.StringArg("a", false, false, "some arg"),
					commands.StringArg("b", true, false, "another arg"),
				},
			},
			"stdinenabled": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, true, "some arg").EnableStdin(),
				},
			},
			"stdinenabled2args": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, true, "another arg").EnableStdin(),
				},
			},
			"stdinenablednotvariadic": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg").EnableStdin(),
				},
			},
			"stdinenablednotvariadic2args": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, false, "another arg").EnableStdin(),
				},
			},
		},
	}

	test := func(cmd words, f *os.File, res words) {
		if f != nil {
			if _, err := f.Seek(0, os.SEEK_SET); err != nil {
				t.Fatal(err)
			}
		}
		req, _, _, err := Parse(cmd, f, rootCmd)
		if err != nil {
			t.Errorf("Command '%v' should have passed parsing: %v", cmd, err)
		}
		if !sameWords(req.Arguments(), res) {
			t.Errorf("Arguments parsed from '%v' are '%v' instead of '%v'", cmd, req.Arguments(), res)
		}
	}

	testFail := func(cmd words, msg string) {
		_, _, _, err := Parse(cmd, nil, rootCmd)
		if err == nil {
			t.Errorf("Should have failed: %v", msg)
		}
	}

	test([]string{"noarg"}, nil, []string{})
	testFail([]string{"noarg", "value!"}, "provided an arg, but command didn't define any")

	test([]string{"onearg", "value!"}, nil, []string{"value!"})
	testFail([]string{"onearg"}, "didn't provide any args, arg is required")

	test([]string{"twoargs", "value1", "value2"}, nil, []string{"value1", "value2"})
	testFail([]string{"twoargs", "value!"}, "only provided 1 arg, needs 2")
	testFail([]string{"twoargs"}, "didn't provide any args, 2 required")

	test([]string{"variadic", "value!"}, nil, []string{"value!"})
	test([]string{"variadic", "value1", "value2", "value3"}, nil, []string{"value1", "value2", "value3"})
	testFail([]string{"variadic"}, "didn't provide any args, 1 required")

	test([]string{"optional", "value!"}, nil, []string{"value!"})
	test([]string{"optional"}, nil, []string{})
	test([]string{"optional", "value1", "value2"}, nil, []string{"value1", "value2"})

	test([]string{"optionalsecond", "value!"}, nil, []string{"value!"})
	test([]string{"optionalsecond", "value1", "value2"}, nil, []string{"value1", "value2"})
	testFail([]string{"optionalsecond"}, "didn't provide any args, 1 required")
	testFail([]string{"optionalsecond", "value1", "value2", "value3"}, "provided too many args, takes 2 maximum")

	test([]string{"reversedoptional", "value1", "value2"}, nil, []string{"value1", "value2"})
	test([]string{"reversedoptional", "value!"}, nil, []string{"value!"})

	testFail([]string{"reversedoptional"}, "didn't provide any args, 1 required")
	testFail([]string{"reversedoptional", "value1", "value2", "value3"}, "provided too many args, only takes 1")

	// Use a temp file to simulate stdin
	fileToSimulateStdin := func(t *testing.T, content string) *os.File {
		fstdin, err := ioutil.TempFile("", "")
		if err != nil {
			t.Fatal(err)
		}
		defer os.Remove(fstdin.Name())

		if _, err := io.WriteString(fstdin, content); err != nil {
			t.Fatal(err)
		}
		return fstdin
	}

	test([]string{"stdinenabled", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin := fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1"})
	test([]string{"stdinenabled", "value1"}, fstdin, []string{"value1"})
	test([]string{"stdinenabled", "value1", "value2"}, fstdin, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1", "stdin2"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2\nstdin3")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1", "stdin2", "stdin3"})

	test([]string{"stdinenabled2args", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenabled2args", "value1"}, fstdin, []string{"value1", "stdin1"})
	test([]string{"stdinenabled2args", "value1", "value2"}, fstdin, []string{"value1", "value2"})
	test([]string{"stdinenabled2args", "value1", "value2", "value3"}, fstdin, []string{"value1", "value2", "value3"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2")
	test([]string{"stdinenabled2args", "value1"}, fstdin, []string{"value1", "stdin1", "stdin2"})

	test([]string{"stdinenablednotvariadic", "value1"}, nil, []string{"value1"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenablednotvariadic"}, fstdin, []string{"stdin1"})
	test([]string{"stdinenablednotvariadic", "value1"}, fstdin, []string{"value1"})

	test([]string{"stdinenablednotvariadic2args", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenablednotvariadic2args", "value1"}, fstdin, []string{"value1", "stdin1"})
	test([]string{"stdinenablednotvariadic2args", "value1", "value2"}, fstdin, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"noarg"}, fstdin, []string{})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"optionalsecond", "value1", "value2"}, fstdin, []string{"value1", "value2"})
}
<commit_msg>skip cli parse tests on windows due to no stdin<commit_after>package cli

import (
	"io"
	"io/ioutil"
	"os"
	"runtime"
	"strings"
	"testing"

	"github.com/ipfs/go-ipfs/commands"
)

type kvs map[string]interface{}
type words []string

func sameWords(a words, b words) bool {
	if len(a) != len(b) {
		return false
	}
	for i, w := range a {
		if w != b[i] {
			return false
		}
	}
	return true
}

func sameKVs(a kvs, b kvs) bool {
	if len(a) != len(b) {
		return false
	}
	for k, v := range a {
		if v != b[k] {
			return false
		}
	}
	return true
}

func TestSameWords(t *testing.T) {
	a := []string{"v1", "v2"}
	b := []string{"v1", "v2", "v3"}
	c := []string{"v2", "v3"}
	d := []string{"v2"}
	e := []string{"v2", "v3"}
	f := []string{"v2", "v1"}

	test := func(a words, b words, v bool) {
		if sameWords(a, b) != v {
			t.Errorf("sameWords('%v', '%v') != %v", a, b, v)
		}
	}

	test(a, b, false)
	test(a, a, true)
	test(a, c, false)
	test(b, c, false)
	test(c, d, false)
	test(c, e, true)
	test(b, e, false)
	test(a, b, false)
	test(a, f, false)
	test(e, f, false)
	test(f, f, true)
}

func TestOptionParsing(t *testing.T) {
	subCmd := &commands.Command{}
	cmd := &commands.Command{
		Options: []commands.Option{
			commands.StringOption("string", "s", "a string"),
			commands.BoolOption("bool", "b", "a bool"),
		},
		Subcommands: map[string]*commands.Command{
			"test": subCmd,
		},
	}

	testHelper := func(args string, expectedOpts kvs, expectedWords words, expectErr bool) {
		_, opts, input, _, err := parseOpts(strings.Split(args, " "), cmd)
		if expectErr {
			if err == nil {
				t.Errorf("Command line '%v' parsing should have failed", args)
			}
		} else if err != nil {
			t.Errorf("Command line '%v' failed to parse: %v", args, err)
		} else if !sameWords(input, expectedWords) || !sameKVs(opts, expectedOpts) {
			t.Errorf("Command line '%v':\n  parsed as  %v %v\n  instead of %v %v",
				args, opts, input, expectedOpts, expectedWords)
		}
	}

	testFail := func(args string) {
		testHelper(args, kvs{}, words{}, true)
	}

	test := func(args string, expectedOpts kvs, expectedWords words) {
		testHelper(args, expectedOpts, expectedWords, false)
	}

	test("-", kvs{}, words{"-"})
	testFail("-b -b")
	test("beep boop", kvs{}, words{"beep", "boop"})
	test("test beep boop", kvs{}, words{"beep", "boop"})
	testFail("-s")
	test("-s foo", kvs{"s": "foo"}, words{})
	test("-sfoo", kvs{"s": "foo"}, words{})
	test("-s=foo", kvs{"s": "foo"}, words{})
	test("-b", kvs{"b": true}, words{})
	test("-bs foo", kvs{"b": true, "s": "foo"}, words{})
	test("-sb", kvs{"s": "b"}, words{})
	test("-b foo", kvs{"b": true}, words{"foo"})
	test("--bool foo", kvs{"bool": true}, words{"foo"})
	testFail("--bool=foo")
	testFail("--string")
	test("--string foo", kvs{"string": "foo"}, words{})
	test("--string=foo", kvs{"string": "foo"}, words{})
	test("-- -b", kvs{}, words{"-b"})
	test("foo -b", kvs{"b": true}, words{"foo"})
	test("-b=false", kvs{"b": false}, words{})
	test("-b=true", kvs{"b": true}, words{})
	test("-b=false foo", kvs{"b": false}, words{"foo"})
	test("-b=true foo", kvs{"b": true}, words{"foo"})
	test("--bool=true foo", kvs{"bool": true}, words{"foo"})
	test("--bool=false foo", kvs{"bool": false}, words{"foo"})
	test("-b=FaLsE foo", kvs{"b": false}, words{"foo"})
	test("-b=TrUe foo", kvs{"b": true}, words{"foo"})
	test("-b true", kvs{"b": true}, words{"true"})
	test("-b false", kvs{"b": true}, words{"false"})
	test("-b --string foo bar", kvs{"b": true, "string": "foo"}, words{"bar"})
	test("-b=false --string bar", kvs{"b": false, "string": "bar"}, words{})
}

func TestArgumentParsing(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Skip("stdin handling doesnt yet work on windows")
	}
	rootCmd := &commands.Command{
		Subcommands: map[string]*commands.Command{
			"noarg": {},
			"onearg": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
				},
			},
			"twoargs": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, false, "another arg"),
				},
			},
			"variadic": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, true, "some arg"),
				},
			},
			"optional": {
				Arguments: []commands.Argument{
					commands.StringArg("b", false, true, "another arg"),
				},
			},
			"optionalsecond": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", false, false, "another arg"),
				},
			},
			"reversedoptional": {
				Arguments: []commands.Argument{
					commands.StringArg("a", false, false, "some arg"),
					commands.StringArg("b", true, false, "another arg"),
				},
			},
			"stdinenabled": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, true, "some arg").EnableStdin(),
				},
			},
			"stdinenabled2args": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, true, "another arg").EnableStdin(),
				},
			},
			"stdinenablednotvariadic": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg").EnableStdin(),
				},
			},
			"stdinenablednotvariadic2args": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, false, "another arg").EnableStdin(),
				},
			},
		},
	}

	test := func(cmd words, f *os.File, res words) {
		if f != nil {
			if _, err := f.Seek(0, os.SEEK_SET); err != nil {
				t.Fatal(err)
			}
		}
		req, _, _, err := Parse(cmd, f, rootCmd)
		if err != nil {
			t.Errorf("Command '%v' should have passed parsing: %v", cmd, err)
		}
		if !sameWords(req.Arguments(), res) {
			t.Errorf("Arguments parsed from '%v' are '%v' instead of '%v'", cmd, req.Arguments(), res)
		}
	}

	testFail := func(cmd words, msg string) {
		_, _, _, err := Parse(cmd, nil, rootCmd)
		if err == nil {
			t.Errorf("Should have failed: %v", msg)
		}
	}

	test([]string{"noarg"}, nil, []string{})
	testFail([]string{"noarg", "value!"}, "provided an arg, but command didn't define any")

	test([]string{"onearg", "value!"}, nil, []string{"value!"})
	testFail([]string{"onearg"}, "didn't provide any args, arg is required")

	test([]string{"twoargs", "value1", "value2"}, nil, []string{"value1", "value2"})
	testFail([]string{"twoargs", "value!"}, "only provided 1 arg, needs 2")
	testFail([]string{"twoargs"}, "didn't provide any args, 2 required")

	test([]string{"variadic", "value!"}, nil, []string{"value!"})
	test([]string{"variadic", "value1", "value2", "value3"}, nil, []string{"value1", "value2", "value3"})
	testFail([]string{"variadic"}, "didn't provide any args, 1 required")

	test([]string{"optional", "value!"}, nil, []string{"value!"})
	test([]string{"optional"}, nil, []string{})
	test([]string{"optional", "value1", "value2"}, nil, []string{"value1", "value2"})

	test([]string{"optionalsecond", "value!"}, nil, []string{"value!"})
	test([]string{"optionalsecond", "value1", "value2"}, nil, []string{"value1", "value2"})
	testFail([]string{"optionalsecond"}, "didn't provide any args, 1 required")
	testFail([]string{"optionalsecond", "value1", "value2", "value3"}, "provided too many args, takes 2 maximum")

	test([]string{"reversedoptional", "value1", "value2"}, nil, []string{"value1", "value2"})
	test([]string{"reversedoptional", "value!"}, nil, []string{"value!"})

	testFail([]string{"reversedoptional"}, "didn't provide any args, 1 required")
	testFail([]string{"reversedoptional", "value1", "value2", "value3"}, "provided too many args, only takes 1")

	// Use a temp file to simulate stdin
	fileToSimulateStdin := func(t *testing.T, content string) *os.File {
		fstdin, err := ioutil.TempFile("", "")
		if err != nil {
			t.Fatal(err)
		}
		defer os.Remove(fstdin.Name())

		if _, err := io.WriteString(fstdin, content); err != nil {
			t.Fatal(err)
		}
		return fstdin
	}

	test([]string{"stdinenabled", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin := fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1"})
	test([]string{"stdinenabled", "value1"}, fstdin, []string{"value1"})
	test([]string{"stdinenabled", "value1", "value2"}, fstdin, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1", "stdin2"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2\nstdin3")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1", "stdin2", "stdin3"})

	test([]string{"stdinenabled2args", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenabled2args", "value1"}, fstdin, []string{"value1", "stdin1"})
	test([]string{"stdinenabled2args", "value1", "value2"}, fstdin, []string{"value1", "value2"})
	test([]string{"stdinenabled2args", "value1", "value2", "value3"}, fstdin, []string{"value1", "value2", "value3"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2")
	test([]string{"stdinenabled2args", "value1"}, fstdin, []string{"value1", "stdin1", "stdin2"})

	test([]string{"stdinenablednotvariadic", "value1"}, nil, []string{"value1"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenablednotvariadic"}, fstdin, []string{"stdin1"})
	test([]string{"stdinenablednotvariadic", "value1"}, fstdin, []string{"value1"})

	test([]string{"stdinenablednotvariadic2args", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenablednotvariadic2args", "value1"}, fstdin, []string{"value1", "stdin1"})
	test([]string{"stdinenablednotvariadic2args", "value1", "value2"}, fstdin, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"noarg"}, fstdin, []string{})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"optionalsecond", "value1", "value2"}, fstdin, []string{"value1", "value2"})
}
<|endoftext|>


<commit_before>package ghost

import "fmt"

type whereConfig struct {
	key        string
	operator   string
	value      interface{}
	anyValue   []interface{}
	joinWithOr bool
}

//Query is the basic building block of an SQL query
type Query struct {
	//userQueryString is for when you need to provide complete, preformed SQL
	//This option will override any BaseAQL + args
	OverrideQueryString string
	//BaseSQL is a formatted SQL string with placeholder for SQLArgs
	BaseSQL string
	//SQLArgs are inserted into the BaseSQL in the order they appear
	SQLArgs []interface{}
	//SELECT fields
	Select []string
	//From Schema.Table
	Schema, Table string
	//Where
	Where []whereConfig
	//Indicate whether to rquest JSON array or object
	//and when unmarshalling, whether map or slice of maps
	IsList bool
	//Role to execute the query as
	Role string
	//UserID to set on the query context
	UserID string
	//CacheLevel specifies the level of caching to use: all, role, user
	//Omitting, or using any other value, will bypass caching
	CacheLevel string
	//cacheKey is the key used to store the SQL query in the cache
	cacheKey string
	//queryString is the output sql string ready to be executed
	queryString string
}

//Build runs a query against the data store and returns JSON
func (q *Query) Build() error {

	//If any override SQL is present, set the output query to its value
	//and exit immediately
	if q.OverrideQueryString != "" {
		q.queryString = q.OverrideQueryString
		return nil
	}

	tempQueryString := queryBuilder("")

	//If base sql + args have been supplied, use them
	//otherwise build from parameters
	if q.BaseSQL != "" && len(q.SQLArgs) != 0 {
		tempQueryString = queryBuilder(fmt.Sprintf(q.BaseSQL, q.SQLArgs...))
	} else {
		tempQueryString = tempQueryString.basicSelect(q.Schema, q.Table, q.Select)
	}

	//For basic where clauses
	if len(q.Where) != 0 {
		tempQueryString = tempQueryString.addWhereClauses(q.Where)
	}

	//Return JSON array or object
	if q.IsList {
		tempQueryString = tempQueryString.requestMultipleResultsAsJSONArray()
	} else {
		tempQueryString = tempQueryString.requestSingleResultAsJSONObject()
	}

	//Add sql role and user id, caching as you go depending
	//on the cache level specified
	if q.CacheLevel == "all" {
		q.cacheKey = tempQueryString.toSQLCacheKey()
	}

	//Add role
	if q.Role != "" {
		tempQueryString = tempQueryString.setQueryRole(q.Role)
	}

	if q.CacheLevel == "role" {
		q.cacheKey = tempQueryString.toSQLCacheKey()
	}

	//Add user id
	if q.UserID != "" {
		tempQueryString = tempQueryString.setUserID(q.UserID)
	}

	if q.CacheLevel == "user" {
		q.cacheKey = tempQueryString.toSQLCacheKey()
	}

	//Transform to SQL string and reset on the struct
	q.queryString = tempQueryString.toSQLString()

	//Execute the query
	return nil

}

//TODO: deprecate this
//QueryBuilder builds an SqlQuery from multiple URL query paramaters
// func QueryBuilder(schema string, table string, queries url.Values) SqlQuery {

// 	//Concat - schema.table
// 	tn := fmt.Sprintf("%s.%s", schema, table)

// 	//Start with all the products from the table
// 	p := sq.Select("*").From(tn)

// 	//Loop through all the URL qeueries
// 	for key, value := range queries {

// 		if strings.ToLower(key) == "orderby" {
// 			p = p.OrderBy(value[0])
// 		} else if strings.ToLower(key) == "limit" {
// 			l, _ := strconv.ParseUint(value[0], 10, 64)
// 			p = p.Limit(l)
// 		} else {
// 			p = p.Where(fmt.Sprintf(`%s %s`, key, value[0]))
// 		}
// 	}

// 	//Build the basic SQL
// 	sql, _, _ := p.ToSql()
// 	//Return as JSON array request
// 	return SqlQuery(sql)

// }
<commit_msg>Removed depracted query builder<commit_after>package ghost

import "fmt"

type whereConfig struct {
	key        string
	operator   string
	value      interface{}
	anyValue   []interface{}
	joinWithOr bool
}

//Query is the basic building block of an SQL query
type Query struct {
	//userQueryString is for when you need to provide complete, preformed SQL
	//This option will override any BaseAQL + args
	OverrideQueryString string
	//BaseSQL is a formatted SQL string with placeholder for SQLArgs
	BaseSQL string
	//SQLArgs are inserted into the BaseSQL in the order they appear
	SQLArgs []interface{}
	//SELECT fields
	Select []string
	//From Schema.Table
	Schema, Table string
	//Where
	Where []whereConfig
	//Indicate whether to rquest JSON array or object
	//and when unmarshalling, whether map or slice of maps
	IsList bool
	//Role to execute the query as
	Role string
	//UserID to set on the query context
	UserID string
	//CacheLevel specifies the level of caching to use: all, role, user
	//Omitting, or using any other value, will bypass caching
	CacheLevel string
	//cacheKey is the key used to store the SQL query in the cache
	cacheKey string
	//queryString is the output sql string ready to be executed
	queryString string
}

//Build runs a query against the data store and returns JSON
func (q *Query) Build() error {

	//If any override SQL is present, set the output query to its value
	//and exit immediately
	if q.OverrideQueryString != "" {
		q.queryString = q.OverrideQueryString
		return nil
	}

	tempQueryString := queryBuilder("")

	//If base sql + args have been supplied, use them
	//otherwise build from parameters
	if q.BaseSQL != "" && len(q.SQLArgs) != 0 {
		tempQueryString = queryBuilder(fmt.Sprintf(q.BaseSQL, q.SQLArgs...))
	} else {
		tempQueryString = tempQueryString.basicSelect(q.Schema, q.Table, q.Select)
	}

	//For basic where clauses
	if len(q.Where) != 0 {
		tempQueryString = tempQueryString.addWhereClauses(q.Where)
	}

	//Return JSON array or object
	if q.IsList {
		tempQueryString = tempQueryString.requestMultipleResultsAsJSONArray()
	} else {
		tempQueryString = tempQueryString.requestSingleResultAsJSONObject()
	}

	//Add sql role and user id, caching as you go depending
	//on the cache level specified
	if q.CacheLevel == "all" {
		q.cacheKey = tempQueryString.toSQLCacheKey()
	}

	//Add role
	if q.Role != "" {
		tempQueryString = tempQueryString.setQueryRole(q.Role)
	}

	if q.CacheLevel == "role" {
		q.cacheKey = tempQueryString.toSQLCacheKey()
	}

	//Add user id
	if q.UserID != "" {
		tempQueryString = tempQueryString.setUserID(q.UserID)
	}

	if q.CacheLevel == "user" {
		q.cacheKey = tempQueryString.toSQLCacheKey()
	}

	//Transform to SQL string and reset on the struct
	q.queryString = tempQueryString.toSQLString()

	//Execute the query
	return nil

}
<|endoftext|>


<commit_before>package git

import (
	"os"
	"os/exec"
	"strings"

	"github.com/Sirupsen/logrus"
)

// Commits returns a set of commits.
// If commitrange is a git still range 12345...54321, then it will be isolated set of commits.
// If commitrange is a single commit, all ancestor commits up through the hash provided.
func Commits(commitrange string) ([]CommitEntry, error) {
	cmdArgs := []string{"git", "--no-pager", "log", `--pretty=format:%H`, commitrange}
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
	}
	output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
	if err != nil {
		logrus.Errorf("mm[git] cmd: %q", strings.Join(cmdArgs, " "))
		return nil, err
	}
	commitHashes := strings.Split(strings.TrimSpace(string(output)), "\n")
	commits := make([]CommitEntry, len(commitHashes))
	for i, commitHash := range commitHashes {
		c, err := LogCommit(commitHash)
		if err != nil {
			return commits, err
		}
		commits[i] = *c
	}
	return commits, nil
}

// FieldNames are for the formating and rendering of the CommitEntry structs.
// Keys here are from git log pretty format "format:..."
var FieldNames = map[string]string{
	"%h":  "abbreviated_commit",
	"%p":  "abbreviated_parent",
	"%t":  "abbreviated_tree",
	"%aD": "author_date",
	"%aE": "author_email",
	"%aN": "author_name",
	"%b":  "body",
	"%H":  "commit",
	"%N":  "commit_notes",
	"%cD": "committer_date",
	"%cE": "committer_email",
	"%cN": "committer_name",
	"%e":  "encoding",
	"%P":  "parent",
	"%D":  "refs",
	"%f":  "sanitized_subject_line",
	"%GS": "signer",
	"%GK": "signer_key",
	"%s":  "subject",
	"%G?": "verification_flag",
}

// Check warns if changes introduce whitespace errors.
// Returns non-zero if any issues are found.
func Check(commit string) ([]byte, error) {
	cmd := exec.Command("git", "--no-pager", "show", "--check", commit)
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
	}
	cmd.Stderr = os.Stderr
	return cmd.Output()
}

// Show returns the diff of a commit.
//
// NOTE: This could be expensive for very large commits.
func Show(commit string) ([]byte, error) {
	cmd := exec.Command("git", "--no-pager", "show", commit)
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
	}
	cmd.Stderr = os.Stderr
	return cmd.Output()
}

// CommitEntry represents a single commit's information from `git`.
// See also FieldNames
type CommitEntry map[string]string

// LogCommit assembles the full information on a commit from its commit hash
func LogCommit(commit string) (*CommitEntry, error) {
	c := CommitEntry{}
	for k, v := range FieldNames {
		cmd := exec.Command("git", "--no-pager", "log", "-1", `--pretty=format:`+k+``, commit)
		if debug() {
			logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
		}
		cmd.Stderr = os.Stderr
		out, err := cmd.Output()
		if err != nil {
			logrus.Errorf("[git] cmd: %q", strings.Join(cmd.Args, " "))
			return nil, err
		}
		c[v] = strings.TrimSpace(string(out))
	}

	return &c, nil
}

func debug() bool {
	return len(os.Getenv("DEBUG")) > 0
}

// FetchHeadCommit returns the hash of FETCH_HEAD
func FetchHeadCommit() (string, error) {
	cmdArgs := []string{"git", "--no-pager", "rev-parse", "--verify", "FETCH_HEAD"}
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
	}
	output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
	if err != nil {
		logrus.Errorf("[git] cmd: %q", strings.Join(cmdArgs, " "))
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}

// HeadCommit returns the hash of HEAD
func HeadCommit() (string, error) {
	cmdArgs := []string{"git", "--no-pager", "rev-parse", "--verify", "HEAD"}
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
	}
	output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
	if err != nil {
		logrus.Errorf("[git] cmd: %q", strings.Join(cmdArgs, " "))
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}
<commit_msg>Add exclude pattern for git checks<commit_after>package git

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

	"github.com/Sirupsen/logrus"
)

// Commits returns a set of commits.
// If commitrange is a git still range 12345...54321, then it will be isolated set of commits.
// If commitrange is a single commit, all ancestor commits up through the hash provided.
func Commits(commitrange string) ([]CommitEntry, error) {
	cmdArgs := []string{"git", "--no-pager", "log", `--pretty=format:%H`, commitrange}
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
	}
	output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
	if err != nil {
		logrus.Errorf("mm[git] cmd: %q", strings.Join(cmdArgs, " "))
		return nil, err
	}
	commitHashes := strings.Split(strings.TrimSpace(string(output)), "\n")
	commits := make([]CommitEntry, len(commitHashes))
	for i, commitHash := range commitHashes {
		c, err := LogCommit(commitHash)
		if err != nil {
			return commits, err
		}
		commits[i] = *c
	}
	return commits, nil
}

// FieldNames are for the formating and rendering of the CommitEntry structs.
// Keys here are from git log pretty format "format:..."
var FieldNames = map[string]string{
	"%h":  "abbreviated_commit",
	"%p":  "abbreviated_parent",
	"%t":  "abbreviated_tree",
	"%aD": "author_date",
	"%aE": "author_email",
	"%aN": "author_name",
	"%b":  "body",
	"%H":  "commit",
	"%N":  "commit_notes",
	"%cD": "committer_date",
	"%cE": "committer_email",
	"%cN": "committer_name",
	"%e":  "encoding",
	"%P":  "parent",
	"%D":  "refs",
	"%f":  "sanitized_subject_line",
	"%GS": "signer",
	"%GK": "signer_key",
	"%s":  "subject",
	"%G?": "verification_flag",
}

// Check warns if changes introduce whitespace errors.
// Returns non-zero if any issues are found.
func Check(commit string) ([]byte, error) {
	args := []string{
		"--no-pager", "log", "--check",
		fmt.Sprintf("%s^..%s", commit, commit),
	}
	if exclude := os.Getenv("GIT_CHECK_EXCLUDE"); exclude != "" {
		args = append(args, "--", ".", fmt.Sprintf(":(exclude)%s", exclude))
	}
	cmd := exec.Command("git", args...)
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
	}
	cmd.Stderr = os.Stderr
	return cmd.Output()
}

// Show returns the diff of a commit.
//
// NOTE: This could be expensive for very large commits.
func Show(commit string) ([]byte, error) {
	cmd := exec.Command("git", "--no-pager", "show", commit)
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
	}
	cmd.Stderr = os.Stderr
	return cmd.Output()
}

// CommitEntry represents a single commit's information from `git`.
// See also FieldNames
type CommitEntry map[string]string

// LogCommit assembles the full information on a commit from its commit hash
func LogCommit(commit string) (*CommitEntry, error) {
	c := CommitEntry{}
	for k, v := range FieldNames {
		cmd := exec.Command("git", "--no-pager", "log", "-1", `--pretty=format:`+k+``, commit)
		if debug() {
			logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
		}
		cmd.Stderr = os.Stderr
		out, err := cmd.Output()
		if err != nil {
			logrus.Errorf("[git] cmd: %q", strings.Join(cmd.Args, " "))
			return nil, err
		}
		c[v] = strings.TrimSpace(string(out))
	}

	return &c, nil
}

func debug() bool {
	return len(os.Getenv("DEBUG")) > 0
}

// FetchHeadCommit returns the hash of FETCH_HEAD
func FetchHeadCommit() (string, error) {
	cmdArgs := []string{"git", "--no-pager", "rev-parse", "--verify", "FETCH_HEAD"}
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
	}
	output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
	if err != nil {
		logrus.Errorf("[git] cmd: %q", strings.Join(cmdArgs, " "))
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}

// HeadCommit returns the hash of HEAD
func HeadCommit() (string, error) {
	cmdArgs := []string{"git", "--no-pager", "rev-parse", "--verify", "HEAD"}
	if debug() {
		logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
	}
	output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
	if err != nil {
		logrus.Errorf("[git] cmd: %q", strings.Join(cmdArgs, " "))
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}
<|endoftext|>


<commit_before>package git

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

type Ref struct {
	// The full name of a ref, starting with refs/
	Name string

	// Value can be either a commit or a tag object
	Value Sha1
}

func (r Ref) Matches(pattern string) bool {
	return r.Name == pattern || strings.HasSuffix(r.Name, "/"+pattern)
}

func (r Ref) String() string {
	return fmt.Sprintf("%v %v", r.Value, r.Name)
}

func (r Ref) CommitID(c *Client) (CommitID, error) {
	switch r.Value.Type(c) {
	case "commit":
		return CommitID(r.Value), nil
	default:
		return CommitID{}, fmt.Errorf("Can not convert %v to commit", r.Name)
	}
}

func (r Ref) TreeID(c *Client) (TreeID, error) {
	switch r.Value.Type(c) {
	case "commit":
		return CommitID(r.Value).TreeID(c)
	case "tree":
		return TreeID(r.Value), nil
	default:
		return TreeID{}, fmt.Errorf("Can not get tree for %v", r.Name)
	}
}

type ShowRefOptions struct {
	IncludeHead bool

	Tags, Heads bool

	Dereference bool

	Sha1Only bool

	Verify bool

	Abbrev int

	Quiet bool

	ExcludeExisting string
}

func ShowRef(c *Client, opts ShowRefOptions, patterns []string) ([]Ref, error) {
	var vals []Ref
	if opts.IncludeHead {
		hcid, err := c.GetHeadCommit()
		if err == nil {
			// If the HEAD reference is a symbolic ref to something that
			// doesn't exist it's not an invalid state of git, we just
			// don't include it in the list.
			vals = append(vals, Ref{"HEAD", Sha1(hcid)})
		}
	}
	// FIXME: Include packed refs
	if !opts.Heads && !opts.Tags {
		err := filepath.Walk(c.GitDir.File("refs").String(),
			func(path string, info os.FileInfo, err error) error {
				if info.IsDir() {
					return nil
				}
				refname := strings.TrimPrefix(path, c.GitDir.String())
				refname = strings.TrimPrefix(refname, "/")

				data, err := ioutil.ReadFile(c.GitDir.File(File(refname)).String())
				if err != nil {
					return err
				}
				sha1, err := Sha1FromString(string(data))
				if err != nil {
					return err
				}
				ref := Ref{refname, sha1}
				if len(patterns) == 0 {

					vals = append(vals, ref)
					return nil
				}
				for _, p := range patterns {
					if ref.Matches(p) {
						vals = append(vals, ref)
						return nil
					}
				}
				return nil
			},
		)
		if err != nil {
			return nil, err
		}
		return vals, nil
	}
	if opts.Heads {
		heads, err := ioutil.ReadDir(c.GitDir.File("refs/heads").String())
		if err != nil {
			return nil, err
		}
		for _, ref := range heads {
			refname := "refs/heads/" + ref.Name()
			data, err := ioutil.ReadFile(c.GitDir.File(File(refname)).String())
			if err != nil {
				return nil, err
			}
			sha1, err := Sha1FromString(string(data))
			if err != nil {
				return nil, err
			}
			ref := Ref{refname, sha1}
			if len(patterns) == 0 {
				vals = append(vals, ref)
				continue
			}
			for _, p := range patterns {
				if ref.Matches(p) {
					vals = append(vals, ref)
					break
				}
			}
		}
	}
	if opts.Tags {
		tags, err := ioutil.ReadDir(c.GitDir.File("refs/tags").String())
		if err != nil {
			return nil, err
		}
		for _, ref := range tags {
			refname := "refs/tags/" + ref.Name()
			data, err := ioutil.ReadFile(c.GitDir.File(File(refname)).String())
			if err != nil {
				return nil, err
			}
			sha1, err := Sha1FromString(string(data))
			if err != nil {
				return nil, err
			}
			ref := Ref{refname, sha1}
			if len(patterns) == 0 {
				vals = append(vals, ref)
				continue
			}
			for _, p := range patterns {
				if ref.Matches(p) {
					vals = append(vals, ref)
					break
				}
			}
		}
	}

	return vals, nil
}
<commit_msg>Fix show-ref on symbolic refs<commit_after>package git

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

type Ref struct {
	// The full name of a ref, starting with refs/
	Name string

	// Value can be either a commit or a tag object
	Value Sha1
}

func (r Ref) Matches(pattern string) bool {
	return r.Name == pattern || strings.HasSuffix(r.Name, "/"+pattern)
}

func (r Ref) String() string {
	return fmt.Sprintf("%v %v", r.Value, r.Name)
}

func (r Ref) CommitID(c *Client) (CommitID, error) {
	switch r.Value.Type(c) {
	case "commit":
		return CommitID(r.Value), nil
	default:
		return CommitID{}, fmt.Errorf("Can not convert %v to commit", r.Name)
	}
}

func (r Ref) TreeID(c *Client) (TreeID, error) {
	switch r.Value.Type(c) {
	case "commit":
		return CommitID(r.Value).TreeID(c)
	case "tree":
		return TreeID(r.Value), nil
	default:
		return TreeID{}, fmt.Errorf("Can not get tree for %v", r.Name)
	}
}

type ShowRefOptions struct {
	IncludeHead bool

	Tags, Heads bool

	Dereference bool

	Sha1Only bool

	Verify bool

	Abbrev int

	Quiet bool

	ExcludeExisting string
}

func ShowRef(c *Client, opts ShowRefOptions, patterns []string) ([]Ref, error) {
	var vals []Ref
	if opts.IncludeHead {
		hcid, err := c.GetHeadCommit()
		if err == nil {
			// If the HEAD reference is a symbolic ref to something that
			// doesn't exist it's not an invalid state of git, we just
			// don't include it in the list.
			vals = append(vals, Ref{"HEAD", Sha1(hcid)})
		}
	}
	// FIXME: Include packed refs
	if !opts.Heads && !opts.Tags {
		err := filepath.Walk(c.GitDir.File("refs").String(),
			func(path string, info os.FileInfo, err error) error {
				if info.IsDir() {
					return nil
				}
				refname := strings.TrimPrefix(path, c.GitDir.String())
				refname = strings.TrimPrefix(refname, "/")

				data, err := ioutil.ReadFile(c.GitDir.File(File(refname)).String())
				if err != nil {
					return err
				}
				var sha1 Sha1
				if strings.HasPrefix(string(data), "ref: ") {
					deref, err := SymbolicRefGet(c, SymbolicRefOptions{}, SymbolicRef(refname))
					if err != nil {
						return err
					}
					sha1a, err := deref.Sha1(c)
					if err != nil {
						return err
					}
					sha1 = sha1a
				} else {
					sha1a, err := Sha1FromString(string(data))
					if err != nil {
						fmt.Println(refname)
						fmt.Println(string(data))
						return err
					}
					sha1 = sha1a
				}
				ref := Ref{refname, sha1}
				if len(patterns) == 0 {

					vals = append(vals, ref)
					return nil
				}
				for _, p := range patterns {
					if ref.Matches(p) {
						vals = append(vals, ref)
						return nil
					}
				}
				return nil
			},
		)
		if err != nil {
			return nil, err
		}
		return vals, nil
	}
	if opts.Heads {
		heads, err := ioutil.ReadDir(c.GitDir.File("refs/heads").String())
		if err != nil {
			return nil, err
		}
		for _, ref := range heads {
			refname := "refs/heads/" + ref.Name()
			data, err := ioutil.ReadFile(c.GitDir.File(File(refname)).String())
			if err != nil {
				return nil, err
			}
			sha1, err := Sha1FromString(string(data))
			if err != nil {
				return nil, err
			}
			ref := Ref{refname, sha1}
			if len(patterns) == 0 {
				vals = append(vals, ref)
				continue
			}
			for _, p := range patterns {
				if ref.Matches(p) {
					vals = append(vals, ref)
					break
				}
			}
		}
	}
	if opts.Tags {
		tags, err := ioutil.ReadDir(c.GitDir.File("refs/tags").String())
		if err != nil {
			return nil, err
		}
		for _, ref := range tags {
			refname := "refs/tags/" + ref.Name()
			data, err := ioutil.ReadFile(c.GitDir.File(File(refname)).String())
			if err != nil {
				return nil, err
			}
			sha1, err := Sha1FromString(string(data))
			if err != nil {
				return nil, err
			}
			ref := Ref{refname, sha1}
			if len(patterns) == 0 {
				vals = append(vals, ref)
				continue
			}
			for _, p := range patterns {
				if ref.Matches(p) {
					vals = append(vals, ref)
					break
				}
			}
		}
	}

	return vals, nil
}
<|endoftext|>