<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"regexp"

	"github.com/skeswa/gophr/common"
)

const (
	packageRequestRegexIndexUser                    = 1
	packageRequestRegexIndexRepo                    = 2
	packageRequestRegexIndexSemverPrefix            = 3
	packageRequestRegexIndexSemverMajorVersion      = 4
	packageRequestRegexIndexSemverMinorVersion      = 5
	packageRequestRegexIndexSemverPatchVersion      = 6
	packageRequestRegexIndexSemverPrereleaseLabel   = 7
	packageRequestRegexIndexSemverPrereleaseVersion = 8
	packageRequestRegexIndexSemverSuffix            = 9
	packageRequestRegexIndexSubpath                 = 10
)

const (
	errorPackageRequestParsePathDoesntMatch = "The URL of the request was not a package request URL"
	errorPackageRequestParseNoSuchVersion   = "Could not find a version of \"%s/%s%s\" that matches %s"
)

const (
	formKeyGoGet                = "go-get"
	formValueGoGet              = "1"
	contentTypeHTML             = "text/html"
	masterGitRefLabel           = "master"
	gitRefsInfoSubPath          = "/info/refs"
	githubRootTemplate          = "github.com/%s/%s"
	httpLocationHeader          = "Location"
	gitUploadPackSubPath        = "/git-upload-pack"
	httpContentTypeHeader       = "Content-Type"
	packagePageURLTemplate      = "https://%s/#/packages/%s/%s"
	contentTypeGitUploadPack    = "application/x-git-upload-pack-advertisement"
	githubUploadPackURLTemplate = "https://github.com/%s/%s/git-upload-pack"
)

var (
	versionSelectorRegexStr = fmt.Sprintf(
		`([\%c\%c]?)([0-9]+)(?:\.([0-9]+|%c))?(?:\.([0-9]+|%c))?(?:\-([a-zA-Z0-9\-_]+[a-zA-Z0-9])(?:\.([0-9]+|%c))?)?([\%c\%c]?)`,
		common.SemverSelectorTildeChar,
		common.SemverSelectorCaratChar,
		common.SemverSelectorWildcardChar,
		common.SemverSelectorWildcardChar,
		common.SemverSelectorWildcardChar,
		common.SemverSelectorLessThanChar,
		common.SemverSelectorGreaterThanChar,
	)

	packageRequestRegexStr = fmt.Sprintf(
		`^\/([a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\/([a-zA-Z0-9\-_]+)(?:@%s)?((?:\/[a-zA-Z0-9][-.a-zA-Z0-9]*)*)`,
		versionSelectorRegexStr,
	)

	packageRequestRegex = regexp.MustCompile(packageRequestRegexStr)

	goGetTemplate = template.Must(template.New("").Parse(
		`{{$gophrRoot := .GophrRoot}}{{$githubRoot := .GithubRoot}}{{$githubTree := .GithubTree}}<meta name="go-import" content="{{$gophrRoot}} git {{.Protocol}}://{{$gophrRoot}}"><meta name="go-source" content="{{.GophrRoot}} _ https://{{$githubRoot}}/tree/{{$githubTree}}{/dir} https://{{$githubRoot}}/blob/{{$githubTree}}{/dir}/{file}#L{line}">go get {{.GophrPath}}`,
	))
)

// GoGetTemplateDataSource has all the fields necessary to compile the template
// used to respond to go get metadata requests.
type GoGetTemplateDataSource struct {
	Protocol   string
	GophrRoot  string
	GophrPath  string
	GithubRoot string
	GithubTree string
}

// NewGoGetTemplateDataSource creates a new instance of GoGetTemplateDataSource.
func NewGoGetTemplateDataSource(
	user string,
	repo string,
	semverSelectorExists bool,
	semverSelector common.SemverSelector,
	subpath string,
	hasMatchedCandidate bool,
	matchedCandidate common.SemverCandidate,
) GoGetTemplateDataSource {
	var (
		buffer bytes.Buffer

		config     = getConfig()
		templateDS = GoGetTemplateDataSource{}
	)

	if config.dev {
		templateDS.Protocol = "http"
	} else {
		templateDS.Protocol = "https"
	}

	buffer.WriteString(config.domain)
	buffer.WriteByte('/')
	buffer.WriteString(user)
	buffer.WriteByte('/')
	buffer.WriteString(repo)
	if semverSelectorExists {
		buffer.WriteByte('@')
		buffer.WriteString(semverSelector.String())
	}
	templateDS.GophrRoot = buffer.String()

	buffer.WriteString(subpath)
	templateDS.GophrPath = buffer.String()

	buffer.Reset()
	buffer.WriteString("github.com")
	buffer.WriteByte('/')
	buffer.WriteString(user)
	buffer.WriteByte('/')
	buffer.WriteString(repo)
	templateDS.GithubRoot = buffer.String()

	if hasMatchedCandidate {
		templateDS.GithubTree = matchedCandidate.GitRefLabel
	} else {
		templateDS.GithubTree = masterGitRefLabel
	}

	return templateDS
}

// RespondToPackageRequest processes an incoming request, evaluates whether is a
// correctly formatted request for package-related data, and either responds
// appropriately or returns an error indicating what went wrong.
func RespondToPackageRequest(
	requestID string,
	req *http.Request,
	res http.ResponseWriter,
) error {
	matches := packageRequestRegex.FindStringSubmatch(req.URL.Path)
	if matches == nil {
		log.Printf("[%s] Failed to process request as a package request because the URL format didn't match the regular expression\n", requestID)
		return errors.New(errorPackageRequestParsePathDoesntMatch)
	}

	var (
		packageRepo          = matches[packageRequestRegexIndexRepo]
		packageCreator       = matches[packageRequestRegexIndexUser]
		packageSubpath       = matches[packageRequestRegexIndexSubpath]
		requesterIsGoGet     = req.FormValue(formKeyGoGet) == formValueGoGet
		hasMatchedCandidate  = false
		semverSelectorExists = false

		semverSelector   common.SemverSelector
		packageRefsData  []byte
		matchedCandidate common.SemverCandidate
	)

	if len(matches[packageRequestRegexIndexSemverMajorVersion]) > 0 {
		selector, err := common.NewSemverSelector(
			matches[packageRequestRegexIndexSemverPrefix],
			matches[packageRequestRegexIndexSemverMajorVersion],
			matches[packageRequestRegexIndexSemverMinorVersion],
			matches[packageRequestRegexIndexSemverPatchVersion],
			matches[packageRequestRegexIndexSemverPrereleaseLabel],
			matches[packageRequestRegexIndexSemverPrereleaseVersion],
			matches[packageRequestRegexIndexSemverSuffix],
		)

		if err != nil {
			return err
		}

		semverSelector = selector
		semverSelectorExists = true

		log.Printf("[%s] Found a version selector in the request URL\n", requestID)
	}

	// Only go out to fetch refs if they're going to get used
	if requesterIsGoGet || packageSubpath == gitRefsInfoSubPath {
		log.Printf("[%s] Fetching Github refs since this request is either from a go get or has an info path\n", requestID)

		refs, err := common.FetchRefs(fmt.Sprintf(
			githubRootTemplate,
			packageCreator,
			packageRepo,
		))

		if err != nil {
			log.Printf("[%s] Github ref fetch failed\n", requestID)
			return err
		}

		if semverSelectorExists &&
			refs.Candidates != nil &&
			len(refs.Candidates) > 0 {
			// Get the list of candidates that match the selector
			matchedCandidates := refs.Candidates.Match(semverSelector)
			log.Printf("[%s] Matched candidates to the version selector\n", requestID)
			// Only proceed if there is at least one matched candidate
			if matchedCandidates != nil && len(matchedCandidates) > 0 {
				if len(matchedCandidates) == 1 {
					matchedCandidate = matchedCandidates[0]
					hasMatchedCandidate = true
				} else {
					selectorHasLessThan :=
						semverSelector.Suffix == common.SemverSelectorSuffixLessThan
					selectorHasWildcards :=
						semverSelector.MinorVersion.Type == common.SemverSegmentTypeWildcard ||
							semverSelector.PatchVersion.Type == common.SemverSegmentTypeWildcard ||
							semverSelector.PrereleaseVersion.Type == common.SemverSegmentTypeWildcard

					var matchedCandidateReference *common.SemverCandidate
					if selectorHasWildcards || selectorHasLessThan {
						matchedCandidateReference = matchedCandidates.Highest()
					} else {
						matchedCandidateReference = matchedCandidates.Lowest()
					}

					matchedCandidate = *matchedCandidateReference
					hasMatchedCandidate = true
				}

				log.Printf("[%s] There was at least one candidate matched to the version selector\n", requestID)
			} else {
				log.Printf("[%s] Couldn't find any candidates to match to the version selector\n", requestID)

				return fmt.Errorf(
					errorPackageRequestParseNoSuchVersion,
					packageCreator,
					packageRepo,
					packageSubpath,
					semverSelector.String(),
				)
			}
		}

		if hasMatchedCandidate {
			log.Printf("[%s] Tweaked the refs to focus on the matched candidate\n", requestID)
			refsData, err := refs.Reserialize(matchedCandidate)
			if err != nil {
				return err
			}
			packageRefsData = refsData
		} else {
			// If there was no matched candidate, and we're fine with it, then return
			// the original refs that we downloaded from github
			packageRefsData = refs.Data
		}
	}

	switch packageSubpath {
	case gitUploadPackSubPath:
		log.Printf("[%s] Responding with the Github upload pack permanent redirect\n", requestID)

		res.Header().Set(
			httpLocationHeader,
			fmt.Sprintf(githubUploadPackURLTemplate, packageCreator, packageRepo),
		)
		res.WriteHeader(http.StatusMovedPermanently)
	case gitRefsInfoSubPath:
		log.Printf("[%s] Responding with the git refs pulled from Github\n", requestID)

		res.Header().Set(httpContentTypeHeader, contentTypeGitUploadPack)
		res.Write(packageRefsData)
	default:
		if requesterIsGoGet {
			log.Printf("[%s] Responding with html formatted for go get\n", requestID)

			res.Header().Set(httpContentTypeHeader, contentTypeHTML)
			err := goGetTemplate.Execute(res, NewGoGetTemplateDataSource(
				packageCreator,
				packageRepo,
				semverSelectorExists,
				semverSelector,
				packageSubpath,
				hasMatchedCandidate,
				matchedCandidate,
			))

			if err != nil {
				log.Printf("[%s] Failed to respond with html formatted for go get\n", requestID)
				return err
			}
		} else {
			log.Printf("[%s] Responding with a permanent redirect to the gophr package webpage\n", requestID)

			res.Header().Set(
				httpLocationHeader,
				fmt.Sprintf(
					packagePageURLTemplate,
					getConfig().domain,
					packageCreator,
					packageRepo,
				),
			)
			res.WriteHeader(http.StatusMovedPermanently)
		}
	}

	return nil
}
<commit_msg>Fixed issue #42<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"regexp"

	"github.com/skeswa/gophr/common"
)

