<commit_before>package v1

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/solefaucet/sole-server/errors"
	"github.com/solefaucet/sole-server/models"
)

/*

0：非即时返利活动,处于待审核状态；
1：即时返利活动，需发放奖励给会员；
2：非即时返利活动，审核通过，重新回传，发放奖励给会员；
3：非即时返利活动，审核不通过，重新回传，不发放奖励；

errno:

offerwow-01: 出现空参数
offerwow-02: 网站id不存在
offerwow-03: uid会员不存在
offerwow-04: 已发放奖励的Eventid重复
offerwow-05: immediate=0
offerwow-06: immediate=3
*/

// offerwow const
const (
	offerwowWebsiteID     = "2626"
	offerwowImmediate0    = "0"
	offerwowImmediate1    = "1"
	offerwowImmediate2    = "2"
	offerwowImmediate3    = "3"
	offerwowStatusSuccess = "success"
	offerwowStatusFailure = "failure"
	offerwowErrno01       = "offerwow-01"
	offerwowErrno02       = "offerwow-02"
	offerwowErrno03       = "offerwow-03"
	offerwowErrno04       = "offerwow-04"
	offerwowErrno05       = "offerwow-05"
	offerwowErrno06       = "offerwow-06"
)

type offerwowPayload struct {
	UserID    int64   `form:"memberid"`
	Amount    float64 `form:"point"`
	EventID   string  `form:"eventid"`
	WebsiteID string  `form:"websiteid"`
	Immediate string  `form:"immediate"`
	Sign      string  `form:"sign"`
}

type offerwowResponse struct {
	UserID    int64
	Amount    float64
	EventID   string
	Immediate string
	Status    string
	Error     string
}

func (r offerwowResponse) MarshalJSON() ([]byte, error) {
	m := map[string]interface{}{
		"memberid":  fmt.Sprint(r.UserID),
		"point":     fmt.Sprint(r.Amount),
		"websiteid": offerwowWebsiteID,
		"eventid":   r.EventID,
		"immediate": r.Immediate,
		"status":    r.Status,
	}
	if r.Error != "" {
		m["errno"] = r.Error
	}
	return json.Marshal(m)
}

// OfferwowCallback handles callback from offerwow
func OfferwowCallback(
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
	getSystemConfig dependencyGetSystemConfig,
	createOfferwowIncome dependencyCreateOfferwowIncome,
) gin.HandlerFunc {
	return func(c *gin.Context) {
		payload := offerwowPayload{}
		c.BindWith(&payload, binding.Form)

		if !validateOfferwowSignature(payload, "86611105freeusd") {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		responseAndLog := func(status, err string) {
			response := offerwowResponse{
				UserID:    payload.UserID,
				Amount:    payload.Amount,
				EventID:   payload.EventID,
				Immediate: payload.Immediate,
				Status:    status,
				Error:     err,
			}
			c.JSON(http.StatusOK, response)
			logrus.WithFields(logrus.Fields{
				"event":    models.EventOfferwowCallback,
				"query":    c.Request.URL.Query().Encode(),
				"payload":  payload,
				"response": response,
			}).Debug("get offerwow callback")
		}

		if payload.UserID == 0 || payload.Amount == 0 || payload.EventID == "" || payload.WebsiteID == "" || payload.Immediate == "" {
			responseAndLog(offerwowStatusFailure, offerwowErrno01)
			return
		}

		if payload.WebsiteID != offerwowWebsiteID {
			responseAndLog(offerwowStatusFailure, offerwowErrno02)
			return
		}

		// check if user exists
		user, err := getUserByID(payload.UserID)
		if err != nil {
			switch err {
			case errors.ErrNotFound:
				responseAndLog(offerwowStatusFailure, offerwowErrno03)
			default:
				c.AbortWithError(http.StatusInternalServerError, err)
			}
			return
		}

		// check if eventid duplicates
		_, err = getOfferwowEventByID(payload.EventID)
		switch err {
		case errors.ErrNotFound:
			// pass validation
		case nil:
			responseAndLog(offerwowStatusFailure, offerwowErrno04)
			return
		default:
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		if payload.Immediate == offerwowImmediate0 {
			responseAndLog(offerwowStatusFailure, offerwowErrno05)
			return
		}

		if payload.Immediate == offerwowImmediate3 {
			responseAndLog(offerwowStatusFailure, offerwowErrno06)
			return
		}

		// create income offerwow
		income := models.Income{
			UserID:        user.ID,
			RefererID:     user.RefererID,
			Type:          models.IncomeTypeOfferwow,
			Income:        payload.Amount / 1e8,
			RefererIncome: getSystemConfig().RefererRewardRate * payload.Amount,
		}
		if err := createOfferwowIncome(income, payload.EventID); err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		responseAndLog(offerwowStatusSuccess, "")
	}
}

func validateOfferwowSignature(payload offerwowPayload, key string) bool {
	data := fmt.Sprintf("%v%v%v%v%v%v", payload.UserID, payload.Amount, payload.EventID, payload.WebsiteID, payload.Immediate, key)
	sign := strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(data))))
	logrus.WithFields(logrus.Fields{
		"event": models.EventOfferwowCallback,
	}).Info(sign)
	return sign == payload.Sign
}
<commit_msg>Code cleanup for offerwow callback handler<commit_after>package v1

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/solefaucet/sole-server/errors"
	"github.com/solefaucet/sole-server/models"
)

// offerwow const
const (
	offerwowWebsiteID     = "2626"
	offerwowImmediate0    = "0" // 非即时返利活动,处于待审核状态；
	offerwowImmediate1    = "1" // 即时返利活动，需发放奖励给会员；
	offerwowImmediate2    = "2" // 非即时返利活动，审核通过，重新回传，发放奖励给会员；
	offerwowImmediate3    = "3" // 非即时返利活动，审核不通过，重新回传，不发放奖励；
	offerwowStatusSuccess = "success"
	offerwowStatusFailure = "failure"
	offerwowErrno01       = "offerwow-01"
	offerwowErrno02       = "offerwow-02"
	offerwowErrno03       = "offerwow-03"
	offerwowErrno04       = "offerwow-04"
	offerwowErrno05       = "offerwow-05"
	offerwowErrno06       = "offerwow-06"
)

type offerwowPayload struct {
	UserID    int64   `form:"memberid"`
	Amount    float64 `form:"point"`
	EventID   string  `form:"eventid"`
	WebsiteID string  `form:"websiteid"`
	Immediate string  `form:"immediate"`
	Sign      string  `form:"sign"`
}

type offerwowResponse struct {
	UserID    int64
	Amount    float64
	EventID   string
	Immediate string
	Status    string
	Error     string
}

func (r offerwowResponse) MarshalJSON() ([]byte, error) {
	m := map[string]interface{}{
		"memberid":  fmt.Sprint(r.UserID),
		"point":     fmt.Sprint(r.Amount),
		"websiteid": offerwowWebsiteID,
		"eventid":   r.EventID,
		"immediate": r.Immediate,
		"status":    r.Status,
	}
	if r.Error != "" {
		m["errno"] = r.Error
	}
	return json.Marshal(m)
}

// OfferwowCallback handles callback from offerwow
func OfferwowCallback(
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
	getSystemConfig dependencyGetSystemConfig,
	createOfferwowIncome dependencyCreateOfferwowIncome,
) gin.HandlerFunc {
	return func(c *gin.Context) {
		payload := offerwowPayload{}
		c.BindWith(&payload, binding.Form)

		if !validateOfferwowSignature(payload, "86611105freeusd") {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		responseAndLog := func(status, err string) {
			response := offerwowResponse{
				UserID:    payload.UserID,
				Amount:    payload.Amount,
				EventID:   payload.EventID,
				Immediate: payload.Immediate,
				Status:    status,
				Error:     err,
			}
			c.JSON(http.StatusOK, response)
			logrus.WithFields(logrus.Fields{
				"event":    models.EventOfferwowCallback,
				"query":    c.Request.URL.Query().Encode(),
				"payload":  payload,
				"response": response,
			}).Debug("get offerwow callback")
		}

		errno, user, err := validateOfferwowEvent(payload, getUserByID, getOfferwowEventByID)
		if err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		} else if errno != "" {
			responseAndLog(offerwowStatusFailure, errno)
			return
		}

		// create income offerwow
		income := models.Income{
			UserID:        user.ID,
			RefererID:     user.RefererID,
			Type:          models.IncomeTypeOfferwow,
			Income:        payload.Amount / 1e8,
			RefererIncome: getSystemConfig().RefererRewardRate * payload.Amount,
		}
		if err := createOfferwowIncome(income, payload.EventID); err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		responseAndLog(offerwowStatusSuccess, "")
	}
}

func validateOfferwowSignature(payload offerwowPayload, key string) bool {
	data := fmt.Sprintf("%v%v%v%v%v%v", payload.UserID, payload.Amount, payload.EventID, payload.WebsiteID, payload.Immediate, key)
	sign := strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(data))))
	logrus.WithFields(logrus.Fields{
		"event": models.EventOfferwowCallback,
	}).Info(sign)
	return sign == payload.Sign
}

/*
We need to check it one by one

offerwow-01: 出现空参数
offerwow-02: 网站id不存在
offerwow-03: uid会员不存在
offerwow-04: 已发放奖励的Eventid重复
offerwow-05: immediate=0
offerwow-06: immediate=3
*/
func validateOfferwowEvent(
	payload offerwowPayload,
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
) (errno string, user models.User, err error) {
	if payload.UserID == 0 || payload.Amount == 0 || payload.EventID == "" || payload.WebsiteID == "" || payload.Immediate == "" {
		errno = offerwowErrno01
		return
	}

	if payload.WebsiteID != offerwowWebsiteID {
		errno = offerwowErrno02
		return
	}

	// check if user exists
	user, err = getUserByID(payload.UserID)
	if err == errors.ErrNotFound {
		errno = offerwowErrno03
		err = nil
		return
	} else if err != nil {
		return
	}

	// check if eventid duplicates
	_, err = getOfferwowEventByID(payload.EventID)
	switch err {
	case errors.ErrNotFound:
		err = nil
	case nil:
		errno = offerwowErrno04
		return
	default:
		return
	}

	if payload.Immediate == offerwowImmediate0 {
		errno = offerwowErrno05
		return
	}

	if payload.Immediate == offerwowImmediate3 {
		errno = offerwowErrno06
		return
	}

	return
}
<|endoftext|>


<commit_before>package resource

import (
	"errors"
	"fmt"
	"log"
	"math"
	"time"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an EC2 instance after refreshing
// it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Delay      time.Duration    // Wait this time before starting checks
	Pending    []string         // States that are "allowed" and will continue trying
	Refresh    StateRefreshFunc // Refreshes the current state
	Target     string           // Target state
	Timeout    time.Duration    // The amount of time to wait before timeout
	MinTimeout time.Duration    // Smallest time to wait before refreshes
}