const (
	packageRequestRegexIndexUser                    = 1
	packageRequestRegexIndexRepo                    = 2
	packageRequestRegexIndexSemverPrefix            = 3
	packageRequestRegexIndexSemverMajorVersion      = 4
	packageRequestRegexIndexSemverMinorVersion      = 5
	packageRequestRegexIndexSemverPatchVersion      = 6
	packageRequestRegexIndexSemverPrereleaseLabel   = 7
	packageRequestRegexIndexSemverPrereleaseVersion = 8
	packageRequestRegexIndexSemverSuffix            = 9
	packageRequestRegexIndexSubpath                 = 10
)

const (
	errorPackageRequestParsePathDoesntMatch = "The URL of the request was not a package request URL"
	errorPackageRequestParseNoSuchVersion   = "Could not find a version of \"%s/%s%s\" that matches %s"
)

const (
	formKeyGoGet                = "go-get"
	formValueGoGet              = "1"
	contentTypeHTML             = "text/html"
	masterGitRefLabel           = "master"
	gitRefsInfoSubPath          = "/info/refs"
	githubRootTemplate          = "github.com/%s/%s"
	httpLocationHeader          = "Location"
	gitUploadPackSubPath        = "/git-upload-pack"
	httpContentTypeHeader       = "Content-Type"
	packagePageURLTemplate      = "https://%s/#/packages/%s/%s"
	contentTypeGitUploadPack    = "application/x-git-upload-pack-advertisement"
	githubUploadPackURLTemplate = "https://github.com/%s/%s/git-upload-pack"
)

var (
	versionSelectorRegexStr = fmt.Sprintf(
		`([\%c\%c]?)([0-9]+)(?:\.([0-9]+|%c))?(?:\.([0-9]+|%c))?(?:\-([a-zA-Z0-9\-_]+[a-zA-Z0-9])(?:\.([0-9]+|%c))?)?([\%c\%c]?)`,
		common.SemverSelectorTildeChar,
		common.SemverSelectorCaratChar,
		common.SemverSelectorWildcardChar,
		common.SemverSelectorWildcardChar,
		common.SemverSelectorWildcardChar,
		common.SemverSelectorLessThanChar,
		common.SemverSelectorGreaterThanChar,
	)

	packageRequestRegexStr = fmt.Sprintf(
		`^\/([a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\/([a-zA-Z0-9\-_]+)(?:@%s)?((?:\/[a-zA-Z0-9][-.a-zA-Z0-9]*)*)`,
		versionSelectorRegexStr,
	)

	packageRequestRegex = regexp.MustCompile(packageRequestRegexStr)

	goGetTemplate = template.Must(template.New("").Parse(
		`{{$gophrRoot := .GophrRoot}}{{$githubRoot := .GithubRoot}}{{$githubTree := .GithubTree}}<meta name="go-import" content="{{$gophrRoot}} git {{.Protocol}}://{{$gophrRoot}}"><meta name="go-source" content="{{.GophrRoot}} _ https://{{$githubRoot}}/tree/{{$githubTree}}{/dir} https://{{$githubRoot}}/blob/{{$githubTree}}{/dir}/{file}#L{line}">go get {{.GophrPath}}`,
	))
)

// GoGetTemplateDataSource has all the fields necessary to compile the template
// used to respond to go get metadata requests.
type GoGetTemplateDataSource struct {
	Protocol   string
	GophrRoot  string
	GophrPath  string
	GithubRoot string
	GithubTree string
}

// NewGoGetTemplateDataSource creates a new instance of GoGetTemplateDataSource.
func NewGoGetTemplateDataSource(
	user string,
	repo string,
	semverSelectorExists bool,
	semverSelector common.SemverSelector,
	subpath string,
	hasMatchedCandidate bool,
	matchedCandidate common.SemverCandidate,
) GoGetTemplateDataSource {
	var (
		buffer bytes.Buffer

		config     = getConfig()
		templateDS = GoGetTemplateDataSource{}
	)

	if config.dev {
		templateDS.Protocol = "http"
	} else {
		templateDS.Protocol = "https"
	}

	buffer.WriteString(config.domain)
	buffer.WriteByte('/')
	buffer.WriteString(user)
	buffer.WriteByte('/')
	buffer.WriteString(repo)
	if semverSelectorExists {
		buffer.WriteByte('@')
		buffer.WriteString(semverSelector.String())
	}
	templateDS.GophrRoot = buffer.String()

	buffer.WriteString(subpath)
	templateDS.GophrPath = buffer.String()

	buffer.Reset()
	buffer.WriteString("github.com")
	buffer.WriteByte('/')
	buffer.WriteString(user)
	buffer.WriteByte('/')
	buffer.WriteString(repo)
	templateDS.GithubRoot = buffer.String()

	if hasMatchedCandidate {
		templateDS.GithubTree = matchedCandidate.GitRefLabel
	} else {
		templateDS.GithubTree = masterGitRefLabel
	}

	return templateDS
}

// RespondToPackageRequest processes an incoming request, evaluates whether is a
// correctly formatted request for package-related data, and either responds
// appropriately or returns an error indicating what went wrong.
func RespondToPackageRequest(
	requestID string,
	req *http.Request,
	res http.ResponseWriter,
) error {
	matches := packageRequestRegex.FindStringSubmatch(req.URL.Path)
	if matches == nil {
		log.Printf("[%s] Failed to process request as a package request because the URL format didn't match the regular expression\n", requestID)
		return errors.New(errorPackageRequestParsePathDoesntMatch)
	}

	var (
		packageRepo          = matches[packageRequestRegexIndexRepo]
		packageCreator       = matches[packageRequestRegexIndexUser]
		packageSubpath       = matches[packageRequestRegexIndexSubpath]
		requesterIsGoGet     = req.FormValue(formKeyGoGet) == formValueGoGet
		hasMatchedCandidate  = false
		semverSelectorExists = false

		semverSelector   common.SemverSelector
		packageRefsData  []byte
		matchedCandidate common.SemverCandidate
	)

	if len(matches[packageRequestRegexIndexSemverMajorVersion]) > 0 {
		selector, err := common.NewSemverSelector(
			matches[packageRequestRegexIndexSemverPrefix],
			matches[packageRequestRegexIndexSemverMajorVersion],
			matches[packageRequestRegexIndexSemverMinorVersion],
			matches[packageRequestRegexIndexSemverPatchVersion],
			matches[packageRequestRegexIndexSemverPrereleaseLabel],
			matches[packageRequestRegexIndexSemverPrereleaseVersion],
			matches[packageRequestRegexIndexSemverSuffix],
		)

		if err != nil {
			return err
		}

		semverSelector = selector
		semverSelectorExists = true

		log.Printf("[%s] Found a version selector in the request URL\n", requestID)
	}

	// Only go out to fetch refs if they're going to get used
	if requesterIsGoGet || packageSubpath == gitRefsInfoSubPath {
		log.Printf("[%s] Fetching Github refs since this request is either from a go get or has an info path\n", requestID)

		refs, err := common.FetchRefs(fmt.Sprintf(
			githubRootTemplate,
			packageCreator,
			packageRepo,
		))

		if err != nil {
			log.Printf("[%s] Github ref fetch failed\n", requestID)
			return err
		}

		if semverSelectorExists &&
			refs.Candidates != nil &&
			len(refs.Candidates) > 0 {
			// Get the list of candidates that match the selector
			matchedCandidates := refs.Candidates.Match(semverSelector)
			log.Printf("[%s] Matched candidates to the version selector\n", requestID)
			// Only proceed if there is at least one matched candidate
			if matchedCandidates != nil && len(matchedCandidates) > 0 {
				if len(matchedCandidates) == 1 {
					matchedCandidate = matchedCandidates[0]
					hasMatchedCandidate = true
				} else {
					selectorHasLessThan :=
						semverSelector.Suffix == common.SemverSelectorSuffixLessThan
					selectorHasWildcards :=
						semverSelector.MinorVersion.Type == common.SemverSegmentTypeWildcard ||
							semverSelector.PatchVersion.Type == common.SemverSegmentTypeWildcard ||
							semverSelector.PrereleaseVersion.Type == common.SemverSegmentTypeWildcard

					var matchedCandidateReference *common.SemverCandidate
					if selectorHasWildcards || selectorHasLessThan {
						matchedCandidateReference = matchedCandidates.Highest()
					} else {
						matchedCandidateReference = matchedCandidates.Lowest()
					}

					matchedCandidate = *matchedCandidateReference
					hasMatchedCandidate = true
				}

				log.Printf("[%s] There was at least one candidate matched to the version selector\n", requestID)
			}
		}

		if hasMatchedCandidate {
			log.Printf("[%s] Tweaked the refs to focus on the matched candidate\n", requestID)
			refsData, err := refs.Reserialize(matchedCandidate)
			if err != nil {
				return err
			}
			packageRefsData = refsData
		} else {
			if !semverSelectorExists {
				// Since there was no selector, we are fine with the fact that we didn't
				// find a match. Now, return the original refs that we downloaded from
				// github that point to master by default.
				packageRefsData = refs.Data
			} else {
				log.Printf("[%s] Couldn't find any candidates to match to the version selector \"%s\"\n", requestID, semverSelector.String())

				return fmt.Errorf(
					errorPackageRequestParseNoSuchVersion,
					packageCreator,
					packageRepo,
					packageSubpath,
					semverSelector.String(),
				)
			}
		}
	}

	switch packageSubpath {
	case gitUploadPackSubPath:
		log.Printf("[%s] Responding with the Github upload pack permanent redirect\n", requestID)

		res.Header().Set(
			httpLocationHeader,
			fmt.Sprintf(githubUploadPackURLTemplate, packageCreator, packageRepo),
		)
		res.WriteHeader(http.StatusMovedPermanently)
	case gitRefsInfoSubPath:
		log.Printf("[%s] Responding with the git refs pulled from Github\n", requestID)

		res.Header().Set(httpContentTypeHeader, contentTypeGitUploadPack)
		res.Write(packageRefsData)
	default:
		if requesterIsGoGet {
			log.Printf("[%s] Responding with html formatted for go get\n", requestID)

			res.Header().Set(httpContentTypeHeader, contentTypeHTML)
			err := goGetTemplate.Execute(res, NewGoGetTemplateDataSource(
				packageCreator,
				packageRepo,
				semverSelectorExists,
				semverSelector,
				packageSubpath,
				hasMatchedCandidate,
				matchedCandidate,
			))

			if err != nil {
				log.Printf("[%s] Failed to respond with html formatted for go get\n", requestID)
				return err
			}
		} else {
			log.Printf("[%s] Responding with a permanent redirect to the gophr package webpage\n", requestID)

			res.Header().Set(
				httpLocationHeader,
				fmt.Sprintf(
					packagePageURLTemplate,
					getConfig().domain,
					packageCreator,
					packageRepo,
				),
			)
			res.WriteHeader(http.StatusMovedPermanently)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"github.com/google/go-cmp/cmp"
)

func TestIssuesService_ListMilestones(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{
			"state":     "closed",
			"sort":      "due_date",
			"direction": "asc",
			"page":      "2",
		})
		fmt.Fprint(w, `[{"number":1}]`)
	})

	opt := &MilestoneListOptions{"closed", "due_date", "asc", ListOptions{Page: 2}}
	ctx := context.Background()
	milestones, _, err := client.Issues.ListMilestones(ctx, "o", "r", opt)
	if err != nil {
		t.Errorf("IssuesService.ListMilestones returned error: %v", err)
	}

	want := []*Milestone{{Number: Int(1)}}
	if !cmp.Equal(milestones, want) {
		t.Errorf("IssuesService.ListMilestones returned %+v, want %+v", milestones, want)
	}

	const methodName = "ListMilestones"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.ListMilestones(ctx, "\n", "\n", opt)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.ListMilestones(ctx, "o", "r", opt)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.ListMilestones(ctx, "%", "r", nil)
	testURLParseError(t, err)
}