// WaitForState watches an object and waits for it to achieve the state
// specified in the configuration using the specified Refresh() func,
// waiting the number of seconds specified in the timeout configuration.
func (conf *StateChangeConf) WaitForState() (interface{}, error) {
	log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)

	notfoundTick := 0

	var result interface{}
	var resulterr error

	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)

		// Wait for the delay
		time.Sleep(conf.Delay)

		var err error
		for tries := 0; ; tries++ {
			// Wait between refreshes using an exponential backoff
			wait := time.Duration(math.Pow(2, float64(tries))) *
				100 * time.Millisecond
			if wait < conf.MinTimeout {
				wait = conf.MinTimeout
			} else if wait > 10*time.Second {
				wait = 10 * time.Second
			}

			log.Printf("[TRACE] Waiting %s before next try", wait)
			time.Sleep(wait)

			var currentState string
			result, currentState, err = conf.Refresh()
			if err != nil {
				resulterr = err
				return
			}

			// If we're waiting for the absence of a thing, then return
			if result == nil && conf.Target == "" {
				return
			}

			if result == nil {
				// If we didn't find the resource, check if we have been
				// not finding it for awhile, and if so, report an error.
				notfoundTick += 1
				if notfoundTick > 20 {
					resulterr = errors.New("couldn't find resource")
					return
				}
			} else {
				// Reset the counter for when a resource isn't found
				notfoundTick = 0

				if currentState == conf.Target {
					return
				}

				found := false
				for _, allowed := range conf.Pending {
					if currentState == allowed {
						found = true
						break
					}
				}

				if !found {
					resulterr = fmt.Errorf(
						"unexpected state '%s', wanted target '%s'",
						currentState,
						conf.Target)
					return
				}
			}
		}
	}()

	select {
	case <-doneCh:
		return result, resulterr
	case <-time.After(conf.Timeout):
		return nil, fmt.Errorf(
			"timeout while waiting for state to become '%s'",
			conf.Target)
	}
}
<commit_msg>helper/resource: allow configuration of not found checks in state change<commit_after>package resource

import (
	"errors"
	"fmt"
	"log"
	"math"
	"time"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
// responsible for refreshing the item being watched for a state change.
//
// It returns three results. `result` is any object that will be returned
// as the final object after waiting for state change. This allows you to
// return the final updated object, for example an EC2 instance after refreshing
// it.
//
// `state` is the latest state of that object. And `err` is any error that
// may have happened while refreshing the state.
type StateRefreshFunc func() (result interface{}, state string, err error)

// StateChangeConf is the configuration struct used for `WaitForState`.
type StateChangeConf struct {
	Delay          time.Duration    // Wait this time before starting checks
	Pending        []string         // States that are "allowed" and will continue trying
	Refresh        StateRefreshFunc // Refreshes the current state
	Target         string           // Target state
	Timeout        time.Duration    // The amount of time to wait before timeout
	MinTimeout     time.Duration    // Smallest time to wait before refreshes
	NotFoundChecks int              // Number of times to allow not found
}

// WaitForState watches an object and waits for it to achieve the state
// specified in the configuration using the specified Refresh() func,
// waiting the number of seconds specified in the timeout configuration.
func (conf *StateChangeConf) WaitForState() (interface{}, error) {
	log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)

	notfoundTick := 0

	// Set a default for times to check for not found
	if conf.NotFoundChecks == 0 {
		conf.NotFoundChecks = 20
	}

	var result interface{}
	var resulterr error

	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)

		// Wait for the delay
		time.Sleep(conf.Delay)

		var err error
		for tries := 0; ; tries++ {
			// Wait between refreshes using an exponential backoff
			wait := time.Duration(math.Pow(2, float64(tries))) *
				100 * time.Millisecond
			if wait < conf.MinTimeout {
				wait = conf.MinTimeout
			} else if wait > 10*time.Second {
				wait = 10 * time.Second
			}

			log.Printf("[TRACE] Waiting %s before next try", wait)
			time.Sleep(wait)

			var currentState string
			result, currentState, err = conf.Refresh()
			if err != nil {
				resulterr = err
				return
			}

			// If we're waiting for the absence of a thing, then return
			if result == nil && conf.Target == "" {
				return
			}

			if result == nil {
				// If we didn't find the resource, check if we have been
				// not finding it for awhile, and if so, report an error.
				notfoundTick += 1
				if notfoundTick > conf.NotFoundChecks {
					resulterr = errors.New("couldn't find resource")
					return
				}
			} else {
				// Reset the counter for when a resource isn't found
				notfoundTick = 0

				if currentState == conf.Target {
					return
				}

				found := false
				for _, allowed := range conf.Pending {
					if currentState == allowed {
						found = true
						break
					}
				}

				if !found {
					resulterr = fmt.Errorf(
						"unexpected state '%s', wanted target '%s'",
						currentState,
						conf.Target)
					return
				}
			}
		}
	}()

	select {
	case <-doneCh:
		return result, resulterr
	case <-time.After(conf.Timeout):
		return nil, fmt.Errorf(
			"timeout while waiting for state to become '%s'",
			conf.Target)
	}
}
<|endoftext|>


<commit_before>// +build linux

package gopsutil

import (
	"syscall"
)

func VirtualMemory() (*VirtualMemoryStat, error) {
	sysinfo := &syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(sysinfo); err != nil {
		return nil, err
	}

	ret := &VirtualMemoryStat{
		Total:   uint64(sysinfo.Totalram),
		Free:    uint64(sysinfo.Freeram),
		Shared:  uint64(sysinfo.Sharedram),
		Buffers: uint64(sysinfo.Bufferram),
	}

	// TODO: platform independent
	ret.Available = ret.Free + ret.Buffers + ret.Cached

	ret.Used = ret.Total - ret.Free
	ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0

	/*
		kern := buffers + cached
		ret.ActualFree = ret.Free + kern
		ret.ActualUsed = ret.Used - kern
	*/

	return ret, nil
}

func SwapMemory() (*SwapMemoryStat, error) {
	sysinfo := &syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(sysinfo); err != nil {
		return nil, err
	}
	ret := &SwapMemoryStat{
		Total: uint64(sysinfo.Totalswap),
		Free:  uint64(sysinfo.Freeswap),
	}
	ret.Used = ret.Total - ret.Free
	ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0

	return ret, nil
}
<commit_msg>mem_linux: VirtualMemory() is now from /proc/meminfo, not syscall.<commit_after>// +build linux

package gopsutil

import (
	"strings"
	"syscall"
)

func VirtualMemory() (*VirtualMemoryStat, error) {
	filename := "/proc/meminfo"
	lines, _ := readLines(filename)

	ret := &VirtualMemoryStat{}
	for _, line := range lines {
		fields := strings.Split(line, ":")
		if len(fields) != 2 {
			continue
		}
		key := strings.TrimSpace(fields[0])
		value := strings.TrimSpace(fields[1])
		value = strings.Replace(value, " kB", "", -1)

		switch key {
		case "MemTotal":
			ret.Total = mustParseUint64(value)
		case "MemFree":
			ret.Free = mustParseUint64(value)
		case "Buffers":
			ret.Buffers = mustParseUint64(value)
		case "Cached":
			ret.Cached = mustParseUint64(value)
		case "Active":
			ret.Active = mustParseUint64(value)
		case "Inactive":
			ret.Inactive = mustParseUint64(value)

		}
	}
	ret.Available = ret.Free + ret.Buffers + ret.Cached
	ret.Used = ret.Total - ret.Free
	ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0

	return ret, nil
}

func SwapMemory() (*SwapMemoryStat, error) {
	sysinfo := &syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(sysinfo); err != nil {
		return nil, err
	}
	ret := &SwapMemoryStat{
		Total: uint64(sysinfo.Totalswap),
		Free:  uint64(sysinfo.Freeswap),
	}
	ret.Used = ret.Total - ret.Free
	ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0

	return ret, nil
}
<|endoftext|>


<commit_before>package jira

import (
	"fmt"
	"strings"

	"github.com/trivago/tgo/tcontainer"
)

// CreateMetaInfo contains information about fields and their attributed to create a ticket.
type CreateMetaInfo struct {
	Expand   string         `json:"expand,omitempty"`
	Projects []*MetaProject `json:"projects,omitempty"`
}

// MetaProject is the meta information about a project returned from createmeta api
type MetaProject struct {
	Expand string `json:"expand,omitempty"`
	Self   string `json:"self, omitempty"`
	Id     string `json:"id,omitempty"`
	Key    string `json:"key,omitempty"`
	Name   string `json:"name,omitempty"`
	// omitted avatarUrls
	IssueTypes []*MetaIssueType `json:"issuetypes,omitempty"`
}

// MetaIssueType represents the different issue types a project has.
//
// Note: Fields is interface because this is an object which can
// have arbitraty keys related to customfields. It is not possible to
// expect these for a general way. This will be returning a map.
// Further processing must be done depending on what is required.
type MetaIssueType struct {
	Self        string                `json:"expand,omitempty"`
	Id          string                `json:"id,omitempty"`
	Description string                `json:"description,omitempty"`
	IconUrl     string                `json:"iconurl,omitempty"`
	Name        string                `json:"name,omitempty"`
	Subtasks    bool                  `json:"subtask,omitempty"`
	Expand      string                `json:"expand,omitempty"`
	Fields      tcontainer.MarshalMap `json:"fields,omitempty"`
}

// GetCreateMeta makes the api call to get the meta information required to create a ticket
func (s *IssueService) GetCreateMeta(projectkey string) (*CreateMetaInfo, *Response, error) {

	apiEndpoint := fmt.Sprintf("/rest/api/2/issue/createmeta?projectKeys=%s&expand=projects.issuetypes.fields", projectkey)

	req, err := s.client.NewRequest("GET", apiEndpoint, nil)
	if err != nil {
		return nil, nil, err
	}

	meta := new(CreateMetaInfo)
	resp, err := s.client.Do(req, meta)

	if err != nil {
		return nil, resp, err
	}

	return meta, resp, nil
}

// GetProjectWithName returns a project with "name" from the meta information recieved. If not found, this returns nil.
// The comparision of the name is case insensitive.
func (m *CreateMetaInfo) GetProjectWithName(name string) *MetaProject {
	for _, m := range m.Projects {
		if strings.ToLower(m.Name) == strings.ToLower(name) {
			return m
		}
	}
	return nil
}

// GetProjectWithKey returns a project with "name" from the meta information recieved. If not found, this returns nil.
// The comparision of the name is case insensitive.
func (m *CreateMetaInfo) GetProjectWithKey(key string) *MetaProject {
	for _, m := range m.Projects {
		if strings.ToLower(m.Key) == strings.ToLower(key) {
			return m
		}
	}
	return nil
}

// GetIssueTypeWithName returns an IssueType with name from a given MetaProject. If not found, this returns nil.
// The comparision of the name is case insensitive
func (p *MetaProject) GetIssueTypeWithName(name string) *MetaIssueType {
	for _, m := range p.IssueTypes {
		if strings.ToLower(m.Name) == strings.ToLower(name) {
			return m
		}
	}
	return nil
}