func TestIssuesService_GetMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"number":1}`)
	})

	ctx := context.Background()
	milestone, _, err := client.Issues.GetMilestone(ctx, "o", "r", 1)
	if err != nil {
		t.Errorf("IssuesService.GetMilestone returned error: %v", err)
	}

	want := &Milestone{Number: Int(1)}
	if !cmp.Equal(milestone, want) {
		t.Errorf("IssuesService.GetMilestone returned %+v, want %+v", milestone, want)
	}

	const methodName = "GetMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.GetMilestone(ctx, "\n", "\n", -1)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.GetMilestone(ctx, "o", "r", 1)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.GetMilestone(ctx, "%", "r", 1)
	testURLParseError(t, err)
}

func TestIssuesService_CreateMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	input := &Milestone{Title: String("t")}

	mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) {
		v := new(Milestone)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "POST")
		if !cmp.Equal(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"number":1}`)
	})

	ctx := context.Background()
	milestone, _, err := client.Issues.CreateMilestone(ctx, "o", "r", input)
	if err != nil {
		t.Errorf("IssuesService.CreateMilestone returned error: %v", err)
	}

	want := &Milestone{Number: Int(1)}
	if !cmp.Equal(milestone, want) {
		t.Errorf("IssuesService.CreateMilestone returned %+v, want %+v", milestone, want)
	}

	const methodName = "CreateMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.CreateMilestone(ctx, "\n", "\n", input)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.CreateMilestone(ctx, "o", "r", input)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.CreateMilestone(ctx, "%", "r", nil)
	testURLParseError(t, err)
}

func TestIssuesService_EditMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	input := &Milestone{Title: String("t")}

	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
		v := new(Milestone)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PATCH")
		if !cmp.Equal(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"number":1}`)
	})

	ctx := context.Background()
	milestone, _, err := client.Issues.EditMilestone(ctx, "o", "r", 1, input)
	if err != nil {
		t.Errorf("IssuesService.EditMilestone returned error: %v", err)
	}

	want := &Milestone{Number: Int(1)}
	if !cmp.Equal(milestone, want) {
		t.Errorf("IssuesService.EditMilestone returned %+v, want %+v", milestone, want)
	}

	const methodName = "EditMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.EditMilestone(ctx, "\n", "\n", -1, input)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.EditMilestone(ctx, "o", "r", 1, input)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.EditMilestone(ctx, "%", "r", 1, nil)
	testURLParseError(t, err)
}

func TestIssuesService_DeleteMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	ctx := context.Background()
	_, err := client.Issues.DeleteMilestone(ctx, "o", "r", 1)
	if err != nil {
		t.Errorf("IssuesService.DeleteMilestone returned error: %v", err)
	}

	const methodName = "DeleteMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, err = client.Issues.DeleteMilestone(ctx, "\n", "\n", -1)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		return client.Issues.DeleteMilestone(ctx, "o", "r", 1)
	})
}

func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, err := client.Issues.DeleteMilestone(ctx, "%", "r", 1)
	testURLParseError(t, err)
}
<commit_msg>Add test cases for JSON resource marshaling (#1981)<commit_after>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"github.com/google/go-cmp/cmp"
)

func TestIssuesService_ListMilestones(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{
			"state":     "closed",
			"sort":      "due_date",
			"direction": "asc",
			"page":      "2",
		})
		fmt.Fprint(w, `[{"number":1}]`)
	})

	opt := &MilestoneListOptions{"closed", "due_date", "asc", ListOptions{Page: 2}}
	ctx := context.Background()
	milestones, _, err := client.Issues.ListMilestones(ctx, "o", "r", opt)
	if err != nil {
		t.Errorf("IssuesService.ListMilestones returned error: %v", err)
	}

	want := []*Milestone{{Number: Int(1)}}
	if !cmp.Equal(milestones, want) {
		t.Errorf("IssuesService.ListMilestones returned %+v, want %+v", milestones, want)
	}

	const methodName = "ListMilestones"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.ListMilestones(ctx, "\n", "\n", opt)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.ListMilestones(ctx, "o", "r", opt)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.ListMilestones(ctx, "%", "r", nil)
	testURLParseError(t, err)
}

func TestIssuesService_GetMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"number":1}`)
	})

	ctx := context.Background()
	milestone, _, err := client.Issues.GetMilestone(ctx, "o", "r", 1)
	if err != nil {
		t.Errorf("IssuesService.GetMilestone returned error: %v", err)
	}

	want := &Milestone{Number: Int(1)}
	if !cmp.Equal(milestone, want) {
		t.Errorf("IssuesService.GetMilestone returned %+v, want %+v", milestone, want)
	}

	const methodName = "GetMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.GetMilestone(ctx, "\n", "\n", -1)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.GetMilestone(ctx, "o", "r", 1)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.GetMilestone(ctx, "%", "r", 1)
	testURLParseError(t, err)
}