// GetMandatoryFields returns a map of all the required fields from the MetaIssueTypes.
// if a frield returned by the api was:
// "customfield_10806": {
//					"required": true,
//					"schema": {
//						"type": "any",
//						"custom": "com.pyxis.greenhopper.jira:gh-epic-link",
//						"customId": 10806
//					},
//					"name": "Epic Link",
//					"hasDefaultValue": false,
//					"operations": [
//						"set"
//					]
//				}
// the returned map would have "Epic Link" as the key and "customfield_10806" as value.
// This choice has been made so that the it is easier to generate the create api request later.
func (t *MetaIssueType) GetMandatoryFields() (map[string]string, error) {
	ret := make(map[string]string)
	for key := range t.Fields {
		required, err := t.Fields.Bool(key + "/required")
		if err != nil {
			return nil, err
		}
		if required {
			name, err := t.Fields.String(key + "/name")
			if err != nil {
				return nil, err
			}
			ret[name] = key
		}
	}
	return ret, nil
}

// GetAllFields returns a map of all the fields for an IssueType. This includes all required and not required.
// The key of the returned map is what you see in the form and the value is how it is representated in the jira schema.
func (t *MetaIssueType) GetAllFields() (map[string]string, error) {
	ret := make(map[string]string)
	for key := range t.Fields {

		name, err := t.Fields.String(key + "/name")
		if err != nil {
			return nil, err
		}
		ret[name] = key
	}
	return ret, nil
}

// CheckCompleteAndAvailable checks if the given fields satisfies the mandatory field required to create a issue for the given type
// And also if the given fields are available.
func (t *MetaIssueType) CheckCompleteAndAvailable(config map[string]string) (bool, error) {
	mandatory, err := t.GetMandatoryFields()
	if err != nil {
		return false, err
	}
	all, err := t.GetAllFields()
	if err != nil {
		return false, err
	}

	// check templateconfig against mandatory fields
	for key := range mandatory {
		if _, okay := config[key]; !okay {
			var requiredFields []string
			for name := range mandatory {
				requiredFields = append(requiredFields, name)
			}
			return false, fmt.Errorf("Required field not found in provided jira.fields. Required are: %#v", requiredFields)
		}
	}

	// check templateConfig against all fields to verify they are available
	for key := range config {
		if _, okay := all[key]; !okay {
			var availableFields []string
			for name := range all {
				availableFields = append(availableFields, name)
			}
			return false, fmt.Errorf("Fields in jira.fields are not available in jira. Available are: %#v", availableFields)
		}
	}

	return true, nil
}
<commit_msg>Fix typo metaissue.go<commit_after>package jira

import (
	"fmt"
	"strings"

	"github.com/trivago/tgo/tcontainer"
)

// CreateMetaInfo contains information about fields and their attributed to create a ticket.
type CreateMetaInfo struct {
	Expand   string         `json:"expand,omitempty"`
	Projects []*MetaProject `json:"projects,omitempty"`
}

// MetaProject is the meta information about a project returned from createmeta api
type MetaProject struct {
	Expand string `json:"expand,omitempty"`
	Self   string `json:"self, omitempty"`
	Id     string `json:"id,omitempty"`
	Key    string `json:"key,omitempty"`
	Name   string `json:"name,omitempty"`
	// omitted avatarUrls
	IssueTypes []*MetaIssueType `json:"issuetypes,omitempty"`
}

// MetaIssueType represents the different issue types a project has.
//
// Note: Fields is interface because this is an object which can
// have arbitraty keys related to customfields. It is not possible to
// expect these for a general way. This will be returning a map.
// Further processing must be done depending on what is required.
type MetaIssueType struct {
	Self        string                `json:"expand,omitempty"`
	Id          string                `json:"id,omitempty"`
	Description string                `json:"description,omitempty"`
	IconUrl     string                `json:"iconurl,omitempty"`
	Name        string                `json:"name,omitempty"`
	Subtasks    bool                  `json:"subtask,omitempty"`
	Expand      string                `json:"expand,omitempty"`
	Fields      tcontainer.MarshalMap `json:"fields,omitempty"`
}

// GetCreateMeta makes the api call to get the meta information required to create a ticket
func (s *IssueService) GetCreateMeta(projectkey string) (*CreateMetaInfo, *Response, error) {

	apiEndpoint := fmt.Sprintf("/rest/api/2/issue/createmeta?projectKeys=%s&expand=projects.issuetypes.fields", projectkey)

	req, err := s.client.NewRequest("GET", apiEndpoint, nil)
	if err != nil {
		return nil, nil, err
	}

	meta := new(CreateMetaInfo)
	resp, err := s.client.Do(req, meta)

	if err != nil {
		return nil, resp, err
	}

	return meta, resp, nil
}

// GetProjectWithName returns a project with "name" from the meta information recieved. If not found, this returns nil.
// The comparision of the name is case insensitive.
func (m *CreateMetaInfo) GetProjectWithName(name string) *MetaProject {
	for _, m := range m.Projects {
		if strings.ToLower(m.Name) == strings.ToLower(name) {
			return m
		}
	}
	return nil
}

// GetProjectWithKey returns a project with "name" from the meta information recieved. If not found, this returns nil.
// The comparision of the name is case insensitive.
func (m *CreateMetaInfo) GetProjectWithKey(key string) *MetaProject {
	for _, m := range m.Projects {
		if strings.ToLower(m.Key) == strings.ToLower(key) {
			return m
		}
	}
	return nil
}

// GetIssueTypeWithName returns an IssueType with name from a given MetaProject. If not found, this returns nil.
// The comparision of the name is case insensitive
func (p *MetaProject) GetIssueTypeWithName(name string) *MetaIssueType {
	for _, m := range p.IssueTypes {
		if strings.ToLower(m.Name) == strings.ToLower(name) {
			return m
		}
	}
	return nil
}

// GetMandatoryFields returns a map of all the required fields from the MetaIssueTypes.
// if a field returned by the api was:
// "customfield_10806": {
//					"required": true,
//					"schema": {
//						"type": "any",
//						"custom": "com.pyxis.greenhopper.jira:gh-epic-link",
//						"customId": 10806
//					},
//					"name": "Epic Link",
//					"hasDefaultValue": false,
//					"operations": [
//						"set"
//					]
//				}
// the returned map would have "Epic Link" as the key and "customfield_10806" as value.
// This choice has been made so that the it is easier to generate the create api request later.
func (t *MetaIssueType) GetMandatoryFields() (map[string]string, error) {
	ret := make(map[string]string)
	for key := range t.Fields {
		required, err := t.Fields.Bool(key + "/required")
		if err != nil {
			return nil, err
		}
		if required {
			name, err := t.Fields.String(key + "/name")
			if err != nil {
				return nil, err
			}
			ret[name] = key
		}
	}
	return ret, nil
}

// GetAllFields returns a map of all the fields for an IssueType. This includes all required and not required.
// The key of the returned map is what you see in the form and the value is how it is representated in the jira schema.
func (t *MetaIssueType) GetAllFields() (map[string]string, error) {
	ret := make(map[string]string)
	for key := range t.Fields {

		name, err := t.Fields.String(key + "/name")
		if err != nil {
			return nil, err
		}
		ret[name] = key
	}
	return ret, nil
}

// CheckCompleteAndAvailable checks if the given fields satisfies the mandatory field required to create a issue for the given type
// And also if the given fields are available.
func (t *MetaIssueType) CheckCompleteAndAvailable(config map[string]string) (bool, error) {
	mandatory, err := t.GetMandatoryFields()
	if err != nil {
		return false, err
	}
	all, err := t.GetAllFields()
	if err != nil {
		return false, err
	}

	// check templateconfig against mandatory fields
	for key := range mandatory {
		if _, okay := config[key]; !okay {
			var requiredFields []string
			for name := range mandatory {
				requiredFields = append(requiredFields, name)
			}
			return false, fmt.Errorf("Required field not found in provided jira.fields. Required are: %#v", requiredFields)
		}
	}

	// check templateConfig against all fields to verify they are available
	for key := range config {
		if _, okay := all[key]; !okay {
			var availableFields []string
			for name := range all {
				availableFields = append(availableFields, name)
			}
			return false, fmt.Errorf("Fields in jira.fields are not available in jira. Available are: %#v", availableFields)
		}
	}

	return true, nil
}
<|endoftext|>


<commit_before>package floatingips

import (
	"fmt"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the floating IP attributes you want to see returned. SortKey allows you to
// sort by a particular network attribute. SortDir sets the direction, and is
// either `asc' or `desc'. Marker and Limit are used for pagination.
type ListOpts struct {
	ID                string `q:"id"`
	FloatingNetworkID string `q:"floating_network_id"`
	PortID            string `q:"port_id"`
	FixedIP           string `q:"fixed_ip_address"`
	FloatingIP        string `q:"floating_ip_address"`
	TenantID          string `q:"tenant_id"`
	Limit             int    `q:"limit"`
	Marker            string `q:"marker"`
	SortKey           string `q:"sort_key"`
	SortDir           string `q:"sort_dir"`
}

// List returns a Pager which allows you to iterate over a collection of
// floating IP resources. It accepts a ListOpts struct, which allows you to
// filter and sort the returned collection for greater efficiency.
func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
	q, err := gophercloud.BuildQueryString(&opts)
	if err != nil {
		return pagination.Pager{Err: err}
	}
	u := rootURL(c) + q.String()
	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
		return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}}
	})
}

// CreateOpts contains all the values needed to create a new floating IP
// resource. The only required fields are FloatingNetworkID and PortID which
// refer to the external network and internal port respectively.
type CreateOpts struct {
	FloatingNetworkID string
	FloatingIP        string
	PortID            string
	FixedIP           string
	TenantID          string
}

var (
	errFloatingNetworkIDRequired = fmt.Errorf("A NetworkID is required")
)

// Create accepts a CreateOpts struct and uses the values provided to create a
// new floating IP resource. You can create floating IPs on external networks
// only. If you provide a FloatingNetworkID which refers to a network that is
// not external (i.e. its `router:external' attribute is False), the operation
// will fail and return a 400 error.
//
// If you do not specify a FloatingIP address value, the operation will
// automatically allocate an available address for the new resource. If you do
// choose to specify one, it must fall within the subnet range for the external
// network - otherwise the operation returns a 400 error. If the FloatingIP
// address is already in use, the operation returns a 409 error code.
//
// You can associate the new resource with an internal port by using the PortID
// field. If you specify a PortID that is not valid, the operation will fail and
// return 404 error code.
//
// You must also configure an IP address for the port associated with the PortID
// you have provided - this is what the FixedIP refers to: an IP fixed to a port.
// Because a port might be associated with multiple IP addresses, you can use
// the FixedIP field to associate a particular IP address rather than have the
// API assume for you. If you specify an IP address that is not valid, the
// operation will fail and return a 400 error code. If the PortID and FixedIP
// are already associated with another resource, the operation will fail and
// returns a 409 error code.
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
	var res CreateResult

	// Validate
	if opts.FloatingNetworkID == "" {
		res.Err = errFloatingNetworkIDRequired
		return res
	}

	// Define structures
	type floatingIP struct {
		FloatingNetworkID string `json:"floating_network_id"`
		FloatingIP        string `json:"floating_ip_address,omitempty"`
		PortID            string `json:"port_id,omitempty"`
		FixedIP           string `json:"fixed_ip_address,omitempty"`
		TenantID          string `json:"tenant_id,omitempty"`
	}
	type request struct {
		FloatingIP floatingIP `json:"floatingip"`
	}

	// Populate request body
	reqBody := request{FloatingIP: floatingIP{
		FloatingNetworkID: opts.FloatingNetworkID,
		PortID:            opts.PortID,
		FixedIP:           opts.FixedIP,
		TenantID:          opts.TenantID,
	}}

	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
	return res
}

// Get retrieves a particular floating IP resource based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
	var res GetResult
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
	return res
}

// UpdateOpts contains the values used when updating a floating IP resource. The
// only value that can be updated is which internal port the floating IP is
// linked to. To associate the floating IP with a new internal port, provide its
// ID. To disassociate the floating IP from all ports, provide an empty string.
type UpdateOpts struct {
	PortID string
}

// Update allows floating IP resources to be updated. Currently, the only way to
// "update" a floating IP is to associate it with a new internal port, or
// disassociated it from all ports. See UpdateOpts for instructions of how to
// do this.
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
	type floatingIP struct {
		PortID *string `json:"port_id"`
	}

	type request struct {
		FloatingIP floatingIP `json:"floatingip"`
	}

	var portID *string
	if opts.PortID == "" {
		portID = nil
	} else {
		portID = &opts.PortID
	}

	reqBody := request{FloatingIP: floatingIP{PortID: portID}}

	// Send request to API
	var res UpdateResult
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
		OkCodes: []int{200},
	})

	return res
}

// Delete will permanently delete a particular floating IP resource. Please
// ensure this is what you want - you can also disassociate the IP from existing
// internal ports.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
	var res DeleteResult
	_, res.Err = c.Delete(resourceURL(c, id), nil)
	return res
}
<commit_msg>Fix gophercloud to create floatingip<commit_after>package floatingips

import (
	"fmt"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/pagination"
)

// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the floating IP attributes you want to see returned. SortKey allows you to
// sort by a particular network attribute. SortDir sets the direction, and is
// either `asc' or `desc'. Marker and Limit are used for pagination.
type ListOpts struct {
	ID                string `q:"id"`
	FloatingNetworkID string `q:"floating_network_id"`
	PortID            string `q:"port_id"`
	FixedIP           string `q:"fixed_ip_address"`
	FloatingIP        string `q:"floating_ip_address"`
	TenantID          string `q:"tenant_id"`
	Limit             int    `q:"limit"`
	Marker            string `q:"marker"`
	SortKey           string `q:"sort_key"`
	SortDir           string `q:"sort_dir"`
}

// List returns a Pager which allows you to iterate over a collection of
// floating IP resources. It accepts a ListOpts struct, which allows you to
// filter and sort the returned collection for greater efficiency.
func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
	q, err := gophercloud.BuildQueryString(&opts)
	if err != nil {
		return pagination.Pager{Err: err}
	}
	u := rootURL(c) + q.String()
	return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
		return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}}
	})
}

// CreateOpts contains all the values needed to create a new floating IP
// resource. The only required fields are FloatingNetworkID and PortID which
// refer to the external network and internal port respectively.
type CreateOpts struct {
	FloatingNetworkID string
	FloatingIP        string
	PortID            string
	FixedIP           string
	TenantID          string
}

var (
	errFloatingNetworkIDRequired = fmt.Errorf("A NetworkID is required")
)

// Create accepts a CreateOpts struct and uses the values provided to create a
// new floating IP resource. You can create floating IPs on external networks
// only. If you provide a FloatingNetworkID which refers to a network that is
// not external (i.e. its `router:external' attribute is False), the operation
// will fail and return a 400 error.
//
// If you do not specify a FloatingIP address value, the operation will
// automatically allocate an available address for the new resource. If you do
// choose to specify one, it must fall within the subnet range for the external
// network - otherwise the operation returns a 400 error. If the FloatingIP
// address is already in use, the operation returns a 409 error code.
//
// You can associate the new resource with an internal port by using the PortID
// field. If you specify a PortID that is not valid, the operation will fail and
// return 404 error code.
//
// You must also configure an IP address for the port associated with the PortID
// you have provided - this is what the FixedIP refers to: an IP fixed to a port.
// Because a port might be associated with multiple IP addresses, you can use
// the FixedIP field to associate a particular IP address rather than have the
// API assume for you. If you specify an IP address that is not valid, the
// operation will fail and return a 400 error code. If the PortID and FixedIP
// are already associated with another resource, the operation will fail and
// returns a 409 error code.
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
	var res CreateResult

	// Validate
	if opts.FloatingNetworkID == "" {
		res.Err = errFloatingNetworkIDRequired
		return res
	}

	// Define structures
	type floatingIP struct {
		FloatingNetworkID string `json:"floating_network_id"`
		FloatingIP        string `json:"floating_ip_address,omitempty"`
		PortID            string `json:"port_id,omitempty"`
		FixedIP           string `json:"fixed_ip_address,omitempty"`
		TenantID          string `json:"tenant_id,omitempty"`
	}
	type request struct {
		FloatingIP floatingIP `json:"floatingip"`
	}

	// Populate request body
	reqBody := request{FloatingIP: floatingIP{
		FloatingNetworkID: opts.FloatingNetworkID,
		FloatingIP:        opts.FloatingIP,
		PortID:            opts.PortID,
		FixedIP:           opts.FixedIP,
		TenantID:          opts.TenantID,
	}}

	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
	return res
}

// Get retrieves a particular floating IP resource based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
	var res GetResult
	_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
	return res
}

// UpdateOpts contains the values used when updating a floating IP resource. The
// only value that can be updated is which internal port the floating IP is
// linked to. To associate the floating IP with a new internal port, provide its
// ID. To disassociate the floating IP from all ports, provide an empty string.
type UpdateOpts struct {
	PortID string
}

// Update allows floating IP resources to be updated. Currently, the only way to
// "update" a floating IP is to associate it with a new internal port, or
// disassociated it from all ports. See UpdateOpts for instructions of how to
// do this.
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
	type floatingIP struct {
		PortID *string `json:"port_id"`
	}

	type request struct {
		FloatingIP floatingIP `json:"floatingip"`
	}

	var portID *string
	if opts.PortID == "" {
		portID = nil
	} else {
		portID = &opts.PortID
	}

	reqBody := request{FloatingIP: floatingIP{PortID: portID}}

	// Send request to API
	var res UpdateResult
	_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
		OkCodes: []int{200},
	})

	return res
}

// Delete will permanently delete a particular floating IP resource. Please
// ensure this is what you want - you can also disassociate the IP from existing
// internal ports.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
	var res DeleteResult
	_, res.Err = c.Delete(resourceURL(c, id), nil)
	return res
}
<|endoftext|>


<commit_before>package gomatrixserverlib

import (
	"encoding/json"
	"fmt"
)

// A RespSend is the content of a response to PUT /_matrix/federation/v1/send/{txnID}/
type RespSend struct {
	// Map of event ID to the result of processing that event.
	PDUs map[string]PDUResult `json:"pdus"`
}

// A PDUResult is the result of processing a matrix room event.
type PDUResult struct {
	// If not empty then this is a human readable description of a problem
	// encountered processing an event.
	Error string `json:"error,omitempty"`
}

// A RespStateIDs is the content of a response to GET /_matrix/federation/v1/state_ids/{roomID}/{eventID}
type RespStateIDs struct {
	// A list of state event IDs for the state of the room before the requested event.
	StateEventIDs []string `json:"pdu_ids"`
	// A list of event IDs needed to authenticate the state events.
	AuthEventIDs []string `json:"auth_chain_ids"`
}

// A RespState is the content of a response to GET /_matrix/federation/v1/state/{roomID}/{eventID}
type RespState struct {
	// A list of events giving the state of the room before the request event.
	StateEvents []Event `json:"pdus"`
	// A list of events needed to authenticate the state events.
	AuthEvents []Event `json:"auth_chain"`
}

// Check that a response to /state is valid.
func (r RespState) Check(keyRing KeyRing) error {
	var allEvents []Event
	for _, event := range r.AuthEvents {
		if event.StateKey() == nil {
			return fmt.Errorf("gomatrixserverlib: event %q does not have a state key", event.EventID())
		}
		allEvents = append(allEvents, event)
	}

	stateTuples := map[StateKeyTuple]bool{}
	for _, event := range r.StateEvents {
		if event.StateKey() == nil {
			return fmt.Errorf("gomatrixserverlib: event %q does not have a state key", event.EventID())
		}
		stateTuple := StateKeyTuple{event.Type(), *event.StateKey()}
		if stateTuples[stateTuple] {
			return fmt.Errorf(
				"gomatrixserverlib: duplicate state key tuple (%q, %q)",
				event.Type(), *event.StateKey(),
			)
		}
		stateTuples[stateTuple] = true
		allEvents = append(allEvents, event)
	}

	// Check if the events pass signature checks.
	if err := VerifyEventSignatures(allEvents, keyRing); err != nil {
		return nil
	}

	eventsByID := map[string]*Event{}
	// Collect a map of event reference to event
	for i := range allEvents {
		eventsByID[allEvents[i].EventID()] = &allEvents[i]
	}

	// Check whether the events are allowed by the auth rules.
	for _, event := range allEvents {
		if err := checkAllowedByAuthEvents(event, eventsByID); err != nil {
			return err
		}
	}

	return nil
}

// A RespMakeJoin is the content of a response to GET /_matrix/federation/v1/make_join/{roomID}/{userID}
type RespMakeJoin struct {
	// An incomplete m.room.member event for a user on the requesting server
	// generated by the responding server.
	// See https://matrix.org/docs/spec/server_server/unstable.html#joining-rooms
	JoinEvent EventBuilder `json:"event"`
}

// A RespSendJoin is the content of a response to PUT /_matrix/federation/v1/send_join/{roomID}/{eventID}
// It has the same data as a response to /state, but in a slightly different wire format.
type RespSendJoin RespState

// MarshalJSON implements json.Marshaller
func (r RespSendJoin) MarshalJSON() ([]byte, error) {
	// SendJoinResponses contain the same data as a StateResponse but are
	// formatted slightly differently on the wire:
	//  1) The "pdus" field is renamed to "state".
	//  2) The object is placed as the second element of a two element list
	//     where the first element is the constant integer 200.
	//
	//
	// So a state response of:
	//
	//		{"pdus": x, "auth_chain": y}
	//
	// Becomes:
	//
	//      [200, {"state": x, "auth_chain": y}]
	//
	// (This protocol oddity is the result of a typo in the synapse matrix
	//  server, and is preserved to maintain compatibility.)

	return json.Marshal([]interface{}{200, respSendJoinFields{
		r.StateEvents, r.AuthEvents,
	}})
}

// UnmarshalJSON implements json.Unmarshaller
func (r *RespSendJoin) UnmarshalJSON(data []byte) error {
	var tuple []rawJSON
	if err := json.Unmarshal(data, &tuple); err != nil {
		return err
	}
	if len(tuple) != 2 {
		return fmt.Errorf("gomatrixserverlib: invalid send join response, invalid length: %d != 2", len(tuple))
	}
	var fields respSendJoinFields
	if err := json.Unmarshal(tuple[1], &fields); err != nil {
		return err
	}
	r.StateEvents = fields.StateEvents
	r.AuthEvents = fields.AuthEvents
	return nil
}