func TestIssuesService_CreateMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	input := &Milestone{Title: String("t")}

	mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) {
		v := new(Milestone)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "POST")
		if !cmp.Equal(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"number":1}`)
	})

	ctx := context.Background()
	milestone, _, err := client.Issues.CreateMilestone(ctx, "o", "r", input)
	if err != nil {
		t.Errorf("IssuesService.CreateMilestone returned error: %v", err)
	}

	want := &Milestone{Number: Int(1)}
	if !cmp.Equal(milestone, want) {
		t.Errorf("IssuesService.CreateMilestone returned %+v, want %+v", milestone, want)
	}

	const methodName = "CreateMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.CreateMilestone(ctx, "\n", "\n", input)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.CreateMilestone(ctx, "o", "r", input)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.CreateMilestone(ctx, "%", "r", nil)
	testURLParseError(t, err)
}

func TestIssuesService_EditMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	input := &Milestone{Title: String("t")}

	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
		v := new(Milestone)
		json.NewDecoder(r.Body).Decode(v)

		testMethod(t, r, "PATCH")
		if !cmp.Equal(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		fmt.Fprint(w, `{"number":1}`)
	})

	ctx := context.Background()
	milestone, _, err := client.Issues.EditMilestone(ctx, "o", "r", 1, input)
	if err != nil {
		t.Errorf("IssuesService.EditMilestone returned error: %v", err)
	}

	want := &Milestone{Number: Int(1)}
	if !cmp.Equal(milestone, want) {
		t.Errorf("IssuesService.EditMilestone returned %+v, want %+v", milestone, want)
	}

	const methodName = "EditMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Issues.EditMilestone(ctx, "\n", "\n", -1, input)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Issues.EditMilestone(ctx, "o", "r", 1, input)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Issues.EditMilestone(ctx, "%", "r", 1, nil)
	testURLParseError(t, err)
}

func TestIssuesService_DeleteMilestone(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	ctx := context.Background()
	_, err := client.Issues.DeleteMilestone(ctx, "o", "r", 1)
	if err != nil {
		t.Errorf("IssuesService.DeleteMilestone returned error: %v", err)
	}

	const methodName = "DeleteMilestone"
	testBadOptions(t, methodName, func() (err error) {
		_, err = client.Issues.DeleteMilestone(ctx, "\n", "\n", -1)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		return client.Issues.DeleteMilestone(ctx, "o", "r", 1)
	})
}

func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, err := client.Issues.DeleteMilestone(ctx, "%", "r", 1)
	testURLParseError(t, err)
}

func TestMilestone_Marshal(t *testing.T) {
	testJSONMarshal(t, &Milestone{}, "{}")

	u := &Milestone{
		URL:         String("url"),
		HTMLURL:     String("hurl"),
		LabelsURL:   String("lurl"),
		ID:          Int64(1),
		Number:      Int(1),
		State:       String("state"),
		Title:       String("title"),
		Description: String("desc"),
		Creator: &User{
			Login:           String("l"),
			ID:              Int64(1),
			URL:             String("u"),
			AvatarURL:       String("a"),
			GravatarID:      String("g"),
			Name:            String("n"),
			Company:         String("c"),
			Blog:            String("b"),
			Location:        String("l"),
			Email:           String("e"),
			Hireable:        Bool(true),
			Bio:             String("b"),
			TwitterUsername: String("tu"),
			PublicRepos:     Int(1),
			Followers:       Int(1),
			Following:       Int(1),
			CreatedAt:       &Timestamp{referenceTime},
			SuspendedAt:     &Timestamp{referenceTime},
		},
		OpenIssues:   Int(1),
		ClosedIssues: Int(1),
		CreatedAt:    &referenceTime,
		UpdatedAt:    &referenceTime,
		ClosedAt:     &referenceTime,
		DueOn:        &referenceTime,
		NodeID:       String("nid"),
	}

	want := `{
		"url": "url",
		"html_url": "hurl",
		"labels_url": "lurl",
		"id": 1,
		"number": 1,
		"state": "state",
		"title": "title",
		"description": "desc",
		"creator": {
			"login": "l",
			"id": 1,
			"avatar_url": "a",
			"gravatar_id": "g",
			"name": "n",
			"company": "c",
			"blog": "b",
			"location": "l",
			"email": "e",
			"hireable": true,
			"bio": "b",
			"twitter_username": "tu",
			"public_repos": 1,
			"followers": 1,
			"following": 1,
			"created_at": ` + referenceTimeStr + `,
			"suspended_at": ` + referenceTimeStr + `,
			"url": "u"
		},
		"open_issues": 1,
		"closed_issues": 1,
		"created_at": ` + referenceTimeStr + `,
		"updated_at": ` + referenceTimeStr + `,
		"closed_at": ` + referenceTimeStr + `,
		"due_on": ` + referenceTimeStr + `,
		"node_id": "nid"
	}`

	testJSONMarshal(t, u, want)
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-toggl AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package toggl

import (
	"time"
)

// UsersService handles communication with the user related
// methods of the Toggl API.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/users.md
type UsersService struct {
	client *Client
}

// UserResponse acts as a response wrapper where response returns
// in format of "data": User's object.
type UserResponse struct {
	Since int   `json:"since,omitempty"`
	Data  *User `json:"data,omitempty"`
}

// User represents Toggl's user.
type User struct {
	// User's ID
	ID int `json:"id,omitempty"`

	// User's fullname
	Fullname string `json:"fullname,omitempty"`

	// User API token in https://www.toggl.com/user/edit
	APIToken string `json:"api_token,omitempty"`

	// Default workspace ID
	DefautWID int `json:"default_wid,omitempty"`

	// User's email
	Email string `json:"email,omitempty"`

	// Time format like "h:mm A"
	TimeOfDayFormat string `json:"timeofday_format,omitempty"`

	// Date format like "MM/DD/YYYY"
	DateFormat string `json:"date_format,omitempty"`

	// Whether start and stop time are saved on time entry
	StoreStartAndStopTime bool `json:"store_start_and_stop_time,omitempty"`

	// Sunday=0
	BeginningOfWeek int `json:"beginning_of_week,omitempty"`

	// User's language such as "en_US"
	Language string `json:"language,omitempty"`

	// URL with the user's profile picture
	ImageURL string `json:"image_url,omitempty"`

	// Should a piechart be shown on the sidebar
	SidebarPiechart bool `json:"sidebar_piechart,omitempty"`

	// Timestamp of last changes, e.g. "2013-03-06T12:18:42+00:00"
	At *time.Time `json:"at,omitempty"`

	// TODO
	// time_entries []
	// projects []
	// tags []
	// workspaces []
	// clients []
}

// UserSignup represents posted data to be sent to Signup endpoint.
type UserSignup struct {
	User *UserCredential `json:"user,omitempty"`
}

// UserCredential represents user credential to signup a new user.
type UserCredential struct {
	Email    string `json:"email,omitempty"`
	Password string `json:"password,omitempty"`
}

// Me returns current user data. If related data is set to true,
// a more complete response will be returned.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/users.md#get-current-user-data
func (s *UsersService) Me(withRelatedData bool) (*User, error) {
	u := "me"
	if withRelatedData {
		u += "?with_related_data=true"
	}
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	data := new(UserResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Signup new user.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/users.md#sign-up-new-user
func (s *UsersService) Signup(uc *UserCredential) (*User, error) {
	u := "signups"
	us := &UserSignup{uc}
	req, err := s.client.NewRequest("POST", u, us)
	if err != nil {
		return nil, err
	}

	data := new(UserResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}
<commit_msg>Enable related fields for users.<commit_after>// Copyright 2013 The go-toggl AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package toggl

import (
	"time"
)

// UsersService handles communication with the user related
// methods of the Toggl API.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/users.md
type UsersService struct {
	client *Client
}

// UserResponse acts as a response wrapper where response returns
// in format of "data": User's object.
type UserResponse struct {
	Since int   `json:"since,omitempty"`
	Data  *User `json:"data,omitempty"`
}

// User represents Toggl's user.
type User struct {
	// User's ID
	ID int `json:"id,omitempty"`

	// User's fullname
	Fullname string `json:"fullname,omitempty"`

	// User API token in https://www.toggl.com/user/edit
	APIToken string `json:"api_token,omitempty"`

	// Default workspace ID
	DefautWID int `json:"default_wid,omitempty"`

	// User's email
	Email string `json:"email,omitempty"`

	// Time format like "h:mm A"
	TimeOfDayFormat string `json:"timeofday_format,omitempty"`

	// Date format like "MM/DD/YYYY"
	DateFormat string `json:"date_format,omitempty"`

	// Whether start and stop time are saved on time entry
	StoreStartAndStopTime bool `json:"store_start_and_stop_time,omitempty"`

	// Sunday=0
	BeginningOfWeek int `json:"beginning_of_week,omitempty"`

	// User's language such as "en_US"
	Language string `json:"language,omitempty"`

	// URL with the user's profile picture
	ImageURL string `json:"image_url,omitempty"`

	// Should a piechart be shown on the sidebar
	SidebarPiechart bool `json:"sidebar_piechart,omitempty"`

	// Timestamp of last changes, e.g. "2013-03-06T12:18:42+00:00"
	At *time.Time `json:"at,omitempty"`

	TimeEntries []TimeEntry `json:"time_entries,omitempty"`
	Projects    []Project   `json:"projects,omitempty"`
	Tags        []Tag       `json:"tags,omitempty"`
	// workspaces []
	// clients []
}

// UserSignup represents posted data to be sent to Signup endpoint.
type UserSignup struct {
	User *UserCredential `json:"user,omitempty"`
}

// UserCredential represents user credential to signup a new user.
type UserCredential struct {
	Email    string `json:"email,omitempty"`
	Password string `json:"password,omitempty"`
}

// Me returns current user data. If related data is set to true,
// a more complete response will be returned.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/users.md#get-current-user-data
func (s *UsersService) Me(withRelatedData bool) (*User, error) {
	u := "me"
	if withRelatedData {
		u += "?with_related_data=true"
	}
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	data := new(UserResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Signup new user.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/users.md#sign-up-new-user
func (s *UsersService) Signup(uc *UserCredential) (*User, error) {
	u := "signups"
	us := &UserSignup{uc}
	req, err := s.client.NewRequest("POST", u, us)
	if err != nil {
		return nil, err
	}

	data := new(UserResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}
<|endoftext|>


<commit_before>package oauth2

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/hooklift/oauth2/providers/test"
	"github.com/hooklift/oauth2/types"
)

func AuthzGrantTokenRequestTest(t *testing.T, grantType, authzCode string) *http.Request {
	// http://tools.ietf.org/html/rfc6749#section-4.1.3
	queryStr := url.Values{
		"grant_type":   {grantType},
		"code":         {authzCode},
		"redirect_uri": {"https://example.com/oauth2/callback"},
		"client_id":    {"test_client_id"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	return req
}

// TestAccessToken tests a happy path for getting access tokens in accordance with
// http://tools.ietf.org/html/rfc6749#section-4.1.3 and
// http://tools.ietf.org/html/rfc6749#section-4.1.4
func TestAuthzGrantTokenRequest(t *testing.T) {
	provider, authzCode := getTestAuthzCode(t)

	req := AuthzGrantTokenRequestTest(t, "authorization_code", authzCode)
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	// http://tools.ietf.org/html/rfc6749#section-4.1.4
	accessToken := types.Token{}
	err := json.Unmarshal(w.Body.Bytes(), &accessToken)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", accessToken.Type)
	equals(t, "600", accessToken.ExpiresIn)
}

// TestClientAuthRequired tests that client is required to always authenticate in order
// to request access tokens.
func TestAuthzGrantClientAuthRequired(t *testing.T) {
	provider, authzCode := getTestAuthzCode(t)

	req := AuthzGrantTokenRequestTest(t, "authorization_code", authzCode)

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)
	// Tests for a 400 instead of 401 in accordance to http://tools.ietf.org/html/rfc6749#section-5.1
	equals(t, http.StatusBadRequest, w.Code)

	appErr := AuthzError{}
	err := json.Unmarshal(w.Body.Bytes(), &appErr)
	ok(t, err)
	equals(t, "unauthorized_client", appErr.Code)
}

// TestResourceOwnerCredentialsGrant tests happy path for http://tools.ietf.org/html/rfc6749#section-4.3
func TestResourceOwnerCredentialsGrant(t *testing.T) {
	provider := test.NewProvider(true)
	queryStr := url.Values{
		"grant_type": {"password"},
		"username":   {"test_user"},
		"password":   {"test_password"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	accessToken := types.Token{}
	err = json.Unmarshal(w.Body.Bytes(), &accessToken)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", accessToken.Type)
	equals(t, "600", accessToken.ExpiresIn)
}

// TestClientCredentialsGrant tests happy path for http://tools.ietf.org/html/rfc6749#section-4.4
func TestClientCredentialsGrant(t *testing.T) {
	provider := test.NewProvider(true)
	queryStr := url.Values{
		"grant_type": {"client_credentials"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	accessToken := types.Token{}
	err = json.Unmarshal(w.Body.Bytes(), &accessToken)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", accessToken.Type)
	equals(t, "600", accessToken.ExpiresIn)

	// A refresh token SHOULD NOT be included.
	equals(t, "", accessToken.RefreshToken)
}

func TestRefreshToken(t *testing.T) {
	provider := test.NewProvider(true)
	accessToken, err := provider.GenToken([]types.Scope{
		types.Scope{
			ID: "identity",
		}}, types.Client{
		ID: "test_client_id",
	}, true)
	ok(t, err)

	queryStr := url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {accessToken.RefreshToken},
		"scope":         {"identity"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	token := types.Token{}
	err = json.Unmarshal(w.Body.Bytes(), &token)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", token.Type)
	equals(t, "600", token.ExpiresIn)
	assert(t, accessToken.Value != token.Value, "We got the same access token, it should be different!")
	assert(t, token.Value != "", "We were expecting to get a token and instead we got: %s", token.Value)
	assert(t, token.RefreshToken != accessToken.RefreshToken, "We got the same refresh token, it should be different!")
}

// TestAuthzCodeOwnership tests that the authorization code was issued to the client
// requesting the access token.
func TestAuthzCodeOwnership(t *testing.T) {

}
<commit_msg>Adds comment to test function<commit_after>package oauth2

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/hooklift/oauth2/providers/test"
	"github.com/hooklift/oauth2/types"
)

func AuthzGrantTokenRequestTest(t *testing.T, grantType, authzCode string) *http.Request {
	// http://tools.ietf.org/html/rfc6749#section-4.1.3
	queryStr := url.Values{
		"grant_type":   {grantType},
		"code":         {authzCode},
		"redirect_uri": {"https://example.com/oauth2/callback"},
		"client_id":    {"test_client_id"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	return req
}

// TestAccessToken tests a happy path for getting access tokens in accordance with
// http://tools.ietf.org/html/rfc6749#section-4.1.3 and
// http://tools.ietf.org/html/rfc6749#section-4.1.4
func TestAuthzGrantTokenRequest(t *testing.T) {
	provider, authzCode := getTestAuthzCode(t)

	req := AuthzGrantTokenRequestTest(t, "authorization_code", authzCode)
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	// http://tools.ietf.org/html/rfc6749#section-4.1.4
	accessToken := types.Token{}
	err := json.Unmarshal(w.Body.Bytes(), &accessToken)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", accessToken.Type)
	equals(t, "600", accessToken.ExpiresIn)
}

// TestClientAuthRequired tests that client is required to always authenticate in order
// to request access tokens.
func TestAuthzGrantClientAuthRequired(t *testing.T) {
	provider, authzCode := getTestAuthzCode(t)

	req := AuthzGrantTokenRequestTest(t, "authorization_code", authzCode)

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)
	// Tests for a 400 instead of 401 in accordance to http://tools.ietf.org/html/rfc6749#section-5.1
	equals(t, http.StatusBadRequest, w.Code)

	appErr := AuthzError{}
	err := json.Unmarshal(w.Body.Bytes(), &appErr)
	ok(t, err)
	equals(t, "unauthorized_client", appErr.Code)
}

// TestResourceOwnerCredentialsGrant tests happy path for http://tools.ietf.org/html/rfc6749#section-4.3
func TestResourceOwnerCredentialsGrant(t *testing.T) {
	provider := test.NewProvider(true)
	queryStr := url.Values{
		"grant_type": {"password"},
		"username":   {"test_user"},
		"password":   {"test_password"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	accessToken := types.Token{}
	err = json.Unmarshal(w.Body.Bytes(), &accessToken)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", accessToken.Type)
	equals(t, "600", accessToken.ExpiresIn)
}

// TestClientCredentialsGrant tests happy path for http://tools.ietf.org/html/rfc6749#section-4.4
func TestClientCredentialsGrant(t *testing.T) {
	provider := test.NewProvider(true)
	queryStr := url.Values{
		"grant_type": {"client_credentials"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	accessToken := types.Token{}
	err = json.Unmarshal(w.Body.Bytes(), &accessToken)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", accessToken.Type)
	equals(t, "600", accessToken.ExpiresIn)

	// A refresh token SHOULD NOT be included.
	equals(t, "", accessToken.RefreshToken)
}

// TestRefreshToken tests happy path for http://tools.ietf.org/html/rfc6749#section-6
func TestRefreshToken(t *testing.T) {
	provider := test.NewProvider(true)
	accessToken, err := provider.GenToken([]types.Scope{
		types.Scope{
			ID: "identity",
		}}, types.Client{
		ID: "test_client_id",
	}, true)
	ok(t, err)

	queryStr := url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {accessToken.RefreshToken},
		"scope":         {"identity"},
	}

	buffer := bytes.NewBufferString(queryStr.Encode())
	req, err := http.NewRequest("POST", "https://example.com/oauth2/tokens", buffer)
	ok(t, err)
	req.Header.Set("Content-type", "application/x-www-form-urlencoded")
	req.SetBasicAuth("testclient", "testclient")

	w := httptest.NewRecorder()
	IssueAccessToken(w, req, provider)

	token := types.Token{}
	err = json.Unmarshal(w.Body.Bytes(), &token)
	ok(t, err)

	//log.Printf("%s", w.Body.String())
	equals(t, "bearer", token.Type)
	equals(t, "600", token.ExpiresIn)
	assert(t, accessToken.Value != token.Value, "We got the same access token, it should be different!")
	assert(t, token.Value != "", "We were expecting to get a token and instead we got: %s", token.Value)
	assert(t, token.RefreshToken != accessToken.RefreshToken, "We got the same refresh token, it should be different!")
}

// TestAuthzCodeOwnership tests that the authorization code was issued to the client
// requesting the access token.
func TestAuthzCodeOwnership(t *testing.T) {

}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package protobuf

import (
	"fmt"
	"reflect"
	"strings"

	"k8s.io/kubernetes/cmd/libs/go2idl/generator"
	"k8s.io/kubernetes/cmd/libs/go2idl/types"
)

type localNamer struct {
	localPackage types.Name
}

func (n localNamer) Name(t *types.Type) string {
	if t.Kind == types.Map {
		return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
	}
	if len(n.localPackage.Package) != 0 && n.localPackage.Package == t.Name.Package {
		return t.Name.Name
	}
	return t.Name.String()
}

type protobufNamer struct {
	packages       []*protobufPackage
	packagesByPath map[string]*protobufPackage
}

func NewProtobufNamer() *protobufNamer {
	return &protobufNamer{
		packagesByPath: make(map[string]*protobufPackage),
	}
}

func (n *protobufNamer) Name(t *types.Type) string {
	if t.Kind == types.Map {
		return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
	}
	return t.Name.String()
}

func (n *protobufNamer) List() []generator.Package {
	packages := make([]generator.Package, 0, len(n.packages))
	for i := range n.packages {
		packages = append(packages, n.packages[i])
	}
	return packages
}

func (n *protobufNamer) Add(p *protobufPackage) {
	n.packagesByPath[p.PackagePath] = p
	n.packages = append(n.packages, p)
}

func (n *protobufNamer) GoNameToProtoName(name types.Name) types.Name {
	if p, ok := n.packagesByPath[name.Package]; ok {
		return types.Name{
			Name:    name.Name,
			Package: p.PackageName,
			Path:    p.ImportPath(),
		}
	}
	for _, p := range n.packages {
		if _, ok := p.FilterTypes[name]; ok {
			return types.Name{
				Name:    name.Name,
				Package: p.PackageName,
				Path:    p.ImportPath(),
			}
		}
	}
	return types.Name{Name: name.Name}
}

func (n *protobufNamer) protoNameForTypeName(name types.Name) string {
	packageName := name.Package
	if len(name.Package) != 0 {
		if p, ok := n.packagesByPath[packageName]; ok {
			packageName = p.Name()
		} else {
			packageName = protoSafePackage(packageName)
		}
	}
	if len(name.Name) == 0 {
		return packageName
	}
	if len(packageName) > 0 {
		return packageName + "." + name.Name
	}
	return name.Name
}

func protoSafePackage(name string) string {
	return strings.Replace(name, "/", ".", -1)
}

type typeNameSet map[types.Name]*protobufPackage

// assignGoTypeToProtoPackage looks for Go and Protobuf types that are referenced by a type in
// a package. It will not recurse into protobuf types.
func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global typeNameSet) {
	newT, isProto := isFundamentalProtoType(t)
	if isProto {
		t = newT
	}
	if otherP, ok := global[t.Name]; ok {
		if _, ok := local[t.Name]; !ok {
			p.Imports.AddType(&types.Type{
				Kind: typesKindProtobuf,
				Name: otherP.ProtoTypeName(),
			})
		}
		return
	}
	global[t.Name] = p
	if _, ok := local[t.Name]; ok {
		return
	}
	// don't recurse into existing proto types
	if isProto {
		p.Imports.AddType(t)
		return
	}

	local[t.Name] = p
	for _, m := range t.Members {
		if isPrivateGoName(m.Name) {
			continue
		}
		field := &protoField{}
		if err := protobufTagToField(reflect.StructTag(m.Tags).Get("protobuf"), field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil {
			assignGoTypeToProtoPackage(p, field.Type, local, global)
			continue
		}
		assignGoTypeToProtoPackage(p, m.Type, local, global)
	}
	// TODO: should methods be walked?
	if t.Elem != nil {
		assignGoTypeToProtoPackage(p, t.Elem, local, global)
	}
	if t.Key != nil {
		assignGoTypeToProtoPackage(p, t.Key, local, global)
	}
	if t.Underlying != nil {
		assignGoTypeToProtoPackage(p, t.Underlying, local, global)
	}
}

func (n *protobufNamer) AssignTypesToPackages(c *generator.Context) error {
	global := make(typeNameSet)
	for _, p := range n.packages {
		local := make(typeNameSet)
		p.Imports = NewImportTracker(p.ProtoTypeName())
		for _, t := range c.Order {
			if t.Name.Package != p.PackagePath {
				continue
			}
			assignGoTypeToProtoPackage(p, t, local, global)
		}
		p.FilterTypes = make(map[types.Name]struct{})
		p.LocalNames = make(map[string]struct{})
		for k, v := range local {
			if v == p {
				p.FilterTypes[k] = struct{}{}
				p.LocalNames[k.Name] = struct{}{}
			}
		}
	}
	return nil
}
<commit_msg>Remove dead code<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package protobuf

import (
	"fmt"
	"reflect"
	"strings"

	"k8s.io/kubernetes/cmd/libs/go2idl/generator"
	"k8s.io/kubernetes/cmd/libs/go2idl/types"
)

type localNamer struct {
	localPackage types.Name
}

func (n localNamer) Name(t *types.Type) string {
	if t.Kind == types.Map {
		return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
	}
	if len(n.localPackage.Package) != 0 && n.localPackage.Package == t.Name.Package {
		return t.Name.Name
	}
	return t.Name.String()
}

type protobufNamer struct {
	packages       []*protobufPackage
	packagesByPath map[string]*protobufPackage
}

func NewProtobufNamer() *protobufNamer {
	return &protobufNamer{
		packagesByPath: make(map[string]*protobufPackage),
	}
}

func (n *protobufNamer) Name(t *types.Type) string {
	if t.Kind == types.Map {
		return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
	}
	return t.Name.String()
}

func (n *protobufNamer) List() []generator.Package {
	packages := make([]generator.Package, 0, len(n.packages))
	for i := range n.packages {
		packages = append(packages, n.packages[i])
	}
	return packages
}

func (n *protobufNamer) Add(p *protobufPackage) {
	n.packagesByPath[p.PackagePath] = p
	n.packages = append(n.packages, p)
}

func (n *protobufNamer) GoNameToProtoName(name types.Name) types.Name {
	if p, ok := n.packagesByPath[name.Package]; ok {
		return types.Name{
			Name:    name.Name,
			Package: p.PackageName,
			Path:    p.ImportPath(),
		}
	}
	for _, p := range n.packages {
		if _, ok := p.FilterTypes[name]; ok {
			return types.Name{
				Name:    name.Name,
				Package: p.PackageName,
				Path:    p.ImportPath(),
			}
		}
	}
	return types.Name{Name: name.Name}
}

func protoSafePackage(name string) string {
	return strings.Replace(name, "/", ".", -1)
}

type typeNameSet map[types.Name]*protobufPackage

// assignGoTypeToProtoPackage looks for Go and Protobuf types that are referenced by a type in
// a package. It will not recurse into protobuf types.
func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global typeNameSet) {
	newT, isProto := isFundamentalProtoType(t)
	if isProto {
		t = newT
	}
	if otherP, ok := global[t.Name]; ok {
		if _, ok := local[t.Name]; !ok {
			p.Imports.AddType(&types.Type{
				Kind: typesKindProtobuf,
				Name: otherP.ProtoTypeName(),
			})
		}
		return
	}
	global[t.Name] = p
	if _, ok := local[t.Name]; ok {
		return
	}
	// don't recurse into existing proto types
	if isProto {
		p.Imports.AddType(t)
		return
	}

	local[t.Name] = p
	for _, m := range t.Members {
		if isPrivateGoName(m.Name) {
			continue
		}
		field := &protoField{}
		if err := protobufTagToField(reflect.StructTag(m.Tags).Get("protobuf"), field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil {
			assignGoTypeToProtoPackage(p, field.Type, local, global)
			continue
		}
		assignGoTypeToProtoPackage(p, m.Type, local, global)
	}
	// TODO: should methods be walked?
	if t.Elem != nil {
		assignGoTypeToProtoPackage(p, t.Elem, local, global)
	}
	if t.Key != nil {
		assignGoTypeToProtoPackage(p, t.Key, local, global)
	}
	if t.Underlying != nil {
		assignGoTypeToProtoPackage(p, t.Underlying, local, global)
	}
}

func (n *protobufNamer) AssignTypesToPackages(c *generator.Context) error {
	global := make(typeNameSet)
	for _, p := range n.packages {
		local := make(typeNameSet)
		p.Imports = NewImportTracker(p.ProtoTypeName())
		for _, t := range c.Order {
			if t.Name.Package != p.PackagePath {
				continue
			}
			assignGoTypeToProtoPackage(p, t, local, global)
		}
		p.FilterTypes = make(map[types.Name]struct{})
		p.LocalNames = make(map[string]struct{})
		for k, v := range local {
			if v == p {
				p.FilterTypes[k] = struct{}{}
				p.LocalNames[k.Name] = struct{}{}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package fakesqldb provides a MySQL server for tests.
package fakesqldb

import (
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/youtube/vitess/go/mysqlconn"
	"github.com/youtube/vitess/go/sqldb"
	"github.com/youtube/vitess/go/sqltypes"
)

// DB is a fake database and all its methods are thread safe.  It
// creates a mysqlconn.Listener and implements the mysqlconn.Handler interface.
type DB struct {
	// Fields set at construction time.

	// t is our testing.T instance
	t *testing.T

	// listener is our mysqlconn.Listener.
	listener *mysqlconn.Listener

	// name is the name of this DB. Set to 'fakesqldb' by default.
	// Use SetName() to change.
	name string

	// acceptWG is set when we listen, and can be waited on to
	// make sure we don't accept any more.
	acceptWG sync.WaitGroup

	// Fields set at runtime.

	// mu protects all the following fields.
	mu sync.Mutex
	// isConnFail trigger a panic in the connection handler.
	isConnFail bool
	// shouldClose, if true, tells ComQuery() to close the connection when
	// processing the next query. This will trigger a MySQL client error with
	// errno 2013 ("server lost").
	shouldClose bool
	// data maps tolower(query) to a result.
	data map[string]*sqltypes.Result
	// rejectedData maps tolower(query) to an error.
	rejectedData map[string]error
	// patternData is a list of regexp to results.
	patternData []exprResult
	// queryCalled keeps track of how many times a query was called.
	queryCalled map[string]int
	// connections tracks all open connections.
	// The key for the map is the value of mysql.Conn.ConnectionID.
	connections map[uint32]*mysqlconn.Conn
}

type exprResult struct {
	expr   *regexp.Regexp
	result *sqltypes.Result
}

// New creates a server, and starts listening.
func New(t *testing.T) *DB {
	// Create our DB.
	db := &DB{
		t:            t,
		name:         "fakesqldb",
		data:         make(map[string]*sqltypes.Result),
		rejectedData: make(map[string]error),
		queryCalled:  make(map[string]int),
		connections:  make(map[uint32]*mysqlconn.Conn),
	}

	// Start listening.
	var err error
	db.listener, err = mysqlconn.NewListener("tcp", ":0", db)
	if err != nil {
		t.Fatalf("NewListener failed: %v", err)
	}

	db.listener.PasswordMap["user1"] = "password1"
	db.acceptWG.Add(1)
	go func() {
		defer db.acceptWG.Done()
		db.listener.Accept()
	}()

	// Return the db and connection parameters.
	return db
}

// SetName sets the name of the DB. to differentiate them in tests if needed.
func (db *DB) SetName(name string) *DB {
	db.name = name
	return db
}

// Close closes the Listener and waits for it to stop accepting.
func (db *DB) Close() {
	db.listener.Close()
	db.acceptWG.Wait()

	db.CloseAllConnections()
}

// CloseAllConnections can be used to provoke MySQL client errors for open
// connections.
// Make sure to call WaitForShutdown() as well.
func (db *DB) CloseAllConnections() {
	db.mu.Lock()
	defer db.mu.Unlock()

	for _, c := range db.connections {
		c.Close()
	}
}

// WaitForClose should be used after CloseAllConnections() is closed and
// you want to provoke a MySQL client error with errno 2006.
//
// If you don't call this function and execute the next query right away, you
// will very likely see errno 2013 instead due to timing issues.
// That's because the following can happen:
//
// 1. vttablet MySQL client is able to send the query to this fake server.
// 2. The fake server sees the query and calls the ComQuery() callback.
// 3. The fake server tries to write the response back on the connection.
// => This will finally fail because the connection is already closed.
// In this example, the client would have been able to send off the query and
// therefore return errno 2013 ("server lost").
// Instead, if step 1 already fails, the client returns errno 2006 ("gone away").
// By waiting for the connections to close, you make sure of that.
func (db *DB) WaitForClose(timeout time.Duration) error {
	start := time.Now()
	for {
		db.mu.Lock()
		count := len(db.connections)
		db.mu.Unlock()

		if count == 0 {
			return nil
		}
		if d := time.Since(start); d > timeout {
			return fmt.Errorf("connections were not correctly closed after %v: %v are left", d, count)
		}
		time.Sleep(1 * time.Microsecond)
	}
}

// Host returns the host we're listening on.
func (db *DB) Host() string {
	return db.listener.Addr().(*net.TCPAddr).IP.String()
}

// Port returns the port we're listening on.
func (db *DB) Port() int {
	return db.listener.Addr().(*net.TCPAddr).Port
}

// ConnParams returns the ConnParams to connect to the DB.
func (db *DB) ConnParams() *sqldb.ConnParams {
	return &sqldb.ConnParams{
		Host:    db.Host(),
		Port:    db.Port(),
		Uname:   "user1",
		Pass:    "password1",
		Charset: "utf8",
	}
}

//
// mysqlconn.Handler interface
//

// NewConnection is part of the mysqlconn.Handler interface.
func (db *DB) NewConnection(c *mysqlconn.Conn) {
	db.t.Logf("NewConnection(%v): client %v", db.name, c.ConnectionID)

	if db.IsConnFail() {
		panic(fmt.Errorf("simulating a connection failure"))
	}

	db.mu.Lock()
	defer db.mu.Unlock()

	if conn, ok := db.connections[c.ConnectionID]; ok {
		db.t.Fatalf("BUG: connection with id: %v is already active. existing conn: %v new conn: %v", c.ConnectionID, conn, c)
	}
	db.connections[c.ConnectionID] = c
}

// ConnectionClosed is part of the mysqlconn.Handler interface.
func (db *DB) ConnectionClosed(c *mysqlconn.Conn) {
	db.t.Logf("ConnectionClosed(%v): client %v", db.name, c.ConnectionID)

	db.mu.Lock()
	defer db.mu.Unlock()

	if _, ok := db.connections[c.ConnectionID]; !ok {
		db.t.Fatalf("BUG: Cannot delete connection from list of open connections because it is not registered. ID: %v Conn: %v", c.ConnectionID, c)
	}
	delete(db.connections, c.ConnectionID)
}

// ComQuery is part of the mysqlconn.Handler interface.
func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error) {
	db.t.Logf("ComQuery(%v): client %v: %v", db.name, c.ConnectionID, query)

	key := strings.ToLower(query)
	db.mu.Lock()
	defer db.mu.Unlock()
	db.queryCalled[key]++

	// Check if we should close the connection and provoke errno 2013.
	if db.shouldClose {
		c.Close()
		return &sqltypes.Result{}, nil
	}

	// Using special handling for 'SET NAMES utf8'.  The driver
	// may send this at connection time, and we don't want it to
	// interfere.
	if key == "set names utf8" {
		return &sqltypes.Result{}, nil
	}

	// check if we should reject it.
	if err, ok := db.rejectedData[key]; ok {
		return nil, err
	}

	// Check explicit queries from AddQuery().
	result, ok := db.data[key]
	if ok {
		return result, nil
	}

	// Check query patterns from AddQueryPattern().
	for _, pat := range db.patternData {
		if pat.expr.MatchString(query) {
			return pat.result, nil
		}
	}

	// Nothing matched.
	return nil, fmt.Errorf("query: %s is not supported on %v", query, db.name)
}

//
// Methods to add expected queries and results.
//

// AddQuery adds a query and its expected result.
func (db *DB) AddQuery(query string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", query))
	}
	result := &sqltypes.Result{}
	*result = *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	db.data[key] = result
	db.queryCalled[key] = 0
}

// AddQueryPattern adds an expected result for a set of queries.
// These patterns are checked if no exact matches from AddQuery() are found.
// This function forces the addition of begin/end anchors (^$) and turns on
// case-insensitive matching mode.
func (db *DB) AddQueryPattern(queryPattern string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", queryPattern))
	}
	expr := regexp.MustCompile("(?is)^" + queryPattern + "$")
	result := *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	db.patternData = append(db.patternData, exprResult{expr, &result})
}

// DeleteQuery deletes query from the fake DB.
func (db *DB) DeleteQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	delete(db.data, key)
	delete(db.queryCalled, key)
}

// AddRejectedQuery adds a query which will be rejected at execution time.
func (db *DB) AddRejectedQuery(query string, err error) {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.rejectedData[strings.ToLower(query)] = err
}

// DeleteRejectedQuery deletes query from the fake DB.
func (db *DB) DeleteRejectedQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	delete(db.rejectedData, strings.ToLower(query))
}

// GetQueryCalledNum returns how many times db executes a certain query.
func (db *DB) GetQueryCalledNum(query string) int {
	db.mu.Lock()
	defer db.mu.Unlock()
	num, ok := db.queryCalled[strings.ToLower(query)]
	if !ok {
		return 0
	}
	return num
}

// EnableConnFail makes connection to this fake DB fail.
func (db *DB) EnableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = true
}

// DisableConnFail makes connection to this fake DB success.
func (db *DB) DisableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = false
}

// IsConnFail tests whether there is a connection failure.
func (db *DB) IsConnFail() bool {
	db.mu.Lock()
	defer db.mu.Unlock()
	return db.isConnFail
}

// EnableShouldClose closes the connection when processing the next query.
func (db *DB) EnableShouldClose() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.shouldClose = true
}
<commit_msg>fakesqldb: Fix data race when SetName() is used.<commit_after>// Package fakesqldb provides a MySQL server for tests.
package fakesqldb

import (
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/youtube/vitess/go/mysqlconn"
	"github.com/youtube/vitess/go/sqldb"
	"github.com/youtube/vitess/go/sqltypes"
)

// DB is a fake database and all its methods are thread safe.  It
// creates a mysqlconn.Listener and implements the mysqlconn.Handler interface.
type DB struct {
	// Fields set at construction time.

	// t is our testing.T instance
	t *testing.T

	// listener is our mysqlconn.Listener.
	listener *mysqlconn.Listener

	// acceptWG is set when we listen, and can be waited on to
	// make sure we don't accept any more.
	acceptWG sync.WaitGroup

	// Fields set at runtime.

	// mu protects all the following fields.
	mu sync.Mutex
	// name is the name of this DB. Set to 'fakesqldb' by default.
	// Use SetName() to change.
	name string
	// isConnFail trigger a panic in the connection handler.
	isConnFail bool
	// shouldClose, if true, tells ComQuery() to close the connection when
	// processing the next query. This will trigger a MySQL client error with
	// errno 2013 ("server lost").
	shouldClose bool
	// data maps tolower(query) to a result.
	data map[string]*sqltypes.Result
	// rejectedData maps tolower(query) to an error.
	rejectedData map[string]error
	// patternData is a list of regexp to results.
	patternData []exprResult
	// queryCalled keeps track of how many times a query was called.
	queryCalled map[string]int
	// connections tracks all open connections.
	// The key for the map is the value of mysql.Conn.ConnectionID.
	connections map[uint32]*mysqlconn.Conn
}

type exprResult struct {
	expr   *regexp.Regexp
	result *sqltypes.Result
}

// New creates a server, and starts listening.
func New(t *testing.T) *DB {
	// Create our DB.
	db := &DB{
		t:            t,
		name:         "fakesqldb",
		data:         make(map[string]*sqltypes.Result),
		rejectedData: make(map[string]error),
		queryCalled:  make(map[string]int),
		connections:  make(map[uint32]*mysqlconn.Conn),
	}

	// Start listening.
	var err error
	db.listener, err = mysqlconn.NewListener("tcp", ":0", db)
	if err != nil {
		t.Fatalf("NewListener failed: %v", err)
	}

	db.listener.PasswordMap["user1"] = "password1"
	db.acceptWG.Add(1)
	go func() {
		defer db.acceptWG.Done()
		db.listener.Accept()
	}()

	// Return the db and connection parameters.
	return db
}

// SetName sets the name of the DB. to differentiate them in tests if needed.
func (db *DB) SetName(name string) *DB {
	db.mu.Lock()
	defer db.mu.Unlock()

	db.name = name
	return db
}

// Close closes the Listener and waits for it to stop accepting.
func (db *DB) Close() {
	db.listener.Close()
	db.acceptWG.Wait()

	db.CloseAllConnections()
}

// CloseAllConnections can be used to provoke MySQL client errors for open
// connections.
// Make sure to call WaitForShutdown() as well.
func (db *DB) CloseAllConnections() {
	db.mu.Lock()
	defer db.mu.Unlock()

	for _, c := range db.connections {
		c.Close()
	}
}

// WaitForClose should be used after CloseAllConnections() is closed and
// you want to provoke a MySQL client error with errno 2006.
//
// If you don't call this function and execute the next query right away, you
// will very likely see errno 2013 instead due to timing issues.
// That's because the following can happen:
//
// 1. vttablet MySQL client is able to send the query to this fake server.
// 2. The fake server sees the query and calls the ComQuery() callback.
// 3. The fake server tries to write the response back on the connection.
// => This will finally fail because the connection is already closed.
// In this example, the client would have been able to send off the query and
// therefore return errno 2013 ("server lost").
// Instead, if step 1 already fails, the client returns errno 2006 ("gone away").
// By waiting for the connections to close, you make sure of that.
func (db *DB) WaitForClose(timeout time.Duration) error {
	start := time.Now()
	for {
		db.mu.Lock()
		count := len(db.connections)
		db.mu.Unlock()

		if count == 0 {
			return nil
		}
		if d := time.Since(start); d > timeout {
			return fmt.Errorf("connections were not correctly closed after %v: %v are left", d, count)
		}
		time.Sleep(1 * time.Microsecond)
	}
}

// Host returns the host we're listening on.
func (db *DB) Host() string {
	return db.listener.Addr().(*net.TCPAddr).IP.String()
}

// Port returns the port we're listening on.
func (db *DB) Port() int {
	return db.listener.Addr().(*net.TCPAddr).Port
}

// ConnParams returns the ConnParams to connect to the DB.
func (db *DB) ConnParams() *sqldb.ConnParams {
	return &sqldb.ConnParams{
		Host:    db.Host(),
		Port:    db.Port(),
		Uname:   "user1",
		Pass:    "password1",
		Charset: "utf8",
	}
}

//
// mysqlconn.Handler interface
//

// NewConnection is part of the mysqlconn.Handler interface.
func (db *DB) NewConnection(c *mysqlconn.Conn) {
	db.mu.Lock()
	defer db.mu.Unlock()

	db.t.Logf("NewConnection(%v): client %v", db.name, c.ConnectionID)

	if db.isConnFail {
		panic(fmt.Errorf("simulating a connection failure"))
	}

	if conn, ok := db.connections[c.ConnectionID]; ok {
		db.t.Fatalf("BUG: connection with id: %v is already active. existing conn: %v new conn: %v", c.ConnectionID, conn, c)
	}
	db.connections[c.ConnectionID] = c
}

// ConnectionClosed is part of the mysqlconn.Handler interface.
func (db *DB) ConnectionClosed(c *mysqlconn.Conn) {
	db.mu.Lock()
	defer db.mu.Unlock()

	db.t.Logf("ConnectionClosed(%v): client %v", db.name, c.ConnectionID)

	if _, ok := db.connections[c.ConnectionID]; !ok {
		db.t.Fatalf("BUG: Cannot delete connection from list of open connections because it is not registered. ID: %v Conn: %v", c.ConnectionID, c)
	}
	delete(db.connections, c.ConnectionID)
}

// ComQuery is part of the mysqlconn.Handler interface.
func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error) {
	db.t.Logf("ComQuery(%v): client %v: %v", db.name, c.ConnectionID, query)

	key := strings.ToLower(query)
	db.mu.Lock()
	defer db.mu.Unlock()
	db.queryCalled[key]++

	// Check if we should close the connection and provoke errno 2013.
	if db.shouldClose {
		c.Close()
		return &sqltypes.Result{}, nil
	}

	// Using special handling for 'SET NAMES utf8'.  The driver
	// may send this at connection time, and we don't want it to
	// interfere.
	if key == "set names utf8" {
		return &sqltypes.Result{}, nil
	}

	// check if we should reject it.
	if err, ok := db.rejectedData[key]; ok {
		return nil, err
	}

	// Check explicit queries from AddQuery().
	result, ok := db.data[key]
	if ok {
		return result, nil
	}

	// Check query patterns from AddQueryPattern().
	for _, pat := range db.patternData {
		if pat.expr.MatchString(query) {
			return pat.result, nil
		}
	}

	// Nothing matched.
	return nil, fmt.Errorf("query: %s is not supported on %v", query, db.name)
}

//
// Methods to add expected queries and results.
//

// AddQuery adds a query and its expected result.
func (db *DB) AddQuery(query string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", query))
	}
	result := &sqltypes.Result{}
	*result = *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	db.data[key] = result
	db.queryCalled[key] = 0
}

// AddQueryPattern adds an expected result for a set of queries.
// These patterns are checked if no exact matches from AddQuery() are found.
// This function forces the addition of begin/end anchors (^$) and turns on
// case-insensitive matching mode.
func (db *DB) AddQueryPattern(queryPattern string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", queryPattern))
	}
	expr := regexp.MustCompile("(?is)^" + queryPattern + "$")
	result := *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	db.patternData = append(db.patternData, exprResult{expr, &result})
}

// DeleteQuery deletes query from the fake DB.
func (db *DB) DeleteQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	delete(db.data, key)
	delete(db.queryCalled, key)
}

// AddRejectedQuery adds a query which will be rejected at execution time.
func (db *DB) AddRejectedQuery(query string, err error) {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.rejectedData[strings.ToLower(query)] = err
}

// DeleteRejectedQuery deletes query from the fake DB.
func (db *DB) DeleteRejectedQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	delete(db.rejectedData, strings.ToLower(query))
}

// GetQueryCalledNum returns how many times db executes a certain query.
func (db *DB) GetQueryCalledNum(query string) int {
	db.mu.Lock()
	defer db.mu.Unlock()
	num, ok := db.queryCalled[strings.ToLower(query)]
	if !ok {
		return 0
	}
	return num
}

// EnableConnFail makes connection to this fake DB fail.
func (db *DB) EnableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = true
}

// DisableConnFail makes connection to this fake DB success.
func (db *DB) DisableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = false
}

// EnableShouldClose closes the connection when processing the next query.
func (db *DB) EnableShouldClose() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.shouldClose = true
}
<|endoftext|>


<commit_before>package actors

import (
	"fmt"

	"github.com/Azure/azure-sdk-for-go/arm/compute"
	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
	acceptance "github.com/cloudfoundry/bosh-bootloader/acceptance-tests"
)

type Azure struct {
	groupsClient              *resources.GroupsClient
	virtualMachinesClient     *compute.VirtualMachinesClient
	applicationGatewaysClient *network.ApplicationGatewaysClient
	subscriptionID            string
	tenantID                  string
	clientID                  string
	clientSecret              string
}

func NewAzure(config acceptance.Config) Azure {
	oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, config.AzureTenantID)
	if err != nil {
		panic(err)
	}

	servicePrincipalToken, err := adal.NewServicePrincipalToken(*oauthConfig, config.AzureClientID, config.AzureClientSecret, azure.PublicCloud.ResourceManagerEndpoint)
	if err != nil {
		panic(err)
	}

	gc := resources.NewGroupsClient(config.AzureSubscriptionID)
	gc.ManagementClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
	gc.ManagementClient.Sender = autorest.CreateSender(autorest.AsIs())

	vmc := compute.NewVirtualMachinesClient(config.AzureSubscriptionID)
	vmc.ManagementClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
	vmc.ManagementClient.Sender = autorest.CreateSender(autorest.AsIs())

	agc := network.NewApplicationGatewaysClient(config.AzureSubscriptionID)
	agc.ManagementClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
	agc.ManagementClient.Sender = autorest.CreateSender(autorest.AsIs())

	return Azure{
		groupsClient:              &gc,
		virtualMachinesClient:     &vmc,
		applicationGatewaysClient: &agc,
		subscriptionID:            config.AzureSubscriptionID,
		tenantID:                  config.AzureTenantID,
		clientID:                  config.AzureClientID,
		clientSecret:              config.AzureClientSecret,
	}
}

func (a Azure) GetApplicationGateway(resourceGroupName, applicationGatewayName string) (bool, error) {
	_, err := a.applicationGatewaysClient.Get(fmt.Sprintf("%s-bosh", resourceGroupName), applicationGatewayName)
	if err != nil {
		if aerr, ok := err.(*autorest.DetailedError); ok {
			if aerr.StatusCode == 404 {
				return false, nil
			}
		}
		return false, err
	}

	return true, nil
}

func (a Azure) GetResourceGroup(resourceGroupName string) (bool, error) {
	_, err := a.groupsClient.Get(resourceGroupName)
	if err != nil {
		return false, err
	}

	return true, nil
}

func (a Azure) NetworkHasBOSHDirector(envID string) bool {
	resourceGroupName := fmt.Sprintf("%s-bosh", envID)
	result, err := a.virtualMachinesClient.List(resourceGroupName)
	if err != nil {
		panic(err)
	}

	for _, vm := range *result.Value {
		if *(*vm.Tags)["deployment"] == "bosh" {
			return true
		}
	}

	return false
}
<commit_msg>Fix azure acceptance tests check.<commit_after>package actors

import (
	"fmt"

	"github.com/Azure/azure-sdk-for-go/arm/compute"
	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
	acceptance "github.com/cloudfoundry/bosh-bootloader/acceptance-tests"
)

type Azure struct {
	groupsClient              *resources.GroupsClient
	virtualMachinesClient     *compute.VirtualMachinesClient
	applicationGatewaysClient *network.ApplicationGatewaysClient
	subscriptionID            string
	tenantID                  string
	clientID                  string
	clientSecret              string
}

func NewAzure(config acceptance.Config) Azure {
	oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, config.AzureTenantID)
	if err != nil {
		panic(err)
	}

	servicePrincipalToken, err := adal.NewServicePrincipalToken(*oauthConfig, config.AzureClientID, config.AzureClientSecret, azure.PublicCloud.ResourceManagerEndpoint)
	if err != nil {
		panic(err)
	}

	gc := resources.NewGroupsClient(config.AzureSubscriptionID)
	gc.ManagementClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
	gc.ManagementClient.Sender = autorest.CreateSender(autorest.AsIs())

	vmc := compute.NewVirtualMachinesClient(config.AzureSubscriptionID)
	vmc.ManagementClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
	vmc.ManagementClient.Sender = autorest.CreateSender(autorest.AsIs())

	agc := network.NewApplicationGatewaysClient(config.AzureSubscriptionID)
	agc.ManagementClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
	agc.ManagementClient.Sender = autorest.CreateSender(autorest.AsIs())

	return Azure{
		groupsClient:              &gc,
		virtualMachinesClient:     &vmc,
		applicationGatewaysClient: &agc,
		subscriptionID:            config.AzureSubscriptionID,
		tenantID:                  config.AzureTenantID,
		clientID:                  config.AzureClientID,
		clientSecret:              config.AzureClientSecret,
	}
}

func (a Azure) GetApplicationGateway(resourceGroupName, applicationGatewayName string) (bool, error) {
	_, err := a.applicationGatewaysClient.Get(fmt.Sprintf("%s-bosh", resourceGroupName), applicationGatewayName)
	if err != nil {
		if aerr, ok := err.(autorest.DetailedError); ok {
			if aerr.StatusCode.(int) == 404 {
				return false, nil
			}
		}
		return false, err
	}

	return true, nil
}

func (a Azure) GetResourceGroup(resourceGroupName string) (bool, error) {
	_, err := a.groupsClient.Get(resourceGroupName)
	if err != nil {
		return false, err
	}

	return true, nil
}

func (a Azure) NetworkHasBOSHDirector(envID string) bool {
	resourceGroupName := fmt.Sprintf("%s-bosh", envID)
	result, err := a.virtualMachinesClient.List(resourceGroupName)
	if err != nil {
		panic(err)
	}

	for _, vm := range *result.Value {
		if *(*vm.Tags)["deployment"] == "bosh" {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package sstable

import (
	"bytes"
	"testing"
)

type BytesReaderCloser struct {
	*bytes.Reader
}

func (BytesReaderCloser) Close() error {
	return nil
}

func equalBytes(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func Test_roundtrip(t *testing.T) {
	input := []Pair{
		Pair{"q", nil},
		Pair{"w", []byte{'a'}},
		Pair{"o", []byte{'b', 'b'}},
		Pair{"p", []byte{'c', 'c', 'c'}},
	}
	var buf bytes.Buffer
	err := Build(&buf, input)
	if err != nil {
		t.Errorf("build err = %#v", err)
		return
	}
	r := BytesReaderCloser{bytes.NewReader(buf.Bytes())}
	tbl, err := New(r)
	if err != nil {
		t.Errorf("load err = %#v", err)
		return
	}
	var output []Pair
	for item := range tbl.All() {
		output = append(output, item)
	}
	i := 0
	j := 0
	for i < len(input) && j < len(output) {
		switch {
		case input[i].Key == output[j].Key && equalBytes(input[i].Value, output[j].Value):
			i++
			j++

		case input[i].Key == output[j].Key:
			t.Errorf("differing values for key %q: expected %v, got %v", input[i].Key, input[i].Value, output[j].Value)
			i++
			j++

		case input[i].Key < output[j].Key:
			t.Errorf("missing key %q", input[i].Key)
			i++

		default:
			t.Errorf("extra key %q", output[j].Key)
			j++
		}
	}
	if len(input) > len(output) {
		t.Errorf("missing %d items", len(input)-len(output))
	}
	if len(input) < len(output) {
		t.Errorf("extra %d items", len(output)-len(input))
	}
}
<commit_msg>Close the table.<commit_after>package sstable

import (
	"bytes"
	"testing"
)

type BytesReaderCloser struct {
	*bytes.Reader
}

func (BytesReaderCloser) Close() error {
	return nil
}

func equalBytes(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func Test_roundtrip(t *testing.T) {
	input := []Pair{
		Pair{"q", nil},
		Pair{"w", []byte{'a'}},
		Pair{"o", []byte{'b', 'b'}},
		Pair{"p", []byte{'c', 'c', 'c'}},
	}
	var buf bytes.Buffer
	err := Build(&buf, input)
	if err != nil {
		t.Errorf("build err = %#v", err)
		return
	}
	r := BytesReaderCloser{bytes.NewReader(buf.Bytes())}
	tbl, err := New(r)
	if err != nil {
		t.Errorf("load err = %#v", err)
		return
	}
	var output []Pair
	for item := range tbl.All() {
		output = append(output, item)
	}
	i := 0
	j := 0
	for i < len(input) && j < len(output) {
		switch {
		case input[i].Key == output[j].Key && equalBytes(input[i].Value, output[j].Value):
			i++
			j++

		case input[i].Key == output[j].Key:
			t.Errorf("differing values for key %q: expected %v, got %v", input[i].Key, input[i].Value, output[j].Value)
			i++
			j++

		case input[i].Key < output[j].Key:
			t.Errorf("missing key %q", input[i].Key)
			i++

		default:
			t.Errorf("extra key %q", output[j].Key)
			j++
		}
	}
	if len(input) > len(output) {
		t.Errorf("missing %d items", len(input)-len(output))
	}
	if len(input) < len(output) {
		t.Errorf("extra %d items", len(output)-len(input))
	}
	tbl.Close()
}
<|endoftext|>