type respSendJoinFields struct {
	StateEvents []Event `json:"state"`
	AuthEvents  []Event `json:"auth_chain"`
}

// Check that a reponse to /send_join is valid.
// This checks that it would be valid as a response to /state
// This also checks that the join event is allowed by the state.
func (r RespSendJoin) Check(keyRing KeyRing, joinEvent Event) error {
	// First check that the state is valid.
	// The response to /send_join has the same data as a response to /state
	// and the checks for a response to /state also apply.
	if err := RespState(r).Check(keyRing); err != nil {
		return err
	}

	stateEventsByID := map[string]*Event{}
	authEvents := NewAuthEvents(nil)
	for i, event := range r.StateEvents {
		stateEventsByID[event.EventID()] = &r.StateEvents[i]
		if err := authEvents.AddEvent(&r.StateEvents[i]); err != nil {
			return err
		}
	}

	// Now check that the join event is valid against its auth events.
	if err := checkAllowedByAuthEvents(joinEvent, stateEventsByID); err != nil {
		return err
	}

	// Now check that the join event is valid against the supplied state.
	if err := Allowed(joinEvent, &authEvents); err != nil {
		return fmt.Errorf(
			"gomatrixserverlib: event with ID %q is not allowed by the supplied state: %s",
			joinEvent.EventID(), err.Error(),
		)

	}

	return nil
}

// A RespDirectory is the content of a response to GET  /_matrix/federation/v1/query/directory
// This is returned when looking up a room alias from a remote server.
// See https://matrix.org/docs/spec/server_server/unstable.html#directory
type RespDirectory struct {
	// The matrix room ID the room alias corresponds to.
	RoomID string `json:"room_id"`
	// A list of matrix servers that the directory server thinks could be used
	// to join the room. The joining server may need to try multiple servers
	// before it finds one that it can use to join the room.
	Servers []ServerName `json:"servers"`
}

func checkAllowedByAuthEvents(event Event, eventsByID map[string]*Event) error {
	authEvents := NewAuthEvents(nil)
	for _, authRef := range event.AuthEvents() {
		authEvent := eventsByID[authRef.EventID]
		if authEvent == nil {
			return fmt.Errorf(
				"gomatrixserverlib: missing auth event with ID %q for event %q",
				authRef.EventID, event.EventID(),
			)
		}
		if err := authEvents.AddEvent(authEvent); err != nil {
			return err
		}
	}
	if err := Allowed(event, &authEvents); err != nil {
		return fmt.Errorf(
			"gomatrixserverlib: event with ID %q is not allowed by its auth_events: %s",
			event.EventID(), err.Error(),
		)
	}
	return nil
}
<commit_msg>Add method for listing the events in a /state response (#45)<commit_after>package gomatrixserverlib

import (
	"encoding/json"
	"fmt"
)

// A RespSend is the content of a response to PUT /_matrix/federation/v1/send/{txnID}/
type RespSend struct {
	// Map of event ID to the result of processing that event.
	PDUs map[string]PDUResult `json:"pdus"`
}

// A PDUResult is the result of processing a matrix room event.
type PDUResult struct {
	// If not empty then this is a human readable description of a problem
	// encountered processing an event.
	Error string `json:"error,omitempty"`
}

// A RespStateIDs is the content of a response to GET /_matrix/federation/v1/state_ids/{roomID}/{eventID}
type RespStateIDs struct {
	// A list of state event IDs for the state of the room before the requested event.
	StateEventIDs []string `json:"pdu_ids"`
	// A list of event IDs needed to authenticate the state events.
	AuthEventIDs []string `json:"auth_chain_ids"`
}

// A RespState is the content of a response to GET /_matrix/federation/v1/state/{roomID}/{eventID}
type RespState struct {
	// A list of events giving the state of the room before the request event.
	StateEvents []Event `json:"pdus"`
	// A list of events needed to authenticate the state events.
	AuthEvents []Event `json:"auth_chain"`
}

// Events combines the auth events and the state events and returns
// them in an order where every event comes after its auth events.
// Each event will only appear once in the output list.
// Returns an error if there are missing auth events or if there is
// a cycle in the auth events.
func (r RespState) Events() ([]Event, error) {
	eventsByID := map[string]*Event{}
	// Collect a map of event reference to event
	for i := range r.StateEvents {
		eventsByID[r.StateEvents[i].EventID()] = &r.StateEvents[i]
	}
	for i := range r.AuthEvents {
		eventsByID[r.AuthEvents[i].EventID()] = &r.AuthEvents[i]
	}

	queued := map[*Event]bool{}
	outputted := map[*Event]bool{}
	var result []Event
	for _, event := range eventsByID {
		if outputted[event] {
			// If we've already written the event then we can skip it.
			continue
		}

		// The code below does a depth first scan through the auth events
		// looking for events that can be appended to the output.

		// We use an explicit stack rather than using recursion so
		// that we can check we aren't creating cycles.
		stack := []*Event{event}

	LoopProcessTopOfStack:
		for len(stack) > 0 {
			top := stack[len(stack)-1]
			// Check if we can output the top of the stack.
			// We can output it if we have outputted all of its auth_events.
			for _, ref := range top.AuthEvents() {
				authEvent := eventsByID[ref.EventID]
				if authEvent == nil {
					return nil, fmt.Errorf(
						"gomatrixserverlib: missing auth event with ID %q for event %q",
						ref.EventID, top.EventID(),
					)
				}
				if outputted[authEvent] {
					continue
				}
				if queued[authEvent] {
					return nil, fmt.Errorf(
						"gomatrixserverlib: auth event cycle for ID %q",
						ref.EventID,
					)
				}
				// If we haven't visited the auth event yet then we need to
				// process it before processing the event currently on top of
				// the stack.
				stack = append(stack, authEvent)
				queued[authEvent] = true
				continue LoopProcessTopOfStack
			}
			// If we've processed all the auth events for the event on top of
			// the stack then we can append it to the result and try processing
			// the item below it in the stack.
			result = append(result, *top)
			outputted[top] = true
			stack = stack[:len(stack)-1]
		}
	}

	return result, nil
}

// Check that a response to /state is valid.
func (r RespState) Check(keyRing KeyRing) error {
	var allEvents []Event
	for _, event := range r.AuthEvents {
		if event.StateKey() == nil {
			return fmt.Errorf("gomatrixserverlib: event %q does not have a state key", event.EventID())
		}
		allEvents = append(allEvents, event)
	}

	stateTuples := map[StateKeyTuple]bool{}
	for _, event := range r.StateEvents {
		if event.StateKey() == nil {
			return fmt.Errorf("gomatrixserverlib: event %q does not have a state key", event.EventID())
		}
		stateTuple := StateKeyTuple{event.Type(), *event.StateKey()}
		if stateTuples[stateTuple] {
			return fmt.Errorf(
				"gomatrixserverlib: duplicate state key tuple (%q, %q)",
				event.Type(), *event.StateKey(),
			)
		}
		stateTuples[stateTuple] = true
		allEvents = append(allEvents, event)
	}

	// Check if the events pass signature checks.
	if err := VerifyEventSignatures(allEvents, keyRing); err != nil {
		return nil
	}

	eventsByID := map[string]*Event{}
	// Collect a map of event reference to event
	for i := range allEvents {
		eventsByID[allEvents[i].EventID()] = &allEvents[i]
	}

	// Check whether the events are allowed by the auth rules.
	for _, event := range allEvents {
		if err := checkAllowedByAuthEvents(event, eventsByID); err != nil {
			return err
		}
	}

	return nil
}

// A RespMakeJoin is the content of a response to GET /_matrix/federation/v1/make_join/{roomID}/{userID}
type RespMakeJoin struct {
	// An incomplete m.room.member event for a user on the requesting server
	// generated by the responding server.
	// See https://matrix.org/docs/spec/server_server/unstable.html#joining-rooms
	JoinEvent EventBuilder `json:"event"`
}

// A RespSendJoin is the content of a response to PUT /_matrix/federation/v1/send_join/{roomID}/{eventID}
// It has the same data as a response to /state, but in a slightly different wire format.
type RespSendJoin RespState

// MarshalJSON implements json.Marshaller
func (r RespSendJoin) MarshalJSON() ([]byte, error) {
	// SendJoinResponses contain the same data as a StateResponse but are
	// formatted slightly differently on the wire:
	//  1) The "pdus" field is renamed to "state".
	//  2) The object is placed as the second element of a two element list
	//     where the first element is the constant integer 200.
	//
	//
	// So a state response of:
	//
	//		{"pdus": x, "auth_chain": y}
	//
	// Becomes:
	//
	//      [200, {"state": x, "auth_chain": y}]
	//
	// (This protocol oddity is the result of a typo in the synapse matrix
	//  server, and is preserved to maintain compatibility.)

	return json.Marshal([]interface{}{200, respSendJoinFields{
		r.StateEvents, r.AuthEvents,
	}})
}

// UnmarshalJSON implements json.Unmarshaller
func (r *RespSendJoin) UnmarshalJSON(data []byte) error {
	var tuple []rawJSON
	if err := json.Unmarshal(data, &tuple); err != nil {
		return err
	}
	if len(tuple) != 2 {
		return fmt.Errorf("gomatrixserverlib: invalid send join response, invalid length: %d != 2", len(tuple))
	}
	var fields respSendJoinFields
	if err := json.Unmarshal(tuple[1], &fields); err != nil {
		return err
	}
	r.StateEvents = fields.StateEvents
	r.AuthEvents = fields.AuthEvents
	return nil
}

type respSendJoinFields struct {
	StateEvents []Event `json:"state"`
	AuthEvents  []Event `json:"auth_chain"`
}

// Check that a reponse to /send_join is valid.
// This checks that it would be valid as a response to /state
// This also checks that the join event is allowed by the state.
func (r RespSendJoin) Check(keyRing KeyRing, joinEvent Event) error {
	// First check that the state is valid.
	// The response to /send_join has the same data as a response to /state
	// and the checks for a response to /state also apply.
	if err := RespState(r).Check(keyRing); err != nil {
		return err
	}

	stateEventsByID := map[string]*Event{}
	authEvents := NewAuthEvents(nil)
	for i, event := range r.StateEvents {
		stateEventsByID[event.EventID()] = &r.StateEvents[i]
		if err := authEvents.AddEvent(&r.StateEvents[i]); err != nil {
			return err
		}
	}

	// Now check that the join event is valid against its auth events.
	if err := checkAllowedByAuthEvents(joinEvent, stateEventsByID); err != nil {
		return err
	}

	// Now check that the join event is valid against the supplied state.
	if err := Allowed(joinEvent, &authEvents); err != nil {
		return fmt.Errorf(
			"gomatrixserverlib: event with ID %q is not allowed by the supplied state: %s",
			joinEvent.EventID(), err.Error(),
		)

	}

	return nil
}

// A RespDirectory is the content of a response to GET  /_matrix/federation/v1/query/directory
// This is returned when looking up a room alias from a remote server.
// See https://matrix.org/docs/spec/server_server/unstable.html#directory
type RespDirectory struct {
	// The matrix room ID the room alias corresponds to.
	RoomID string `json:"room_id"`
	// A list of matrix servers that the directory server thinks could be used
	// to join the room. The joining server may need to try multiple servers
	// before it finds one that it can use to join the room.
	Servers []ServerName `json:"servers"`
}

func checkAllowedByAuthEvents(event Event, eventsByID map[string]*Event) error {
	authEvents := NewAuthEvents(nil)
	for _, authRef := range event.AuthEvents() {
		authEvent := eventsByID[authRef.EventID]
		if authEvent == nil {
			return fmt.Errorf(
				"gomatrixserverlib: missing auth event with ID %q for event %q",
				authRef.EventID, event.EventID(),
			)
		}
		if err := authEvents.AddEvent(authEvent); err != nil {
			return err
		}
	}
	if err := Allowed(event, &authEvents); err != nil {
		return fmt.Errorf(
			"gomatrixserverlib: event with ID %q is not allowed by its auth_events: %s",
			event.EventID(), err.Error(),
		)
	}
	return nil
}
<|endoftext|>


<commit_before>package libkb

import (
	"errors"
	"fmt"
	"testing"
)

// Used by tests that want to mock out the secret store.
type TestSecretStore struct {
	Secret []byte
}

func (tss *TestSecretStore) RetrieveSecret() ([]byte, error) {
	G.Log.Debug("| TestSecretStore::RetrieveSecret(%d)", len(tss.Secret))

	if len(tss.Secret) == 0 {
		return nil, errors.New("No secret to retrieve")
	}

	return tss.Secret, nil
}

func (tss *TestSecretStore) StoreSecret(secret []byte) error {
	G.Log.Debug("| TestSecretStore::StoreSecret(%d)", len(secret))

	tss.Secret = secret
	return nil
}

func (tss *TestSecretStore) ClearSecret() error {
	G.Log.Debug("| TestSecretStore::ClearSecret()")

	tss.Secret = nil
	return nil
}

func TestSecretStoreOps(t *testing.T) {
	if !HasSecretStore() {
		t.Skip("Skipping test since there is no secret store")
	}

	username := "test username"
	expectedSecret1 := []byte("test secret 1")
	expectedSecret2 := []byte("test secret 2")

	secretStore := NewSecretStore(username)

	var err error

	if err = secretStore.ClearSecret(); err != nil {
		t.Error(err)
	}

	// TODO: Use platform-independent errors so they can be
	// checked for.
	var secret []byte
	if secret, err = secretStore.RetrieveSecret(); err == nil {
		t.Error("RetrieveSecret unexpectedly returned a nil error")
	}

	if secret != nil {
		t.Errorf("Retrieved secret unexpectedly: %s", string(secret))
	}

	if err = secretStore.StoreSecret(expectedSecret1); err != nil {
		t.Error(err)
	}

	if secret, err = secretStore.RetrieveSecret(); err != nil {
		t.Error(err)
	}

	if string(secret) != string(expectedSecret1) {
		t.Errorf("Retrieved secret %s, expected %s", string(secret), string(expectedSecret1))
	}

	if err = secretStore.StoreSecret(expectedSecret2); err != nil {
		t.Error(err)
	}

	if secret, err = secretStore.RetrieveSecret(); err != nil {
		t.Error(err)
	}

	if string(secret) != string(expectedSecret2) {
		t.Errorf("Retrieved secret %s, expected %s", string(secret), string(expectedSecret2))
	}

	if err = secretStore.ClearSecret(); err != nil {
		t.Error(err)
	}
}

func TestGetUsersWithStoredSecrets(t *testing.T) {
	if !HasSecretStore() {
		t.Skip("Skipping test since there is no secret store")
	}

	usernames, err := GetUsersWithStoredSecrets()
	if err != nil {
		t.Error(err)
	}
	if len(usernames) != 0 {
		t.Errorf("Expected no usernames, got %d", len(usernames))
	}

	expectedUsernames := make([]string, 10)
	for i := 0; i < len(expectedUsernames); i++ {
		expectedUsernames[i] = fmt.Sprintf("test account with unicode テスト %d", i)
		secretStore := NewSecretStore(expectedUsernames[i])
		if err := secretStore.StoreSecret([]byte{}); err != nil {
			t.Error(err)
		}
	}

	usernames, err = GetUsersWithStoredSecrets()
	if err != nil {
		t.Error(err)
	}

	if len(usernames) != len(expectedUsernames) {
		t.Errorf("Expected %d usernames, got %d", len(expectedUsernames), len(usernames))
	}

	for i := 0; i < len(usernames); i++ {
		if usernames[i] != expectedUsernames[i] {
			t.Errorf("Expected username %s, got %s", expectedUsernames[i], usernames[i])
		}
	}

	for i := 0; i < len(expectedUsernames); i++ {
		secretStore := NewSecretStore(expectedUsernames[i])
		err = secretStore.ClearSecret()
		if err != nil {
			t.Error(err)
		}
	}

	usernames, err = GetUsersWithStoredSecrets()
	if err != nil {
		t.Error(err)
	}
	if len(usernames) != 0 {
		t.Errorf("Expected no usernames, got %d", len(usernames))
	}
}
<commit_msg>Prefix test account names in secret_store_test.go<commit_after>package libkb

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
	"strings"
	"testing"
)

// Used by tests that want to mock out the secret store.
type TestSecretStore struct {
	Secret []byte
}

func (tss *TestSecretStore) RetrieveSecret() ([]byte, error) {
	G.Log.Debug("| TestSecretStore::RetrieveSecret(%d)", len(tss.Secret))

	if len(tss.Secret) == 0 {
		return nil, errors.New("No secret to retrieve")
	}

	return tss.Secret, nil
}

func (tss *TestSecretStore) StoreSecret(secret []byte) error {
	G.Log.Debug("| TestSecretStore::StoreSecret(%d)", len(secret))

	tss.Secret = secret
	return nil
}

func (tss *TestSecretStore) ClearSecret() error {
	G.Log.Debug("| TestSecretStore::ClearSecret()")

	tss.Secret = nil
	return nil
}

func generateTestPrefix(t *testing.T) string {
	buf := make([]byte, 5)
	if _, err := rand.Read(buf); err != nil {
		t.Fatal(err)
	}
	return fmt.Sprintf("test_%s_", hex.EncodeToString(buf))
}

func TestSecretStoreOps(t *testing.T) {
	if !HasSecretStore() {
		t.Skip("Skipping test since there is no secret store")
	}

	prefix := generateTestPrefix(t)

	username := prefix + "username"
	expectedSecret1 := []byte("test secret 1")
	expectedSecret2 := []byte("test secret 2")

	secretStore := NewSecretStore(username)

	var err error

	if err = secretStore.ClearSecret(); err != nil {
		t.Error(err)
	}

	// TODO: Use platform-independent errors so they can be
	// checked for.
	var secret []byte
	if secret, err = secretStore.RetrieveSecret(); err == nil {
		t.Error("RetrieveSecret unexpectedly returned a nil error")
	}

	if secret != nil {
		t.Errorf("Retrieved secret unexpectedly: %s", string(secret))
	}

	if err = secretStore.StoreSecret(expectedSecret1); err != nil {
		t.Error(err)
	}

	if secret, err = secretStore.RetrieveSecret(); err != nil {
		t.Error(err)
	}

	if string(secret) != string(expectedSecret1) {
		t.Errorf("Retrieved secret %s, expected %s", string(secret), string(expectedSecret1))
	}

	if err = secretStore.StoreSecret(expectedSecret2); err != nil {
		t.Error(err)
	}

	if secret, err = secretStore.RetrieveSecret(); err != nil {
		t.Error(err)
	}

	if string(secret) != string(expectedSecret2) {
		t.Errorf("Retrieved secret %s, expected %s", string(secret), string(expectedSecret2))
	}

	if err = secretStore.ClearSecret(); err != nil {
		t.Error(err)
	}
}

func getUsersWithPrefixAndStoredSecrets(prefix string) ([]string, error) {
	usernames, err := GetUsersWithStoredSecrets()
	if err != nil {
		return nil, err
	}

	var testUsernames []string

	for _, username := range usernames {
		if strings.HasPrefix(username, prefix) {
			testUsernames = append(testUsernames, username)
		}
	}

	return testUsernames, nil
}

func TestGetUsersWithStoredSecrets(t *testing.T) {
	if !HasSecretStore() {
		t.Skip("Skipping test since there is no secret store")
	}

	prefix := generateTestPrefix(t)

	usernames, err := getUsersWithPrefixAndStoredSecrets(prefix)
	if err != nil {
		t.Error(err)
	}
	if len(usernames) != 0 {
		t.Errorf("Expected no usernames, got %d", len(usernames))
	}

	expectedUsernames := make([]string, 10)
	for i := 0; i < len(expectedUsernames); i++ {
		expectedUsernames[i] = fmt.Sprintf("%saccount with unicode テスト %d", prefix, i)
		secretStore := NewSecretStore(expectedUsernames[i])
		if err := secretStore.StoreSecret([]byte{}); err != nil {
			t.Error(err)
		}
	}

	usernames, err = getUsersWithPrefixAndStoredSecrets(prefix)
	if err != nil {
		t.Error(err)
	}

	if len(usernames) != len(expectedUsernames) {
		t.Errorf("Expected %d usernames, got %d", len(expectedUsernames), len(usernames))
	}

	for i := 0; i < len(usernames); i++ {
		if usernames[i] != expectedUsernames[i] {
			t.Errorf("Expected username %s, got %s", expectedUsernames[i], usernames[i])
		}
	}

	for i := 0; i < len(expectedUsernames); i++ {
		secretStore := NewSecretStore(expectedUsernames[i])
		err = secretStore.ClearSecret()
		if err != nil {
			t.Error(err)
		}
	}

	usernames, err = getUsersWithPrefixAndStoredSecrets(prefix)
	if err != nil {
		t.Error(err)
	}
	if len(usernames) != 0 {
		t.Errorf("Expected no usernames, got %d", len(usernames))
	}
}
<|endoftext|>


<commit_before>package mfa

import (
	"context"
	"testing"

	logicaltest "github.com/hashicorp/vault/sdk/testing/stepwise"
	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/logical"
)

// MakeTestBackend creates a simple MFA enabled backend.
// Login (before MFA) always succeeds with policy "foo".
// An MFA "test" type is added to mfa.handlers that succeeds
// if MFA method is "accept", otherwise it rejects.
func MakeTestBackend() *framework.Backend {
	handlers["test"] = testMFAHandler
	b := &framework.Backend{
		Help: "",

		PathsSpecial: &logical.Paths{
			Root: MFARootPaths(),
			Unauthenticated: []string{
				"login",
			},
		},
		Paths: MFAPaths(nil, testPathLogin()),
	}
	return b
}

func testPathLogin() *framework.Path {
	return &framework.Path{
		Pattern: `login`,
		Fields: map[string]*framework.FieldSchema{
			"username": &framework.FieldSchema{
				Type: framework.TypeString,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: testPathLoginHandler,
		},
	}
}

func testPathLoginHandler(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	username := d.Get("username").(string)

	return &logical.Response{
		Auth: &logical.Auth{
			Policies: []string{"foo"},
			Metadata: map[string]string{
				"username": username,
			},
		},
	}, nil
}

func testMFAHandler(ctx context.Context, req *logical.Request, d *framework.FieldData, resp *logical.Response) (
	*logical.Response, error) {
	if d.Get("method").(string) != "accept" {
		return logical.ErrorResponse("Deny access"), nil
	} else {
		return resp, nil
	}
}

func TestMFALogin(t *testing.T) {
	b := MakeTestBackend()

	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		LogicalBackend: b,
		Steps: []logicaltest.TestStep{
			testAccStepEnableMFA(t),
			testAccStepLogin(t, "user"),
		},
	})
}

func TestMFALoginDenied(t *testing.T) {
	b := MakeTestBackend()

	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		LogicalBackend: b,
		Steps: []logicaltest.TestStep{
			testAccStepEnableMFA(t),
			testAccStepLoginDenied(t, "user"),
		},
	})
}

func testAccStepEnableMFA(t *testing.T) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "mfa_config",
		Data: map[string]interface{}{
			"type": "test",
		},
	}
}

func testAccStepLogin(t *testing.T, username string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "login",
		Data: map[string]interface{}{
			"method":   "accept",
			"username": username,
		},
		Unauthenticated: true,
		Check:           logicaltest.TestCheckAuth([]string{"foo"}),
	}
}

func testAccStepLoginDenied(t *testing.T, username string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "login",
		Data: map[string]interface{}{
			"method":   "deny",
			"username": username,
		},
		Unauthenticated: true,
		Check:           logicaltest.TestCheckError(),
	}
}
<commit_msg>mfa: fix import path on test file (#9303)<commit_after>package mfa

import (
	"context"
	"testing"

	logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/logical"
)

// MakeTestBackend creates a simple MFA enabled backend.
// Login (before MFA) always succeeds with policy "foo".
// An MFA "test" type is added to mfa.handlers that succeeds
// if MFA method is "accept", otherwise it rejects.
func MakeTestBackend() *framework.Backend {
	handlers["test"] = testMFAHandler
	b := &framework.Backend{
		Help: "",

		PathsSpecial: &logical.Paths{
			Root: MFARootPaths(),
			Unauthenticated: []string{
				"login",
			},
		},
		Paths: MFAPaths(nil, testPathLogin()),
	}
	return b
}

func testPathLogin() *framework.Path {
	return &framework.Path{
		Pattern: `login`,
		Fields: map[string]*framework.FieldSchema{
			"username": &framework.FieldSchema{
				Type: framework.TypeString,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: testPathLoginHandler,
		},
	}
}

func testPathLoginHandler(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	username := d.Get("username").(string)

	return &logical.Response{
		Auth: &logical.Auth{
			Policies: []string{"foo"},
			Metadata: map[string]string{
				"username": username,
			},
		},
	}, nil
}

func testMFAHandler(ctx context.Context, req *logical.Request, d *framework.FieldData, resp *logical.Response) (
	*logical.Response, error) {
	if d.Get("method").(string) != "accept" {
		return logical.ErrorResponse("Deny access"), nil
	} else {
		return resp, nil
	}
}

func TestMFALogin(t *testing.T) {
	b := MakeTestBackend()

	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		LogicalBackend: b,
		Steps: []logicaltest.TestStep{
			testAccStepEnableMFA(t),
			testAccStepLogin(t, "user"),
		},
	})
}

func TestMFALoginDenied(t *testing.T) {
	b := MakeTestBackend()

	logicaltest.Test(t, logicaltest.TestCase{
		AcceptanceTest: true,
		LogicalBackend: b,
		Steps: []logicaltest.TestStep{
			testAccStepEnableMFA(t),
			testAccStepLoginDenied(t, "user"),
		},
	})
}

func testAccStepEnableMFA(t *testing.T) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "mfa_config",
		Data: map[string]interface{}{
			"type": "test",
		},
	}
}

func testAccStepLogin(t *testing.T, username string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "login",
		Data: map[string]interface{}{
			"method":   "accept",
			"username": username,
		},
		Unauthenticated: true,
		Check:           logicaltest.TestCheckAuth([]string{"foo"}),
	}
}

func testAccStepLoginDenied(t *testing.T, username string) logicaltest.TestStep {
	return logicaltest.TestStep{
		Operation: logical.UpdateOperation,
		Path:      "login",
		Data: map[string]interface{}{
			"method":   "deny",
			"username": username,
		},
		Unauthenticated: true,
		Check:           logicaltest.TestCheckError(),
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package rpctest

import (
	"bytes"
	"math"
	"net"
	"os"
	"strconv"
	"testing"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcwallet/waddrmgr"
)

func TestSetUp(t *testing.T) {
	// Create a new test instance.
	nodeTest, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}

	// Initiate setup, generated a chain of length 125.
	if err := nodeTest.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	// Chain length is 125, we should have 26 mature coinbase outputs.
	maxConfs := int32(math.MaxInt32)
	unspentOutputs, err := nodeTest.Wallet.ListUnspent(0, maxConfs, nil)
	if err != nil {
		t.Fatalf("unable to retrieve unspent outputs from the wallet: %v",
			err)
	}
	if len(unspentOutputs) != 26 {
		t.Fatalf("incorrect number of mature coinbases, have %v, should be %v",
			len(unspentOutputs), 26)
	}

	// Current tip should be at height 124.
	nodeInfo, err := nodeTest.Node.GetInfo()
	if err != nil {
		t.Fatalf("unable to execute getinfo on node: %v", err)
	}
	if nodeInfo.Blocks != 125 {
		t.Errorf("Chain height is %v, should be %v",
			nodeInfo.Blocks, 125)
	}

	nodeTest.TearDown()

	// Ensure all test directories have been deleted.
	if _, err := os.Stat(nodeTest.testNodeDir); err == nil {
		t.Errorf("Create test datadir was not deleted.")
	}
}

func TestCoinbaseSpend(t *testing.T) {
	// Create a new test instance.
	nodeTest, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer nodeTest.TearDown()

	// Initiate setup, generated a chain of length 125.
	if err := nodeTest.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	// Grab a fresh address from the wallet.
	addr, err := nodeTest.Wallet.NewAddress(waddrmgr.DefaultAccountNum)
	if err != nil {
		t.Fatalf("unable to get new address: %v", err)
	}

	// Next, send 5 BTC to this address, spending from one of our mature
	// coinbase outputs.
	addrScript, err := txscript.PayToAddrScript(addr)
	if err != nil {
		t.Fatalf("unable to generate pkscript to addr: %v", err)
	}
	output := wire.NewTxOut(5e8, addrScript)
	txid, err := nodeTest.CoinbaseSpend([]*wire.TxOut{output})
	if err != nil {
		t.Fatalf("coinbase spend failed: %v", err)
	}

	// Generate a single block, the transaction the wallet created should
	// be found in this block.
	blockHashes, err := nodeTest.Node.Generate(1)
	if err != nil {
		t.Fatalf("unable to generate single block: %v", err)
	}

	block, err := nodeTest.Node.GetBlock(blockHashes[0])
	if err != nil {
		t.Fatalf("unable to get block: %v", err)
	}

	minedTx := block.Transactions()[1]
	txSha := minedTx.Sha()
	if !bytes.Equal(txid[:], txSha.Bytes()[:]) {
		t.Fatalf("txid's don't match, %v vs %v", txSha, txid)
	}
}

func assertConnectedTo(t *testing.T, nodeA *Harness, nodeB *Harness) {

	nodePort := defaultP2pPort + (2 * nodeB.nodeNum)
	nodeAddr := net.JoinHostPort("127.0.0.1", strconv.Itoa(nodePort))

	nodeAPeers, err := nodeA.Node.GetPeerInfo()
	if err != nil {
		t.Fatalf("unable to get harness1's peer info")
	}

	addrFound := false
	for _, peerInfo := range nodeAPeers {
		if peerInfo.Addr == nodeAddr {
			addrFound = true
			break
		}
	}

	if !addrFound {
		t.Fatal("nodeA not connected to nodeB")
	}
}

func TestConnectNode(t *testing.T) {
	// Create two fresh test harnesses.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness1.TearDown()

	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness2.TearDown()

	// Establish a p2p connection from harness1 to harness2.
	if err := ConnectNode(harness1, harness2); err != nil {
		t.Fatalf("unable to connect harness1 to harness2: %v", err)
	}

	// harness1 should show up in harness2 peer's list, and vice verse.
	assertConnectedTo(t, harness1, harness2)
}

func TestTearDownAll(t *testing.T) {
	// Create two fresh test harnesses.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	// Tear down all currently active harnesses.
	if err := TearDownAll(); err != nil {
		t.Fatalf("unable to teardown all harnesses: %v", err)
	}

	// The global testInstances map should now be fully purged with no
	// active test harnesses remaining.
	if len(ActiveHarnesses()) != 0 {
		t.Fatalf("test harnesses still active after TestTearDownAll(): %v", err)
	}
}

func TestActiveHarnesses(t *testing.T) {
	// Create a single test harness.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer harness1.TearDown()

	// With the harness created above, a single harness should be detected
	// as active.
	numActiveHarnesses := len(ActiveHarnesses())
	if numActiveHarnesses != 1 {
		t.Fatalf("ActiveHarnesses not updated, should have length %v, "+
			"is instead %v", 1, numActiveHarnesses)
	}

	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer harness2.TearDown()

	// With the second harness created above, two harnesses should now be
	// considered active.
	numActiveHarnesses = len(ActiveHarnesses())
	if numActiveHarnesses != 2 {
		t.Fatalf("ActiveHarnesses not updated, should have length %v, "+
			"is instead %v", 2, numActiveHarnesses)
	}
}

func TestJoinMempools(t *testing.T) {
	// Create two test harnesses, starting at the same height.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness1.TearDown()
	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness2.TearDown()

	nodeSlice := []*Harness{harness1, harness2}

	// Both mempools should be considered synced as they are empty.
	// Therefore, this should return instantly.
	if err := JoinNodes(nodeSlice, Mempools); err != nil {
		t.Fatalf("unable to join node on block height: %v", err)
	}

	// Generate a coinbase spend to a new address within harness1's
	// mempool.
	addr, err := harness1.Wallet.NewAddress(waddrmgr.DefaultAccountNum)
	addrScript, err := txscript.PayToAddrScript(addr)
	if err != nil {
		t.Fatalf("unable to generate pkscript to addr: %v", err)
	}
	output := wire.NewTxOut(5e8, addrScript)
	if _, err = harness1.CoinbaseSpend([]*wire.TxOut{output}); err != nil {
		t.Fatalf("coinbase spend failed: %v", err)
	}

	poolsSynced := make(chan struct{})
	go func() {
		if err := JoinNodes(nodeSlice, Mempools); err != nil {
			t.Fatalf("unable to join node on node mempools: %v", err)
		}
		poolsSynced <- struct{}{}
	}()

	// This select case should fall through to the default as the goroutine
	// should be blocked on the JoinNodes calls.
	select {
	case <-poolsSynced:
		t.Fatalf("mempools detected as synced yet harness1 has a new tx")
	default:
	}

	// Establish an outbound connection from harness1 to harness2. After
	// the initial handshake both node should exchange inventory resulting
	// in a synced mempool.
	if err := ConnectNode(harness1, harness2); err != nil {
		t.Fatalf("unable to connect harness1 to harness2: %v", err)
	}

	// Select once again with a special timeout case after 1 minute. The
	// goroutine above should now be blocked on sending into the unbuffered
	// channel. The send should immediately suceed. In order to avoid the
	// test hanging indefinitely, a 1 minute timeout is in place.
	select {
	case <-poolsSynced:
		// fall through
	case <-time.After(time.Minute):
		t.Fatalf("block heights never detected as synced")
	}

}

func TestJoinBlocks(t *testing.T) {
	// Create two test harnesses, with one being 5 block ahead of the other
	// with respect to block height.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 30); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness1.TearDown()
	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness2.TearDown()

	nodeSlice := []*Harness{harness1, harness2}
	blocksSynced := make(chan struct{})
	go func() {
		if err := JoinNodes(nodeSlice, Blocks); err != nil {
			t.Fatalf("unable to join node on block height: %v", err)
		}
		blocksSynced <- struct{}{}
	}()

	// This select case should fall through to the default as the goroutine
	// should be blocked on the JoinNodes calls.
	select {
	case <-blocksSynced:
		t.Fatalf("blocks detected as synced yet harness2 is 5 blocks behind")
	default:
	}

	// Extend harness2's chain by 5 blocks, this should cause JoinNodes to
	// finally unblock and return.
	if _, err := harness2.Node.Generate(5); err != nil {
		t.Fatalf("unable to generate blocks: %v", err)
	}

	// Select once again with a special timeout case after 1 minute. The
	// goroutine above should now be blocked on sending into the unbuffered
	// channel. The send should immediately suceed. In order to avoid the
	// test hanging indefinitely, a 1 minute timeout is in place.
	select {
	case <-blocksSynced:
		// fall through
	case <-time.After(time.Minute):
		t.Fatalf("block heights never detected as synced")
	}
}
<commit_msg>add defer to SetUp test to ensure clean up in the case of failure<commit_after>// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package rpctest

import (
	"bytes"
	"math"
	"net"
	"os"
	"strconv"
	"testing"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcwallet/waddrmgr"
)

func TestSetUp(t *testing.T) {
	// Create a new test instance.
	nodeTest, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer nodeTest.TearDown()

	// Initiate setup, generated a chain of length 125.
	if err := nodeTest.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	// Chain length is 125, we should have 26 mature coinbase outputs.
	maxConfs := int32(math.MaxInt32)
	unspentOutputs, err := nodeTest.Wallet.ListUnspent(0, maxConfs, nil)
	if err != nil {
		t.Fatalf("unable to retrieve unspent outputs from the wallet: %v",
			err)
	}
	if len(unspentOutputs) != 26 {
		t.Fatalf("incorrect number of mature coinbases, have %v, should be %v",
			len(unspentOutputs), 26)
	}

	// Current tip should be at height 124.
	nodeInfo, err := nodeTest.Node.GetInfo()
	if err != nil {
		t.Fatalf("unable to execute getinfo on node: %v", err)
	}
	if nodeInfo.Blocks != 125 {
		t.Errorf("Chain height is %v, should be %v",
			nodeInfo.Blocks, 125)
	}

	nodeTest.TearDown()

	// Ensure all test directories have been deleted.
	if _, err := os.Stat(nodeTest.testNodeDir); err == nil {
		t.Errorf("Create test datadir was not deleted.")
	}
}

func TestCoinbaseSpend(t *testing.T) {
	// Create a new test instance.
	nodeTest, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer nodeTest.TearDown()

	// Initiate setup, generated a chain of length 125.
	if err := nodeTest.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	// Grab a fresh address from the wallet.
	addr, err := nodeTest.Wallet.NewAddress(waddrmgr.DefaultAccountNum)
	if err != nil {
		t.Fatalf("unable to get new address: %v", err)
	}

	// Next, send 5 BTC to this address, spending from one of our mature
	// coinbase outputs.
	addrScript, err := txscript.PayToAddrScript(addr)
	if err != nil {
		t.Fatalf("unable to generate pkscript to addr: %v", err)
	}
	output := wire.NewTxOut(5e8, addrScript)
	txid, err := nodeTest.CoinbaseSpend([]*wire.TxOut{output})
	if err != nil {
		t.Fatalf("coinbase spend failed: %v", err)
	}

	// Generate a single block, the transaction the wallet created should
	// be found in this block.
	blockHashes, err := nodeTest.Node.Generate(1)
	if err != nil {
		t.Fatalf("unable to generate single block: %v", err)
	}

	block, err := nodeTest.Node.GetBlock(blockHashes[0])
	if err != nil {
		t.Fatalf("unable to get block: %v", err)
	}

	minedTx := block.Transactions()[1]
	txSha := minedTx.Sha()
	if !bytes.Equal(txid[:], txSha.Bytes()[:]) {
		t.Fatalf("txid's don't match, %v vs %v", txSha, txid)
	}
}

func assertConnectedTo(t *testing.T, nodeA *Harness, nodeB *Harness) {

	nodePort := defaultP2pPort + (2 * nodeB.nodeNum)
	nodeAddr := net.JoinHostPort("127.0.0.1", strconv.Itoa(nodePort))

	nodeAPeers, err := nodeA.Node.GetPeerInfo()
	if err != nil {
		t.Fatalf("unable to get harness1's peer info")
	}

	addrFound := false
	for _, peerInfo := range nodeAPeers {
		if peerInfo.Addr == nodeAddr {
			addrFound = true
			break
		}
	}

	if !addrFound {
		t.Fatal("nodeA not connected to nodeB")
	}
}

func TestConnectNode(t *testing.T) {
	// Create two fresh test harnesses.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness1.TearDown()

	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness2.TearDown()

	// Establish a p2p connection from harness1 to harness2.
	if err := ConnectNode(harness1, harness2); err != nil {
		t.Fatalf("unable to connect harness1 to harness2: %v", err)
	}

	// harness1 should show up in harness2 peer's list, and vice verse.
	assertConnectedTo(t, harness1, harness2)
}

func TestTearDownAll(t *testing.T) {
	// Create two fresh test harnesses.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}

	// Tear down all currently active harnesses.
	if err := TearDownAll(); err != nil {
		t.Fatalf("unable to teardown all harnesses: %v", err)
	}

	// The global testInstances map should now be fully purged with no
	// active test harnesses remaining.
	if len(ActiveHarnesses()) != 0 {
		t.Fatalf("test harnesses still active after TestTearDownAll(): %v", err)
	}
}

func TestActiveHarnesses(t *testing.T) {
	// Create a single test harness.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer harness1.TearDown()

	// With the harness created above, a single harness should be detected
	// as active.
	numActiveHarnesses := len(ActiveHarnesses())
	if numActiveHarnesses != 1 {
		t.Fatalf("ActiveHarnesses not updated, should have length %v, "+
			"is instead %v", 1, numActiveHarnesses)
	}

	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer harness2.TearDown()

	// With the second harness created above, two harnesses should now be
	// considered active.
	numActiveHarnesses = len(ActiveHarnesses())
	if numActiveHarnesses != 2 {
		t.Fatalf("ActiveHarnesses not updated, should have length %v, "+
			"is instead %v", 2, numActiveHarnesses)
	}
}

func TestJoinMempools(t *testing.T) {
	// Create two test harnesses, starting at the same height.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness1.TearDown()
	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness2.TearDown()

	nodeSlice := []*Harness{harness1, harness2}

	// Both mempools should be considered synced as they are empty.
	// Therefore, this should return instantly.
	if err := JoinNodes(nodeSlice, Mempools); err != nil {
		t.Fatalf("unable to join node on block height: %v", err)
	}

	// Generate a coinbase spend to a new address within harness1's
	// mempool.
	addr, err := harness1.Wallet.NewAddress(waddrmgr.DefaultAccountNum)
	addrScript, err := txscript.PayToAddrScript(addr)
	if err != nil {
		t.Fatalf("unable to generate pkscript to addr: %v", err)
	}
	output := wire.NewTxOut(5e8, addrScript)
	if _, err = harness1.CoinbaseSpend([]*wire.TxOut{output}); err != nil {
		t.Fatalf("coinbase spend failed: %v", err)
	}

	poolsSynced := make(chan struct{})
	go func() {
		if err := JoinNodes(nodeSlice, Mempools); err != nil {
			t.Fatalf("unable to join node on node mempools: %v", err)
		}
		poolsSynced <- struct{}{}
	}()

	// This select case should fall through to the default as the goroutine
	// should be blocked on the JoinNodes calls.
	select {
	case <-poolsSynced:
		t.Fatalf("mempools detected as synced yet harness1 has a new tx")
	default:
	}

	// Establish an outbound connection from harness1 to harness2. After
	// the initial handshake both node should exchange inventory resulting
	// in a synced mempool.
	if err := ConnectNode(harness1, harness2); err != nil {
		t.Fatalf("unable to connect harness1 to harness2: %v", err)
	}

	// Select once again with a special timeout case after 1 minute. The
	// goroutine above should now be blocked on sending into the unbuffered
	// channel. The send should immediately suceed. In order to avoid the
	// test hanging indefinitely, a 1 minute timeout is in place.
	select {
	case <-poolsSynced:
		// fall through
	case <-time.After(time.Minute):
		t.Fatalf("block heights never detected as synced")
	}

}

func TestJoinBlocks(t *testing.T) {
	// Create two test harnesses, with one being 5 block ahead of the other
	// with respect to block height.
	harness1, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness1.SetUp(true, 30); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness1.TearDown()
	harness2, err := New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := harness2.SetUp(true, 25); err != nil {
		t.Fatalf("unable to complete rpctest setup: %v", err)
	}
	defer harness2.TearDown()

	nodeSlice := []*Harness{harness1, harness2}
	blocksSynced := make(chan struct{})
	go func() {
		if err := JoinNodes(nodeSlice, Blocks); err != nil {
			t.Fatalf("unable to join node on block height: %v", err)
		}
		blocksSynced <- struct{}{}
	}()

	// This select case should fall through to the default as the goroutine
	// should be blocked on the JoinNodes calls.
	select {
	case <-blocksSynced:
		t.Fatalf("blocks detected as synced yet harness2 is 5 blocks behind")
	default:
	}

	// Extend harness2's chain by 5 blocks, this should cause JoinNodes to
	// finally unblock and return.
	if _, err := harness2.Node.Generate(5); err != nil {
		t.Fatalf("unable to generate blocks: %v", err)
	}

	// Select once again with a special timeout case after 1 minute. The
	// goroutine above should now be blocked on sending into the unbuffered
	// channel. The send should immediately suceed. In order to avoid the
	// test hanging indefinitely, a 1 minute timeout is in place.
	select {
	case <-blocksSynced:
		// fall through
	case <-time.After(time.Minute):
		t.Fatalf("block heights never detected as synced")
	}
}
<|endoftext|>