<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 chart

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/jetstack/cert-manager/test/e2e/framework/addon/tiller"
	"github.com/jetstack/cert-manager/test/e2e/framework/config"
	"github.com/jetstack/cert-manager/test/e2e/framework/log"
)

// Chart is a generic Helm chart addon for the test environment
type Chart struct {
	config        *config.Config
	tillerDetails *tiller.Details
	// temporary directory used as the --home flag to Helm
	home string

	// Tiller is the tiller instance to submit the release to
	Tiller *tiller.Tiller

	// ReleaseName for this Helm release
	// `helm install --name {{ReleaseName}}`
	ReleaseName string

	// Namespace for the Helm release
	// `helm install --namespace {{Namespace}}`
	Namespace string

	// ChartName is the name of the chart to deploy
	// `helm install {{ChartName}}``
	ChartName string

	// ChartVersion is the version of the chart to deploy
	// `helm install --version {{ChartVersion}}`
	ChartVersion string

	// Vars are additional --set arguments for helm install
	// `helm install --set {{Vars[0].Key}}={{Vars[0].Value}} --set {{Vars[1].Key}}={{Vars[1].Value}} ...`
	Vars []StringTuple

	// Values is a list of paths to additional values.yaml files to include
	// `helm install --values {{Values[0]}} --values {{Values[1]}} ...`
	Values []string

	// If UpdateDeps is true, 'helm dep update' will be run against the chart
	// before installing.
	// This should only be set to true when the ChartName is a local path on disk.
	UpdateDeps bool
}

// StringTuple is a tuple of strings, used to create ordered maps
type StringTuple struct {
	Key   string
	Value string
}

// Details return the details about the Tiller instance deployed
type Details struct {
	// Helm chart release name
	ReleaseName string

	// Namespace that Tiller has been deployed into
	Namespace string
}

func (c *Chart) Setup(cfg *config.Config) error {
	var err error

	c.config = cfg
	if c.config.Addons.Helm.Path == "" {
		return fmt.Errorf("--helm-binary-path must be set")
	}
	if c.Tiller == nil {
		return fmt.Errorf("tiller base addon must be provided")
	}
	c.tillerDetails, err = c.Tiller.Details()
	if err != nil {
		return err
	}

	c.home, err = ioutil.TempDir("", "helm-chart-install")
	if err != nil {
		return err
	}

	return nil
}

// Provision an instance of tiller-deploy
func (c *Chart) Provision() error {
	err := c.runHelmClientInit()
	if err != nil {
		return fmt.Errorf("error running 'helm init': %v", err)
	}

	if c.UpdateDeps {
		err := c.runDepUpdate()
		if err != nil {
			return fmt.Errorf("error updating helm chart dependencies: %v", err)
		}
	}

	err = c.runInstall()
	if err != nil {
		return fmt.Errorf("error install helm chart: %v", err)
	}

	err = c.Tiller.Base.Details().Helper().WaitForAllPodsRunningInNamespace(c.Namespace)
	if err != nil {
		return err
	}

	return nil
}

func (c *Chart) runHelmClientInit() error {
	err := c.buildHelmCmd("init", "--client-only").Run()
	if err != nil {
		return err
	}
	return nil
}

func (c *Chart) runDepUpdate() error {
	err := c.buildHelmCmd("dep", "update", c.ChartName).Run()
	if err != nil {
		return err
	}
	return nil
}

func (c *Chart) runInstall() error {
	args := []string{"install", c.ChartName,
		"--wait",
		"--namespace", c.Namespace,
		"--name", c.ReleaseName,
		"--version", c.ChartVersion}

	for _, v := range c.Values {
		args = append(args, "--values", v)
	}

	for _, s := range c.Vars {
		args = append(args, "--set", fmt.Sprintf("%s=%s", s.Key, s.Value))
	}

	cmd := c.buildHelmCmd(args...)
	err := cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

func (c *Chart) buildHelmCmd(args ...string) *exec.Cmd {
	args = append([]string{
		"--home", c.home,
		"--kubeconfig", c.tillerDetails.KubeConfig,
		"--kube-context", c.tillerDetails.KubeContext,
		"--tiller-namespace", c.tillerDetails.Namespace,
	}, args...)
	cmd := exec.Command(c.config.Addons.Helm.Path, args...)
	cmd.Stdout = log.Writer
	cmd.Stderr = log.Writer
	return cmd
}

func (c *Chart) getHelmVersion() (string, error) {
	cmd := c.buildHelmCmd("version", "--template", "{{.Client.Version}}")
	cmd.Stdout = nil
	out, err := cmd.StdoutPipe()
	if err != nil {
		return "", err
	}
	defer out.Close()

	err = cmd.Run()
	if err != nil {
		return "", err
	}

	outBytes, err := ioutil.ReadAll(out)
	if err != nil {
		return "", err
	}

	return string(outBytes), nil
}

// Deprovision the deployed instance of tiller-deploy
func (c *Chart) Deprovision() error {
	err := c.buildHelmCmd("delete", "--purge", c.ReleaseName).Run()
	if err != nil {
		// Ignore deprovisioning errors
		// TODO: only ignore failed to delete because it doesn't exist errors
		return nil
	}

	// attempt to cleanup
	os.RemoveAll(c.home)

	// TODO: delete namespace manually too
	return nil
}

// Details must be possible to compute without Provision being called if we want
// to be able to provision global/shared instances of Tiller.
func (c *Chart) Details() (*Details, error) {
	d := &Details{
		ReleaseName: c.ReleaseName,
		Namespace:   c.Namespace,
	}

	return d, nil
}

func (c *Chart) SupportsGlobal() bool {
	// We can't run in global mode if the release name is not set, as there's
	// no way for us to communicate the generated release name to other test
	// runners when running in parallel mode.
	if c.ReleaseName == "" {
		return false
	}

	return true
}

func (c *Chart) Logs() (map[string]string, error) {
	kc := c.Tiller.Base.Details().KubeClient
	oldLabelPods, err := kc.CoreV1().Pods(c.Namespace).List(metav1.ListOptions{LabelSelector: "release=" + c.ReleaseName})
	if err != nil {
		return nil, err
	}

	// also check pods with the new style labels used in the cert-manager chart
	newLabelPods, err := kc.CoreV1().Pods(c.Namespace).List(metav1.ListOptions{LabelSelector: "app.kubernetes.io/instance=" + c.ReleaseName})
	if err != nil {
		return nil, err
	}
	podList := append(oldLabelPods.Items, newLabelPods.Items...)

	out := make(map[string]string)
	for _, pod := range podList {
		for _, con := range pod.Spec.Containers {
			for _, b := range []bool{true, false} {
				resp := kc.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &corev1.PodLogOptions{
					Container: con.Name,
					Previous:  b,
				}).Do()

				err := resp.Error()
				if err != nil {
					continue
				}

				logs, err := resp.Raw()
				if err != nil {
					continue
				}

				outPath := path.Join(c.Namespace, pod.Name, con.Name)
				if !b {
					out[outPath] = fmt.Sprintf("%s\n%s", out[outPath], string(logs))
				} else {
					out[outPath] = string(logs)
				}
			}
		}
	}

	return out, nil
}
<commit_msg>Move e2e output logs with suffixes<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 chart

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/jetstack/cert-manager/test/e2e/framework/addon/tiller"
	"github.com/jetstack/cert-manager/test/e2e/framework/config"
	"github.com/jetstack/cert-manager/test/e2e/framework/log"
)

// Chart is a generic Helm chart addon for the test environment
type Chart struct {
	config        *config.Config
	tillerDetails *tiller.Details
	// temporary directory used as the --home flag to Helm
	home string

	// Tiller is the tiller instance to submit the release to
	Tiller *tiller.Tiller

	// ReleaseName for this Helm release
	// `helm install --name {{ReleaseName}}`
	ReleaseName string

	// Namespace for the Helm release
	// `helm install --namespace {{Namespace}}`
	Namespace string

	// ChartName is the name of the chart to deploy
	// `helm install {{ChartName}}``
	ChartName string

	// ChartVersion is the version of the chart to deploy
	// `helm install --version {{ChartVersion}}`
	ChartVersion string

	// Vars are additional --set arguments for helm install
	// `helm install --set {{Vars[0].Key}}={{Vars[0].Value}} --set {{Vars[1].Key}}={{Vars[1].Value}} ...`
	Vars []StringTuple

	// Values is a list of paths to additional values.yaml files to include
	// `helm install --values {{Values[0]}} --values {{Values[1]}} ...`
	Values []string

	// If UpdateDeps is true, 'helm dep update' will be run against the chart
	// before installing.
	// This should only be set to true when the ChartName is a local path on disk.
	UpdateDeps bool
}

// StringTuple is a tuple of strings, used to create ordered maps
type StringTuple struct {
	Key   string
	Value string
}

// Details return the details about the Tiller instance deployed
type Details struct {
	// Helm chart release name
	ReleaseName string

	// Namespace that Tiller has been deployed into
	Namespace string
}

func (c *Chart) Setup(cfg *config.Config) error {
	var err error

	c.config = cfg
	if c.config.Addons.Helm.Path == "" {
		return fmt.Errorf("--helm-binary-path must be set")
	}
	if c.Tiller == nil {
		return fmt.Errorf("tiller base addon must be provided")
	}
	c.tillerDetails, err = c.Tiller.Details()
	if err != nil {
		return err
	}

	c.home, err = ioutil.TempDir("", "helm-chart-install")
	if err != nil {
		return err
	}

	return nil
}

// Provision an instance of tiller-deploy
func (c *Chart) Provision() error {
	err := c.runHelmClientInit()
	if err != nil {
		return fmt.Errorf("error running 'helm init': %v", err)
	}

	if c.UpdateDeps {
		err := c.runDepUpdate()
		if err != nil {
			return fmt.Errorf("error updating helm chart dependencies: %v", err)
		}
	}

	err = c.runInstall()
	if err != nil {
		return fmt.Errorf("error install helm chart: %v", err)
	}

	err = c.Tiller.Base.Details().Helper().WaitForAllPodsRunningInNamespace(c.Namespace)
	if err != nil {
		return err
	}

	return nil
}

func (c *Chart) runHelmClientInit() error {
	err := c.buildHelmCmd("init", "--client-only").Run()
	if err != nil {
		return err
	}
	return nil
}

func (c *Chart) runDepUpdate() error {
	err := c.buildHelmCmd("dep", "update", c.ChartName).Run()
	if err != nil {
		return err
	}
	return nil
}

func (c *Chart) runInstall() error {
	args := []string{"install", c.ChartName,
		"--wait",
		"--namespace", c.Namespace,
		"--name", c.ReleaseName,
		"--version", c.ChartVersion}

	for _, v := range c.Values {
		args = append(args, "--values", v)
	}

	for _, s := range c.Vars {
		args = append(args, "--set", fmt.Sprintf("%s=%s", s.Key, s.Value))
	}

	cmd := c.buildHelmCmd(args...)
	err := cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

func (c *Chart) buildHelmCmd(args ...string) *exec.Cmd {
	args = append([]string{
		"--home", c.home,
		"--kubeconfig", c.tillerDetails.KubeConfig,
		"--kube-context", c.tillerDetails.KubeContext,
		"--tiller-namespace", c.tillerDetails.Namespace,
	}, args...)
	cmd := exec.Command(c.config.Addons.Helm.Path, args...)
	cmd.Stdout = log.Writer
	cmd.Stderr = log.Writer
	return cmd
}

func (c *Chart) getHelmVersion() (string, error) {
	cmd := c.buildHelmCmd("version", "--template", "{{.Client.Version}}")
	cmd.Stdout = nil
	out, err := cmd.StdoutPipe()
	if err != nil {
		return "", err
	}
	defer out.Close()

	err = cmd.Run()
	if err != nil {
		return "", err
	}

	outBytes, err := ioutil.ReadAll(out)
	if err != nil {
		return "", err
	}

	return string(outBytes), nil
}

// Deprovision the deployed instance of tiller-deploy
func (c *Chart) Deprovision() error {
	err := c.buildHelmCmd("delete", "--purge", c.ReleaseName).Run()
	if err != nil {
		// Ignore deprovisioning errors
		// TODO: only ignore failed to delete because it doesn't exist errors
		return nil
	}

	// attempt to cleanup
	os.RemoveAll(c.home)

	// TODO: delete namespace manually too
	return nil
}

// Details must be possible to compute without Provision being called if we want
// to be able to provision global/shared instances of Tiller.
func (c *Chart) Details() (*Details, error) {
	d := &Details{
		ReleaseName: c.ReleaseName,
		Namespace:   c.Namespace,
	}

	return d, nil
}

func (c *Chart) SupportsGlobal() bool {
	// We can't run in global mode if the release name is not set, as there's
	// no way for us to communicate the generated release name to other test
	// runners when running in parallel mode.
	if c.ReleaseName == "" {
		return false
	}

	return true
}

func (c *Chart) Logs() (map[string]string, error) {
	kc := c.Tiller.Base.Details().KubeClient
	oldLabelPods, err := kc.CoreV1().Pods(c.Namespace).List(metav1.ListOptions{LabelSelector: "release=" + c.ReleaseName})
	if err != nil {
		return nil, err
	}

	// also check pods with the new style labels used in the cert-manager chart
	newLabelPods, err := kc.CoreV1().Pods(c.Namespace).List(metav1.ListOptions{LabelSelector: "app.kubernetes.io/instance=" + c.ReleaseName})
	if err != nil {
		return nil, err
	}
	podList := append(oldLabelPods.Items, newLabelPods.Items...)

	out := make(map[string]string)
	for _, pod := range podList {
		for _, con := range pod.Spec.Containers {
			for _, b := range []bool{true, false} {
				resp := kc.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &corev1.PodLogOptions{
					Container: con.Name,
					Previous:  b,
				}).Do()

				err := resp.Error()
				if err != nil {
					continue
				}

				logs, err := resp.Raw()
				if err != nil {
					continue
				}

				outPath := path.Join(c.Namespace,
					fmt.Sprintf("%s-%s", pod.Name, con.Name))

				if b {
					outPath = fmt.Sprintf("%s-previous", outPath)
				}

				out[outPath] = string(logs)
			}
		}
	}

	return out, nil
}
<|endoftext|>


<commit_before>package agouti

import "encoding/json"

// A Capabilities instance defines the desired capabilities the WebDriver
// should use to configure a Page.
//
// For example, to open Firefox with JavaScript disabled:
//    capabilities := agouti.NewCapabilities().Browser("firefox").Without("javascriptEnabled")
//    driver.NewPage(agouti.Desired(capabilities))
// See: https://code.google.com/p/selenium/wiki/DesiredCapabilities
//
// All methods called on this instance will modify the original instance.
type Capabilities map[string]interface{}

// NewCapabilities returns a Capabilities instance with any provided features enabled.
func NewCapabilities(features ...string) Capabilities {
	c := Capabilities{}
	for _, feature := range features {
		c.With(feature)
	}
	return c
}

// Browser sets the desired browser name.
// Possible values:
//    {android|chrome|firefox|htmlunit|internet explorer|iPhone|iPad|opera|safari}
func (c Capabilities) Browser(name string) Capabilities {
	c["browserName"] = name
	return c
}

// A ProxyConfig instance defines the desired proxy configuration the WebDriver
// should use to proxy a Page.
//
//    ProxyType is required and defines the type of proxy being used
//    Possible Values:
//        {direct|manual|pac|autodetect|system}
//
// See: https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#proxy-json-object
type ProxyConfig struct {
	ProxyType          string `json:"proxyType"`
	ProxyAutoconfigURL string `json:"proxyAutoconfigUrl,omitempty"`
	FTPProxy           string `json:"ftpProxy,omitempty"`
	HTTPProxy          string `json:"httpProxy,omitempty"`
	SSLProxy           string `json:"sslProxy,omitempty"`
	SOCKSProxy         string `json:"socksProxy,omitempty"`
	SOCKSUsername      string `json:"socksUsername,omitempty"`
	SOCKSPassword      string `json:"socksPassword,omitempty"`
	NoProxy            string `json:"noProxy,omitempty"`
}

// Proxy sets the desired proxy configuration.
func (c Capabilities) Proxy(p ProxyConfig) Capabilities {
	c["proxy"] = p
	return c
}

// Version sets the desired browser version (ex. "3.6").
func (c Capabilities) Version(version string) Capabilities {
	c["version"] = version
	return c
}

// Platform sets the desired browser platform.
// Possible values:
//    {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANDROID|ANY}.
func (c Capabilities) Platform(platform string) Capabilities {
	c["platform"] = platform
	return c
}

// With enables the provided feature (ex. "trustAllSSLCertificates").
func (c Capabilities) With(feature string) Capabilities {
	c[feature] = true
	return c
}

// Without disables the provided feature (ex. "javascriptEnabled").
func (c Capabilities) Without(feature string) Capabilities {
	c[feature] = false
	return c
}

// JSON returns a JSON string representing the desired capabilities.
func (c Capabilities) JSON() (string, error) {
	capabilitiesJSON, err := json.Marshal(c)
	return string(capabilitiesJSON), err
}
<commit_msg>Fixing link to correct DesiredCapabilities doc<commit_after>package agouti

import "encoding/json"

// A Capabilities instance defines the desired capabilities the WebDriver
// should use to configure a Page.
//
// For example, to open Firefox with JavaScript disabled:
//    capabilities := agouti.NewCapabilities().Browser("firefox").Without("javascriptEnabled")
//    driver.NewPage(agouti.Desired(capabilities))
// See: https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities
//
// All methods called on this instance will modify the original instance.
type Capabilities map[string]interface{}

// NewCapabilities returns a Capabilities instance with any provided features enabled.
func NewCapabilities(features ...string) Capabilities {
	c := Capabilities{}
	for _, feature := range features {
		c.With(feature)
	}
	return c
}

// Browser sets the desired browser name.
// Possible values:
//    {android|chrome|firefox|htmlunit|internet explorer|iPhone|iPad|opera|safari}
func (c Capabilities) Browser(name string) Capabilities {
	c["browserName"] = name
	return c
}

// A ProxyConfig instance defines the desired proxy configuration the WebDriver
// should use to proxy a Page.
//
//    ProxyType is required and defines the type of proxy being used
//    Possible Values:
//        {direct|manual|pac|autodetect|system}
//
// See: https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#proxy-json-object
type ProxyConfig struct {
	ProxyType          string `json:"proxyType"`
	ProxyAutoconfigURL string `json:"proxyAutoconfigUrl,omitempty"`
	FTPProxy           string `json:"ftpProxy,omitempty"`
	HTTPProxy          string `json:"httpProxy,omitempty"`
	SSLProxy           string `json:"sslProxy,omitempty"`
	SOCKSProxy         string `json:"socksProxy,omitempty"`
	SOCKSUsername      string `json:"socksUsername,omitempty"`
	SOCKSPassword      string `json:"socksPassword,omitempty"`
	NoProxy            string `json:"noProxy,omitempty"`
}

// Proxy sets the desired proxy configuration.
func (c Capabilities) Proxy(p ProxyConfig) Capabilities {
	c["proxy"] = p
	return c
}

// Version sets the desired browser version (ex. "3.6").
func (c Capabilities) Version(version string) Capabilities {
	c["version"] = version
	return c
}

// Platform sets the desired browser platform.
// Possible values:
//    {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANDROID|ANY}.
func (c Capabilities) Platform(platform string) Capabilities {
	c["platform"] = platform
	return c
}

// With enables the provided feature (ex. "trustAllSSLCertificates").
func (c Capabilities) With(feature string) Capabilities {
	c[feature] = true
	return c
}

// Without disables the provided feature (ex. "javascriptEnabled").
func (c Capabilities) Without(feature string) Capabilities {
	c[feature] = false
	return c
}

// JSON returns a JSON string representing the desired capabilities.
func (c Capabilities) JSON() (string, error) {
	capabilitiesJSON, err := json.Marshal(c)
	return string(capabilitiesJSON), err
}
<|endoftext|>


<commit_before>package template

const TemplateTypeGeneric TemplateType = "generic"

type GenericTemplate struct {
	// Title is limited to 45 characters
	Title    string `json:"title"`
	ItemURL  string `json:"item_url,omitempty"`
	ImageURL string `json:"image_url,omitempty"`
	DefaultActionGeneric `json:"default_action,omitempty"`
	// Subtitle is limited to 80 characters
	Subtitle string   `json:"subtitle,omitempty"`
	Buttons  []Button `json:"buttons,omitempty"`
}

type DefaultActionGeneric struct {
	Type string `json:"type"` // Must be "web_url"
	URL  string `json:"url",omitempty`
}

func (GenericTemplate) Type() TemplateType {
	return TemplateTypeGeneric
}

func (GenericTemplate) SupportsButtons() bool {
	return true
}<commit_msg>move type in DefaultActionGeneric from string to ButtonType<commit_after>package template

const TemplateTypeGeneric TemplateType = "generic"

type GenericTemplate struct {
	// Title is limited to 45 characters
	Title    string `json:"title"`
	ItemURL  string `json:"item_url,omitempty"`
	ImageURL string `json:"image_url,omitempty"`
	DefaultActionGeneric `json:"default_action,omitempty"`
	// Subtitle is limited to 80 characters
	Subtitle string   `json:"subtitle,omitempty"`
	Buttons  []Button `json:"buttons,omitempty"`
}

type DefaultActionGeneric struct {
	Type ButtonType `json:"type,omitempty"` // Must be "web_url"
	URL  string `json:"url,omitempty"`
}

func (GenericTemplate) Type() TemplateType {
	return TemplateTypeGeneric
}

func (GenericTemplate) SupportsButtons() bool {
	return true
}
<|endoftext|>


<commit_before>package client_test

import (
	"net/http"

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

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/onsi/gomega/ghttp"

	"github.com/tidepool-org/platform/log"
	"github.com/tidepool-org/platform/metricservices/client"
	userservicesClient "github.com/tidepool-org/platform/userservices/client"
	"github.com/tidepool-org/platform/version"
)

type TestAuthenticationDetails struct {
	TokenOutputs    []string
	IsServerOutputs []bool
}

func (t *TestAuthenticationDetails) Token() string {
	output := t.TokenOutputs[0]
	t.TokenOutputs = t.TokenOutputs[1:]
	return output
}

func (t *TestAuthenticationDetails) IsServer() bool {
	output := t.IsServerOutputs[0]
	t.IsServerOutputs = t.IsServerOutputs[1:]
	return output
}

func (t *TestAuthenticationDetails) UserID() string {
	panic("Unexpected invocation of UserID on TestAuthenticationDetails")
}

func (t *TestAuthenticationDetails) ValidateTest() bool {
	return len(t.TokenOutputs) == 0 && len(t.IsServerOutputs) == 0
}

type TestContext struct {
	TestLogger                log.Logger
	TestRequest               *rest.Request
	TestAuthenticationDetails *TestAuthenticationDetails
}

func (t *TestContext) Logger() log.Logger {
	return t.TestLogger
}

func (t *TestContext) Request() *rest.Request {
	return t.TestRequest
}

func (t *TestContext) AuthenticationDetails() userservicesClient.AuthenticationDetails {
	return t.TestAuthenticationDetails
}

func (t *TestContext) ValidateTest() bool {
	return t.TestAuthenticationDetails.ValidateTest()
}

var _ = Describe("Standard", func() {
	var versionReporter version.Reporter
	var context *TestContext

	BeforeEach(func() {
		var err error
		versionReporter, err = version.NewReporter("1.2.3", "4567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn")
		Expect(err).ToNot(HaveOccurred())
		Expect(versionReporter).ToNot(BeNil())
		context = &TestContext{
			TestLogger:                log.NewNull(),
			TestRequest:               &rest.Request{},
			TestAuthenticationDetails: &TestAuthenticationDetails{},
		}
	})

	Context("NewStandard", func() {
		var config *client.Config

		BeforeEach(func() {
			config = &client.Config{
				Address:        "http://localhost:1234",
				RequestTimeout: 30,
			}
		})

		It("returns an error if version reporter is missing", func() {
			standard, err := client.NewStandard(nil, "testservices", config)
			Expect(err).To(MatchError("client: version reporter is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if name is missing", func() {
			standard, err := client.NewStandard(versionReporter, "", config)
			Expect(err).To(MatchError("client: name is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if config is missing", func() {
			standard, err := client.NewStandard(versionReporter, "testservices", nil)
			Expect(err).To(MatchError("client: config is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if config address is invalid", func() {
			config.Address = ""
			standard, err := client.NewStandard(versionReporter, "testservices", config)
			Expect(err).To(MatchError("client: config is invalid; client: address is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if config request timeout is invalid", func() {
			config.RequestTimeout = -1
			standard, err := client.NewStandard(versionReporter, "testservices", config)
			Expect(err).To(MatchError("client: config is invalid; client: request timeout is invalid"))
			Expect(standard).To(BeNil())
		})

		It("returns success", func() {
			standard, err := client.NewStandard(versionReporter, "testservices", config)
			Expect(err).ToNot(HaveOccurred())
			Expect(standard).ToNot(BeNil())
		})
	})

	Context("with server", func() {
		var server *ghttp.Server
		var config *client.Config
		var standard *client.Standard
		var data map[string]string

		BeforeEach(func() {
			server = ghttp.NewServer()
			config = &client.Config{
				Address:        server.URL(),
				RequestTimeout: 30,
			}
			data = map[string]string{
				"left":  "handed",
				"right": "correct",
			}
		})

		JustBeforeEach(func() {
			var err error
			standard, err = client.NewStandard(versionReporter, "testservices", config)
			Expect(err).ToNot(HaveOccurred())
			Expect(standard).ToNot(BeNil())
		})

		AfterEach(func() {
			if server != nil {
				server.Close()
			}
		})

		Context("RecordMetric", func() {
			It("returns error if context is missing", func() {
				Expect(standard.RecordMetric(nil, "test-metric", data)).To(MatchError("client: context is missing"))
				Expect(server.ReceivedRequests()).To(BeEmpty())
				Expect(context.ValidateTest()).To(BeTrue())
			})

			It("returns error if metric is missing", func() {
				Expect(standard.RecordMetric(context, "", data)).To(MatchError("client: metric is missing"))
				Expect(server.ReceivedRequests()).To(BeEmpty())
				Expect(context.ValidateTest()).To(BeTrue())
			})

			It("returns error if the context request is missing", func() {
				context.TestAuthenticationDetails.IsServerOutputs = []bool{false}
				context.TestRequest = nil
				err := standard.RecordMetric(context, "test-metric", data)
				Expect(err).To(MatchError("client: unable to copy request trace; service: source request is missing"))
				Expect(server.ReceivedRequests()).To(BeEmpty())
				Expect(context.ValidateTest()).To(BeTrue())
			})

			It("returns error if the server is not reachable", func() {
				context.TestAuthenticationDetails.TokenOutputs = []string{"test-authentication-token"}
				context.TestAuthenticationDetails.IsServerOutputs = []bool{false}
				server.Close()
				server = nil
				err := standard.RecordMetric(context, "test-metric", data)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(HavePrefix("client: unable to perform request GET "))
				Expect(context.ValidateTest()).To(BeTrue())
			})

			Context("as user", func() {
				BeforeEach(func() {
					context.TestAuthenticationDetails.TokenOutputs = []string{"test-authentication-token"}
					context.TestAuthenticationDetails.IsServerOutputs = []bool{false}
				})

				Context("with an unexpected response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusBadRequest, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(HavePrefix("client: unexpected response status code 400 from GET "))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with an unauthorized response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusUnauthorized, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(MatchError("client: unauthorized"))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns the user id", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response without data", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns the user id", func() {
						err := standard.RecordMetric(context, "test-metric")
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})
			})

			Context("as server", func() {
				BeforeEach(func() {
					context.TestAuthenticationDetails.TokenOutputs = []string{"test-authentication-token"}
					context.TestAuthenticationDetails.IsServerOutputs = []bool{true}
				})

				Context("with an unexpected response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusBadRequest, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(HavePrefix("client: unexpected response status code 400 from GET "))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with an unauthorized response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusUnauthorized, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(MatchError("client: unauthorized"))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns the user id", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response without data", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns the user id", func() {
						err := standard.RecordMetric(context, "test-metric")
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})
			})
		})
	})
})
<commit_msg>Minor update to metric services test names<commit_after>package client_test

import (
	"net/http"

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

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/onsi/gomega/ghttp"

	"github.com/tidepool-org/platform/log"
	"github.com/tidepool-org/platform/metricservices/client"
	userservicesClient "github.com/tidepool-org/platform/userservices/client"
	"github.com/tidepool-org/platform/version"
)

type TestAuthenticationDetails struct {
	TokenOutputs    []string
	IsServerOutputs []bool
}

func (t *TestAuthenticationDetails) Token() string {
	output := t.TokenOutputs[0]
	t.TokenOutputs = t.TokenOutputs[1:]
	return output
}

func (t *TestAuthenticationDetails) IsServer() bool {
	output := t.IsServerOutputs[0]
	t.IsServerOutputs = t.IsServerOutputs[1:]
	return output
}

func (t *TestAuthenticationDetails) UserID() string {
	panic("Unexpected invocation of UserID on TestAuthenticationDetails")
}

func (t *TestAuthenticationDetails) ValidateTest() bool {
	return len(t.TokenOutputs) == 0 && len(t.IsServerOutputs) == 0
}

type TestContext struct {
	TestLogger                log.Logger
	TestRequest               *rest.Request
	TestAuthenticationDetails *TestAuthenticationDetails
}

func (t *TestContext) Logger() log.Logger {
	return t.TestLogger
}

func (t *TestContext) Request() *rest.Request {
	return t.TestRequest
}

func (t *TestContext) AuthenticationDetails() userservicesClient.AuthenticationDetails {
	return t.TestAuthenticationDetails
}

func (t *TestContext) ValidateTest() bool {
	return t.TestAuthenticationDetails.ValidateTest()
}

var _ = Describe("Standard", func() {
	var versionReporter version.Reporter
	var context *TestContext

	BeforeEach(func() {
		var err error
		versionReporter, err = version.NewReporter("1.2.3", "4567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn")
		Expect(err).ToNot(HaveOccurred())
		Expect(versionReporter).ToNot(BeNil())
		context = &TestContext{
			TestLogger:                log.NewNull(),
			TestRequest:               &rest.Request{},
			TestAuthenticationDetails: &TestAuthenticationDetails{},
		}
	})

	Context("NewStandard", func() {
		var config *client.Config

		BeforeEach(func() {
			config = &client.Config{
				Address:        "http://localhost:1234",
				RequestTimeout: 30,
			}
		})

		It("returns an error if version reporter is missing", func() {
			standard, err := client.NewStandard(nil, "testservices", config)
			Expect(err).To(MatchError("client: version reporter is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if name is missing", func() {
			standard, err := client.NewStandard(versionReporter, "", config)
			Expect(err).To(MatchError("client: name is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if config is missing", func() {
			standard, err := client.NewStandard(versionReporter, "testservices", nil)
			Expect(err).To(MatchError("client: config is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if config address is invalid", func() {
			config.Address = ""
			standard, err := client.NewStandard(versionReporter, "testservices", config)
			Expect(err).To(MatchError("client: config is invalid; client: address is missing"))
			Expect(standard).To(BeNil())
		})

		It("returns an error if config request timeout is invalid", func() {
			config.RequestTimeout = -1
			standard, err := client.NewStandard(versionReporter, "testservices", config)
			Expect(err).To(MatchError("client: config is invalid; client: request timeout is invalid"))
			Expect(standard).To(BeNil())
		})

		It("returns success", func() {
			standard, err := client.NewStandard(versionReporter, "testservices", config)
			Expect(err).ToNot(HaveOccurred())
			Expect(standard).ToNot(BeNil())
		})
	})

	Context("with server", func() {
		var server *ghttp.Server
		var config *client.Config
		var standard *client.Standard
		var data map[string]string

		BeforeEach(func() {
			server = ghttp.NewServer()
			config = &client.Config{
				Address:        server.URL(),
				RequestTimeout: 30,
			}
			data = map[string]string{
				"left":  "handed",
				"right": "correct",
			}
		})

		JustBeforeEach(func() {
			var err error
			standard, err = client.NewStandard(versionReporter, "testservices", config)
			Expect(err).ToNot(HaveOccurred())
			Expect(standard).ToNot(BeNil())
		})

		AfterEach(func() {
			if server != nil {
				server.Close()
			}
		})

		Context("RecordMetric", func() {
			It("returns error if context is missing", func() {
				Expect(standard.RecordMetric(nil, "test-metric", data)).To(MatchError("client: context is missing"))
				Expect(server.ReceivedRequests()).To(BeEmpty())
				Expect(context.ValidateTest()).To(BeTrue())
			})

			It("returns error if metric is missing", func() {
				Expect(standard.RecordMetric(context, "", data)).To(MatchError("client: metric is missing"))
				Expect(server.ReceivedRequests()).To(BeEmpty())
				Expect(context.ValidateTest()).To(BeTrue())
			})

			It("returns error if the context request is missing", func() {
				context.TestAuthenticationDetails.IsServerOutputs = []bool{false}
				context.TestRequest = nil
				err := standard.RecordMetric(context, "test-metric", data)
				Expect(err).To(MatchError("client: unable to copy request trace; service: source request is missing"))
				Expect(server.ReceivedRequests()).To(BeEmpty())
				Expect(context.ValidateTest()).To(BeTrue())
			})

			It("returns error if the server is not reachable", func() {
				context.TestAuthenticationDetails.TokenOutputs = []string{"test-authentication-token"}
				context.TestAuthenticationDetails.IsServerOutputs = []bool{false}
				server.Close()
				server = nil
				err := standard.RecordMetric(context, "test-metric", data)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(HavePrefix("client: unable to perform request GET "))
				Expect(context.ValidateTest()).To(BeTrue())
			})

			Context("as user", func() {
				BeforeEach(func() {
					context.TestAuthenticationDetails.TokenOutputs = []string{"test-authentication-token"}
					context.TestAuthenticationDetails.IsServerOutputs = []bool{false}
				})

				Context("with an unexpected response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusBadRequest, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(HavePrefix("client: unexpected response status code 400 from GET "))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with an unauthorized response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusUnauthorized, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(MatchError("client: unauthorized"))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns success", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response without data", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/thisuser/test-metric", "sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns success", func() {
						err := standard.RecordMetric(context, "test-metric")
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})
			})

			Context("as server", func() {
				BeforeEach(func() {
					context.TestAuthenticationDetails.TokenOutputs = []string{"test-authentication-token"}
					context.TestAuthenticationDetails.IsServerOutputs = []bool{true}
				})

				Context("with an unexpected response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusBadRequest, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(HaveOccurred())
						Expect(err.Error()).To(HavePrefix("client: unexpected response status code 400 from GET "))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with an unauthorized response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusUnauthorized, nil, nil)),
						)
					})

					It("returns an error", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).To(MatchError("client: unauthorized"))
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "left=handed&right=correct&sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns success", func() {
						err := standard.RecordMetric(context, "test-metric", data)
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})

				Context("with a successful response without data", func() {
					BeforeEach(func() {
						server.AppendHandlers(
							ghttp.CombineHandlers(
								ghttp.VerifyRequest("GET", "/metrics/server/testservices/test-metric", "sourceVersion=1.2.3"),
								ghttp.VerifyHeaderKV("X-Tidepool-Session-Token", "test-authentication-token"),
								ghttp.VerifyBody([]byte{}),
								ghttp.RespondWith(http.StatusOK, nil, nil)),
						)
					})

					It("returns success", func() {
						err := standard.RecordMetric(context, "test-metric")
						Expect(err).ToNot(HaveOccurred())
						Expect(server.ReceivedRequests()).To(HaveLen(1))
						Expect(context.ValidateTest()).To(BeTrue())
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"strings"
	"time"
)

const (
	MAVEN_JOB_FILE_PATH string = "./test-maven-job.xml"
	MAVEN_JOB_NAME      string = "mavenJob"
)

func iHaveAddedAMavenJob() error {
	body := createJenkinsJob(MAVEN_JOB_NAME, MAVEN_JOB_FILE_PATH)

	if body != "" {
		return fmt.Errorf("Body is not empty - does job already exist? %s", body)
	}

	return nil
}

func iExecuteTheMavenJob() error {
	req, err := http.NewRequest("POST", getJobBuildUrl(MAVEN_JOB_NAME), nil)
	getNewJenkinsCrumb()
	req.Header.Add(crumb.CrumbRequestField, crumb.Crumb)
	req.Header.Add("Content-Type", "application/json")

	resp, err := httpClient.Do(req)

	if err != nil {
		return fmt.Errorf("iExecuteTheMavenJob error: %s", err)
	}

	body, _ := getBodyString(resp)
	fmt.Printf("%s", body)

	return nil
}

func theJobArtefactIsStoredInNexus() error {
	time.Sleep(40 * time.Second)

	jobResp, err := httpClient.Get(getUrl("/job/" + MAVEN_JOB_NAME + "/1/api/json"))

	if err != nil {
		return fmt.Errorf("Failed to create job %s.", MAVEN_JOB_NAME)
	}

	if jobResp.StatusCode != http.StatusOK {
		return fmt.Errorf("theJobArtefactIsStoredInNexus status code returned %d not 200", jobResp.StatusCode)
	}

	body, _ := getBodyString(jobResp)

	if !strings.Contains(body, `"result":"SUCCESS"`) {
		return fmt.Errorf("theJobArtefactIsStoredInNexus failed: %s", body)
	}

	return nil
}
<commit_msg>added maven nexus integration test 2<commit_after>package main

import (
	"fmt"
	"net/http"
	"time"
)

const (
	MAVEN_JOB_FILE_PATH string = "./test-maven-job.xml"
	MAVEN_JOB_NAME      string = "mavenJob"
	NEXUS_ARTEFACT_URL  string = "https://nexus.dev-build-create.build.finkit.io/repository/releases-libs/com/example/demo/0.0.1/demo-0.0.1.jar"
)

func iHaveAddedAMavenJob() error {
	body := createJenkinsJob(MAVEN_JOB_NAME, MAVEN_JOB_FILE_PATH)

	if body != "" {
		return fmt.Errorf("Body is not empty - does job already exist? %s", body)
	}

	return nil
}

func iExecuteTheMavenJob() error {
	req, err := http.NewRequest("POST", getJobBuildUrl(MAVEN_JOB_NAME), nil)
	getNewJenkinsCrumb()
	req.Header.Add(crumb.CrumbRequestField, crumb.Crumb)
	req.Header.Add("Content-Type", "application/json")

	resp, err := httpClient.Do(req)

	if err != nil {
		return fmt.Errorf("iExecuteTheMavenJob error: %s", err)
	}

	body, _ := getBodyString(resp)
	fmt.Printf("%s", body)

	return nil
}

func theJobArtefactIsStoredInNexus() error {
	nexusResp, err := httpClient.Get(NEXUS_ARTEFACT_URL)

	fmt.Printf("nexus response from %s to %s .", NEXUS_ARTEFACT_URL, err)

	if nexusResp.StatusCode == http.StatusOK {
		return fmt.Errorf("returned 200 unexpectedly")
	}

	time.Sleep(40 * time.Second)

	nexusResp, err = httpClient.Get(NEXUS_ARTEFACT_URL)

	if err != nil {
		return fmt.Errorf("Failed to retrieve artefact from %s.", NEXUS_ARTEFACT_URL)
	}

	if nexusResp.StatusCode != http.StatusOK {
		return fmt.Errorf("theJobArtefactIsStoredInNexus status code returned %d not 200", nexusResp.StatusCode)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import "fmt"

// declare a variable, notice the use of "var" keyword
// un-initialized variable "City" is initialized to the zero value, in this case an empty string
// indentifiers that start with uppercase letter are exported .i.e. they are visible outside the
// package they are defined.
// addtionally the type of variable appears after the name, unlike Java, C, or C++
var City string

var a int
var c float64
var d bool

// you can group several variable, saves you from the repeating the "var" keyword
var (
	e    int64
	f, g uint32
	b    complex128
)

// declare and initialize several variable of different types
// note the Go complier was able to infer the types from their respective values
var age, height, hairColor, hasCar = 20, 66.0, "black", true

func main() {
	fmt.Printf("City=%s age=%d height=%.2f hairColor=%s hasCar=%t\n", City, age, height, hairColor, hasCar)
	fmt.Printf("City=%T age=%T height=%T hairColor=%T hasCar=%T\n", City, age, height, hairColor, hasCar)
	fmt.Printf("a=%#v b=%#v c=%#v d=%#v e=%#v f=%#v g=%#v\n", a, b, c, d, e, f, g)

	// this short hand for declaring and initializng the variable is only available within
	// function bodies
	name := "Penguin"
	canFly, avgLifespan := false, 15

	fmt.Println(name, canFly, avgLifespan)
}
<commit_msg>add commentary<commit_after>package main

import "fmt"

// declare a variable, notice the use of "var" keyword
// un-initialized variable "City" is initialized to the zero value,
// in this case an empty string indentifiers that start with uppercase
// letter are exported .i.e. they are visible outside the package they
// are defined. addtionally the type of variable appears after the
// name, unlike Java, C, or C++

// NOTE: Its complie time error to define a variable and not use it
var City string

var a int
var c float64
var d bool

// you can group several variable, saves you from the repeating the "var" keyword
var (
	e    int64
	f, g uint32
	b    complex128
)

// declare and initialize several variable of different types
// note the Go complier was able to infer the types from their respective values
var age, height, hairColor, hasCar = 20, 66.0, "black", true

func main() {
	fmt.Printf("City=%s age=%d height=%.2f hairColor=%s hasCar=%t\n", City, age, height, hairColor, hasCar)
	fmt.Printf("City=%T age=%T height=%T hairColor=%T hasCar=%T\n", City, age, height, hairColor, hasCar)
	fmt.Printf("a=%#v b=%#v c=%#v d=%#v e=%#v f=%#v g=%#v\n", a, b, c, d, e, f, g)

	// this short hand for declaring and initializng the variable is only available within
	// function bodies
	name := "Penguin"
	canFly, avgLifespan := false, 15

	fmt.Println(name, canFly, avgLifespan)
}
<|endoftext|>


<commit_before>package compress

import (
	"bytes"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/disorganizer/brig/util/testutil"
)

var (
	ZipFilePath = filepath.Join(os.TempDir(), "compressed.zip")
)

func openDest(t *testing.T, dest string) *os.File {
	if _, err := os.Stat(dest); !os.IsNotExist(err) {
		t.Fatalf("Opening destination %v failed: %v\n", dest, err)
	}
	fd, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
	if err != nil {
		t.Fatalf("Opening srouce %v failed: %v\n", dest, err)
	}
	return fd
}

func openSrc(t *testing.T, src string) *os.File {
	fd, err := os.Open(src)
	if err != nil {
		t.Fatalf("Opening srouce %v failed: %v\n", src, err)
	}
	return fd
}

const (
	C64K = 64 * 1024
	C32K = 32 * 1024
)

func TestCompressDecompress(t *testing.T) {
	sizes := []int64{0, 1, C64K - 1, C64K, C64K + 1, C32K - 1, C32K, C32K + 1}
	algos := []Algorithm{AlgoNone, AlgoSnappy}
	for _, algo := range algos {
		for _, size := range sizes {
			testCompressDecompress(t, size, algo)
		}
	}
}

func testCompressDecompress(t *testing.T, size int64, algo Algorithm) {
	// Fake data file is written to disk,
	// as compression reader has to be a ReadSeeker.
	data := testutil.CreateDummyBuf(size)
	zipFileDest := openDest(t, ZipFilePath)

	defer testutil.Remover(t, ZipFilePath)

	// Compress.
	w := NewWriter(zipFileDest, algo)
	if _, err := io.Copy(w, bytes.NewReader(data)); err != nil {
		t.Errorf("Compress failed %v", err)
		return
	}

	if err := w.Close(); err != nil {
		t.Errorf("Compression writer close failed: %v", err)
		return
	}

	if err := zipFileDest.Close(); err != nil {
		t.Errorf("close(zipFileDest) failed: %v", err)
		return
	}

	defer zipFileDest.Close()
	// Read compressed file into buffer.
	dataUncomp := bytes.NewBuffer(nil)
	dataFromZip := openSrc(t, ZipFilePath)

	// Uncompress.
	r := NewReader(dataFromZip)
	if _, err := io.Copy(dataUncomp, r); err != nil {
		t.Errorf("Decompression failed: %v", err)
		return
	}
	if err := dataFromZip.Close(); err != nil {
		t.Errorf("Zip close failed: %v", err)
		return
	}

	// Compare.
	if !bytes.Equal(dataUncomp.Bytes(), data) {
		t.Error("Uncompressed data and input data does not match.")
		return
	}
}

func TestSeek(t *testing.T) {
	sizes := []int64{0, 1, 5, 10, 100, 200, 5000, C64K, C32K}
	offsets := []int64{0, 1, 5, 10, 100, 200, 5000, C64K, C32K}
	for _, size := range sizes {
		for _, off := range offsets {
			testSeek(t, size, off)
		}
	}
}

func testSeek(t *testing.T, size, offset int64) {
	// Fake data file is written to disk,
	// as compression reader has to be a ReadSeeker.
	data := testutil.CreateDummyBuf(size)
	zipFileDest := openDest(t, ZipFilePath)

	// Compress.
	w := NewWriter(zipFileDest, AlgoSnappy)
	if _, err := io.Copy(w, bytes.NewReader(data)); err != nil {
		t.Errorf("Compress failed %v", err)
		return
	}
	defer testutil.Remover(t, ZipFilePath)

	if err := w.Close(); err != nil {
		t.Errorf("Compression writer close failed: %v", err)
		return
	}

	if err := zipFileDest.Close(); err != nil {
		t.Errorf("close(zipFileDest) failed: %v", err)
		return
	}

	// Read compressed file into buffer.
	dataUncomp := bytes.NewBuffer(nil)
	dataFromZip := openSrc(t, ZipFilePath)
	zr := NewReader(dataFromZip)

	// Set specific offset before read.
	_, err := zr.Seek(offset, os.SEEK_SET)
	if err == io.EOF && offset <= size {
		t.Errorf("Seek failed: %v", err)
		return
	}
	if err != io.EOF && err != nil {
		t.Errorf("Seek failed: %v", err)
		return
	}

	// Read starting at a specific offset.
	if _, err := io.Copy(dataUncomp, zr); err != nil {
		t.Errorf("Decompression failed: %v", err)
		return
	}
	if err := dataFromZip.Close(); err != nil {
		t.Errorf("Zip close failed: %v", err)
		return
	}

	// Set specific offset on raw data.
	dataRaw := bytes.NewBuffer(nil)
	rr := bytes.NewReader(data)
	_, err = rr.Seek(offset, os.SEEK_SET)
	if err != nil {
		t.Errorf("Seek failed: %v", err)
		return
	}
	// Read raw data at specific offset.
	if _, err := io.Copy(dataRaw, rr); err != nil {
		t.Errorf("Decompression failed: %v", err)
		return
	}

	// Compare.
	if !bytes.Equal(dataUncomp.Bytes(), dataRaw.Bytes()) {
		t.Error("Uncompressed data and input data does not match.")
		return
	}
}
<commit_msg>store/compare_test.go: Testcases and values updated.<commit_after>package compress

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/disorganizer/brig/util/testutil"
)

var (
	ZipFilePath = filepath.Join(os.TempDir(), "compressed.zip")
	TestOffsets = []int64{-1, -500, 0, 1, -C64K, -C32K, C64K - 1, C64K, C64K + 1, C32K - 1, C32K, C32K + 1, C64K - 5, C64K + 5, C32K - 5, C32K + 5}
	TestSizes   = []int64{0, 1, C64K - 1, C64K, C64K + 1, C32K - 1, C32K, C32K + 1, C64K - 5, C64K + 5, C32K - 5, C32K + 5}
)

func openDest(t *testing.T, dest string) *os.File {
	if _, err := os.Stat(dest); !os.IsNotExist(err) {
		t.Fatalf("Opening destination %v failed: %v\n", dest, err)
	}
	fd, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
	if err != nil {
		t.Fatalf("Opening srouce %v failed: %v\n", dest, err)
	}
	return fd
}

func openSrc(t *testing.T, src string) *os.File {
	fd, err := os.Open(src)
	if err != nil {
		t.Fatalf("Opening srouce %v failed: %v\n", src, err)
	}
	return fd
}

const (
	C64K = 64 * 1024
	C32K = 32 * 1024
)

func TestCompressDecompress(t *testing.T) {
	sizes := TestSizes
	algos := []Algorithm{AlgoNone, AlgoSnappy}
	for _, algo := range algos {
		for _, size := range sizes {
			testCompressDecompress(t, size, algo)
		}
	}
}

func testCompressDecompress(t *testing.T, size int64, algo Algorithm) {
	// Fake data file is written to disk,
	// as compression reader has to be a ReadSeeker.
	testutil.Remover(t, ZipFilePath)
	data := testutil.CreateDummyBuf(size)
	zipFileDest := openDest(t, ZipFilePath)

	defer testutil.Remover(t, ZipFilePath)

	// Compress.
	w := NewWriter(zipFileDest, algo)
	if _, err := io.Copy(w, bytes.NewReader(data)); err != nil {
		t.Errorf("Compress failed %v", err)
		return
	}

	if err := w.Close(); err != nil {
		t.Errorf("Compression writer close failed: %v", err)
		return
	}

	if err := zipFileDest.Close(); err != nil {
		t.Errorf("close(zipFileDest) failed: %v", err)
		return
	}

	// Read compressed file into buffer.
	dataUncomp := bytes.NewBuffer(nil)
	dataFromZip := openSrc(t, ZipFilePath)

	// Uncompress.
	r := NewReader(dataFromZip)
	if _, err := io.Copy(dataUncomp, r); err != nil {
		t.Errorf("Decompression failed: %v", err)
		return
	}
	if err := dataFromZip.Close(); err != nil {
		t.Errorf("Zip close failed: %v", err)
		return
	}

	// Compare.
	if !bytes.Equal(dataUncomp.Bytes(), data) {
		t.Error("Uncompressed data and input data does not match.")
		return
	}
}

func TestSeek(t *testing.T) {
	sizes := TestSizes
	offsets := TestOffsets
	for _, size := range sizes {
		for _, off := range offsets {
			testSeek(t, size, off)
		}
	}
}

func omit(data []byte, lim int) string {
	lo := lim
	if lo > len(data) {
		lo = len(data)
	}

	hi := len(data) - lim
	if hi < 0 {
		hi = len(data)
	}

	return fmt.Sprintf("%v ... %v", data[:lo], data[hi:])
}

func testSeek(t *testing.T, size, offset int64) {
	// Fake data file is written to disk,
	// as compression reader has to be a ReadSeeker.
	testutil.Remover(t, ZipFilePath)
	data := testutil.CreateDummyBuf(size)
	zipFileDest := openDest(t, ZipFilePath)

	// Compress.
	w := NewWriter(zipFileDest, AlgoSnappy)
	if _, err := io.Copy(w, bytes.NewReader(data)); err != nil {
		t.Errorf("Compress failed %v", err)
		return
	}
	defer testutil.Remover(t, ZipFilePath)

	if err := w.Close(); err != nil {
		t.Errorf("Compression writer close failed: %v", err)
		return
	}

	if err := zipFileDest.Close(); err != nil {
		t.Errorf("close(zipFileDest) failed: %v", err)
		return
	}

	// Read compressed file into buffer.
	dataUncomp := bytes.NewBuffer(nil)
	dataFromZip := openSrc(t, ZipFilePath)
	zr := NewReader(dataFromZip)

	// Set specific offset before read.
	_, err := zr.Seek(offset, os.SEEK_SET)
	if err == io.EOF && offset < size && offset > -1 {
		t.Errorf("Seek failed even with EOF: %d <= %d", offset, size)
		return
	}
	if err != io.EOF && err != nil {
		t.Errorf("Seek failed: %v", err)
		return
	}

	// Read starting at a specific offset.
	if _, err := io.Copy(dataUncomp, zr); err != nil {
		t.Errorf("Decompression failed: %v", err)
		return
	}
	if err := dataFromZip.Close(); err != nil {
		t.Errorf("Zip close failed: %v", err)
		return
	}

	// Compare.
	maxOffset := offset
	if offset > size {
		maxOffset = size
	}

	if offset < 0 {
		maxOffset = 0
	}

	got, want := dataUncomp.Bytes(), data[maxOffset:]
	if !bytes.Equal(got, want) {
		t.Error("Uncompressed data and input data does not match.")
		t.Errorf("\tGOT:   %v", omit(got, 10))
		t.Errorf("\tWANT:  %v", omit(want, 10))
		return
	}
}
<|endoftext|>


<commit_before>package stringutils

import "testing"
import "strings"
import "github.com/stretchr/testify/assert"

// TestTransform is a unit test.
func TestTransform(t *testing.T) {
	strs := []string{"a", "b", "c"}
	allupper := Transform(strs, strings.ToUpper)
	assert.Len(t, allupper, len(strs))
	assert.Equal(t, "A", allupper[0])
	assert.Equal(t, "B", allupper[1])
	assert.Equal(t, "C", allupper[2])
}

// TestQuote is a unit test.
func TestQuote(t *testing.T) {
	strs := []string{"a", "b", "c"}
	quoted := Quote(strs)
	assert.Len(t, quoted, len(strs))
	assert.Equal(t, "\"a\"", quoted[0])
	assert.Equal(t, "\"b\"", quoted[1])
	assert.Equal(t, "\"c\"", quoted[2])
}

// TestDefaultValue is a unit test.
func TestDefaultValue(t *testing.T) {
	assert.Equal(t, "AA", DefaultIfEmpty("AA", "BB"))
	assert.Equal(t, "BB", DefaultIfEmpty("", "BB"))
	assert.Equal(t, "", DefaultIfEmpty("", ""))
	var a string
	assert.Equal(t, "XX", DefaultIfEmpty(a, "XX"))
}
<commit_msg>add godoc<commit_after>package stringutils

import "testing"
import "strings"
import "github.com/stretchr/testify/assert"

// TestTransform tests the tranformation by transforming strings to uppercase.
func TestTransform(t *testing.T) {
	strs := []string{"a", "b", "c"}
	allupper := Transform(strs, strings.ToUpper)
	assert.Len(t, allupper, len(strs))
	assert.Equal(t, "A", allupper[0])
	assert.Equal(t, "B", allupper[1])
	assert.Equal(t, "C", allupper[2])
}

// TestQuote tests quoting of strings.
func TestQuote(t *testing.T) {
	strs := []string{"a", "b", "c"}
	quoted := Quote(strs)
	assert.Len(t, quoted, len(strs))
	assert.Equal(t, "\"a\"", quoted[0])
	assert.Equal(t, "\"b\"", quoted[1])
	assert.Equal(t, "\"c\"", quoted[2])
}

// TestDefaultIfEmpty tests several constallations of default value returns.
func TestDefaultIfEmpty(t *testing.T) {
	assert.Equal(t, "AA", DefaultIfEmpty("AA", "BB"))
	assert.Equal(t, "BB", DefaultIfEmpty("", "BB"))
	assert.Equal(t, "", DefaultIfEmpty("", ""))
	var a string
	assert.Equal(t, "XX", DefaultIfEmpty(a, "XX"))
}
<|endoftext|>


<commit_before>/**
 *  Copyright 2014 Paul Querna
 *
 *  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 supportscolor

import (
	"github.com/mattn/go-isatty"
	"os"
	"regexp"
)

func stringInArgs(needle string, haystack []string) bool {
	for _, v := range haystack {
		if v == needle {
			return true
		}

		// if we see a --, assume we are chain calling into
		// another program, and we don't want to assume we can
		// interpert it.
		if v == "--" {
			break
		}
	}

	return false
}

func termSupportsColor(term string) bool {
	if term == "dumb" {
		return false
	}

	rv, err := regexp.MatchString("(^screen|^xterm|^vt100|color|ansi|cygwin|linux)", term)
	if err != nil {
		return false
	}

	return rv
}

func SupportsColor() bool {
	if stringInArgs("--no-color", os.Args) {
		return false
	}

	if stringInArgs("--color", os.Args) {
		return true
	}

	if !isatty.IsTerminal(os.Stdout.Fd()) {
		return false
	}

	if os.Getenv("COLORTERM") != "" {
		return true
	}

	var term = os.Getenv("TERM")

	return termSupportsColor(term)
}
<commit_msg>Add basic godocs<commit_after>/**
 *  Copyright 2014 Paul Querna
 *
 *  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 supportscolor

import (
	"github.com/mattn/go-isatty"
	"os"
	"regexp"
)

func stringInArgs(needle string, haystack []string) bool {
	for _, v := range haystack {
		if v == needle {
			return true
		}

		// if we see a --, assume we are chain calling into
		// another program, and we don't want to assume we can
		// interpert it.
		if v == "--" {
			break
		}
	}

	return false
}

func termSupportsColor(term string) bool {
	if term == "dumb" {
		return false
	}

	rv, err := regexp.MatchString("(^screen|^xterm|^vt100|color|ansi|cygwin|linux)", term)
	if err != nil {
		return false
	}

	return rv
}

/*
Function determines if the current process should support colors in the
terminal, and returns a `bool` verdict.

If the process is passed `--no-color` or `--color` it will override the
automatic detection.

SupportsColor detects:
	- stdout is not a TTY
	- COLORTERM enviroment variable is set.
	- TERM enviroment variable is set to DUMB
	- TERM enviroment variable is set to a reasonable value.

*/
func SupportsColor() bool {
	if stringInArgs("--no-color", os.Args) {
		return false
	}

	if stringInArgs("--color", os.Args) {
		return true
	}

	if !isatty.IsTerminal(os.Stdout.Fd()) {
		return false
	}

	if os.Getenv("COLORTERM") != "" {
		return true
	}

	var term = os.Getenv("TERM")

	return termSupportsColor(term)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 lease_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"strings"
	"testing"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/lease"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestMultiReadProxy(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A ReadProxy that wraps another, calling CheckInvariants before and after
// each action.
type checkingReadProxy struct {
	Wrapped lease.ReadProxy
}

func (crp *checkingReadProxy) Size() (size int64) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	size = crp.Wrapped.Size()
	return
}

func (crp *checkingReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	n, err = crp.Wrapped.ReadAt(ctx, p, off)
	return
}

func (crp *checkingReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	rwl, err = crp.Wrapped.Upgrade(ctx)
	return
}

func (crp *checkingReadProxy) Destroy() {
	crp.Wrapped.CheckInvariants()
	crp.Wrapped.Destroy()
}

func (crp *checkingReadProxy) CheckInvariants() {
	crp.Wrapped.CheckInvariants()
}

// A range to read, the contents we expect to get back, and a matcher for the
// returned error. Special case: a nil matcher means Equals(nil).
type readAtTestCase struct {
	start            int64
	limit            int64
	errMatcher       Matcher
	expectedContents string
}

func runReadAtTestCases(
	rp lease.ReadProxy,
	cases []readAtTestCase) {
	for i, tc := range cases {
		desc := fmt.Sprintf("Test case %d: [%d, %d)", i, tc.start, tc.limit)

		AssertLe(tc.start, tc.limit)
		buf := make([]byte, tc.limit-tc.start)

		n, err := rp.ReadAt(context.Background(), buf, tc.start)
		AssertEq(tc.expectedContents, string(buf[:n]), "%s", desc)

		if tc.errMatcher == nil {
			AssertEq(nil, err, "%s", desc)
		} else {
			ExpectThat(err, tc.errMatcher, desc)
		}
	}
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type MultiReadProxyTest struct {
	ctx context.Context

	// Canned content and errors returned by the refreshers.
	refresherContents []string
	refresherErrors   []error

	leaser lease.FileLeaser
	proxy  *checkingReadProxy
}

var _ SetUpInterface = &MultiReadProxyTest{}
var _ TearDownInterface = &MultiReadProxyTest{}

func init() { RegisterTestSuite(&MultiReadProxyTest{}) }

func (t *MultiReadProxyTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
	t.leaser = lease.NewFileLeaser("", math.MaxInt64)

	// Set up default refresher contents and nil errors.
	t.refresherContents = []string{
		"taco",
		"burrito",
		"enchilada",
	}
	t.refresherErrors = make([]error, len(t.refresherContents))

	// Create the proxy.
	t.proxy = &checkingReadProxy{
		Wrapped: lease.NewMultiReadProxy(
			t.leaser,
			t.makeRefreshers(),
			nil),
	}
}

func (t *MultiReadProxyTest) TearDown() {
	// Make sure nothing goes crazy.
	t.proxy.Destroy()
}

// Create refreshers based on the current contents of t.refresherContents.
// t.refresherErrors will be inspected only when Refresh is called.
func (t *MultiReadProxyTest) makeRefreshers() (refreshers []lease.Refresher) {
	for i := range t.refresherContents {
		iCopy := i
		contents := t.refresherContents[i]

		r := &funcRefresher{
			N: int64(len(contents)),
			F: func(ctx context.Context) (rc io.ReadCloser, err error) {
				rc = ioutil.NopCloser(strings.NewReader(contents))
				err = t.refresherErrors[iCopy]
				return
			},
		}

		refreshers = append(refreshers, r)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *MultiReadProxyTest) SizeZero_NoRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) SizeZero_WithRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Size() {
	var expected int64
	for _, contents := range t.refresherContents {
		expected += int64(len(contents))
	}

	ExpectEq(expected, t.proxy.Size())
}

func (t *MultiReadProxyTest) ReadAt_NegativeOffset() {
	// Test cases.
	m := Error(HasSubstr("Invalid offset"))
	testCases := []readAtTestCase{
		readAtTestCase{-1, 0, m, ""},
		readAtTestCase{-1, 1, m, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_OneRefresherReturnsError() {
	AssertThat(
		t.refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(t.refresherContents[0]))
	AssertEq(7, len(t.refresherContents[1]))
	AssertEq(9, len(t.refresherContents[2]))

	// Configure an error for the middle read lease.
	someErr := errors.New("foobar")
	t.refresherErrors[1] = someErr

	// Test cases.
	someErrMatcher := Error(HasSubstr(someErr.Error()))
	eofMatcher := Equals(io.EOF)

	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, someErrMatcher, "taco"},
		readAtTestCase{1, 11, someErrMatcher, "aco"},

		// All read leases.
		readAtTestCase{0, 20, someErrMatcher, "taco"},
		readAtTestCase{1, 20, someErrMatcher, "aco"},
		readAtTestCase{1, 100, someErrMatcher, "aco"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, someErrMatcher, ""},
		readAtTestCase{4, 11, someErrMatcher, ""},

		// Second and third read leases.
		readAtTestCase{4, 12, someErrMatcher, ""},
		readAtTestCase{4, 20, someErrMatcher, ""},
		readAtTestCase{5, 100, someErrMatcher, ""},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, eofMatcher, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, eofMatcher, ""},

		// Past end.
		readAtTestCase{21, 21, eofMatcher, ""},
		readAtTestCase{21, 22, eofMatcher, ""},
		readAtTestCase{21, 100, eofMatcher, ""},
		readAtTestCase{100, 1000, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_AllSuccessful() {
	AssertThat(
		t.refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(t.refresherContents[0]))
	AssertEq(7, len(t.refresherContents[1]))
	AssertEq(9, len(t.refresherContents[2]))

	// Test cases.
	eofMatcher := Equals(io.EOF)
	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, nil, "tacob"},
		readAtTestCase{1, 11, nil, "acoburrito"},

		// All read leases.
		readAtTestCase{0, 20, nil, "tacoburritoenchilada"},
		readAtTestCase{1, 19, nil, "acoburritoenchilad"},
		readAtTestCase{3, 17, nil, "oburritoenchil"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, nil, "b"},
		readAtTestCase{4, 11, nil, "burrito"},

		// Second and third read leases.
		readAtTestCase{4, 12, nil, "burritoe"},
		readAtTestCase{4, 20, nil, "burritoenchilada"},
		readAtTestCase{5, 100, eofMatcher, "urritoenchilada"},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, eofMatcher, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, eofMatcher, ""},

		// Past end.
		readAtTestCase{21, 21, eofMatcher, ""},
		readAtTestCase{21, 22, eofMatcher, ""},
		readAtTestCase{21, 100, eofMatcher, ""},
		readAtTestCase{100, 1000, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_ContentAlreadyCached() {
	AssertThat(
		t.refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	// Read the entire contents, causing read leases to be issued for each
	// sub-proxy.
	buf := make([]byte, 1024)
	n, err := t.proxy.ReadAt(context.Background(), buf, 0)

	AssertThat(err, AnyOf(nil, io.EOF))
	AssertEq("tacoburritoenchilada", string(buf[:n]))

	// Set up all refreshers to return errors when invoked.
	for i, _ := range t.refresherErrors {
		t.refresherErrors[i] = errors.New("foo")
	}

	// Despite this, the content should still be available.
	n, err = t.proxy.ReadAt(context.Background(), buf, 0)

	AssertThat(err, AnyOf(nil, io.EOF))
	AssertEq("tacoburritoenchilada", string(buf[:n]))
}

func (t *MultiReadProxyTest) Upgrade_OneRefresherReturnsError() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_AllSuccessful() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_ContentAlreadyCached() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseValid() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseRevoked() {
	AssertTrue(false, "TODO")
}
<commit_msg>MultiReadProxyTest.SizeZero_NoRefreshers<commit_after>// Copyright 2015 Google Inc. 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 lease_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"strings"
	"testing"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/lease"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestMultiReadProxy(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A ReadProxy that wraps another, calling CheckInvariants before and after
// each action.
type checkingReadProxy struct {
	Wrapped lease.ReadProxy
}

func (crp *checkingReadProxy) Size() (size int64) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	size = crp.Wrapped.Size()
	return
}

func (crp *checkingReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	n, err = crp.Wrapped.ReadAt(ctx, p, off)
	return
}

func (crp *checkingReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	rwl, err = crp.Wrapped.Upgrade(ctx)
	return
}

func (crp *checkingReadProxy) Destroy() {
	crp.Wrapped.CheckInvariants()
	crp.Wrapped.Destroy()
}

func (crp *checkingReadProxy) CheckInvariants() {
	crp.Wrapped.CheckInvariants()
}

// A range to read, the contents we expect to get back, and a matcher for the
// returned error. Special case: a nil matcher means Equals(nil).
type readAtTestCase struct {
	start            int64
	limit            int64
	errMatcher       Matcher
	expectedContents string
}

func runReadAtTestCases(
	rp lease.ReadProxy,
	cases []readAtTestCase) {
	for i, tc := range cases {
		desc := fmt.Sprintf("Test case %d: [%d, %d)", i, tc.start, tc.limit)

		AssertLe(tc.start, tc.limit)
		buf := make([]byte, tc.limit-tc.start)

		n, err := rp.ReadAt(context.Background(), buf, tc.start)
		AssertEq(tc.expectedContents, string(buf[:n]), "%s", desc)

		if tc.errMatcher == nil {
			AssertEq(nil, err, "%s", desc)
		} else {
			ExpectThat(err, tc.errMatcher, desc)
		}
	}
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type MultiReadProxyTest struct {
	ctx context.Context

	// Canned content and errors returned by the refreshers.
	refresherContents []string
	refresherErrors   []error

	leaser lease.FileLeaser
	proxy  *checkingReadProxy
}

var _ SetUpInterface = &MultiReadProxyTest{}
var _ TearDownInterface = &MultiReadProxyTest{}

func init() { RegisterTestSuite(&MultiReadProxyTest{}) }

func (t *MultiReadProxyTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
	t.leaser = lease.NewFileLeaser("", math.MaxInt64)

	// Set up default refresher contents and nil errors.
	t.refresherContents = []string{
		"taco",
		"burrito",
		"enchilada",
	}
	t.refresherErrors = make([]error, len(t.refresherContents))

	// Create the proxy.
	t.resetProxy()
}

func (t *MultiReadProxyTest) TearDown() {
	// Make sure nothing goes crazy.
	t.proxy.Destroy()
}

// Recreate refreshers using makeRefreshers and reset the proxy.
func (t *MultiReadProxyTest) resetProxy() {
	t.proxy = &checkingReadProxy{
		Wrapped: lease.NewMultiReadProxy(
			t.leaser,
			t.makeRefreshers(),
			nil),
	}
}

// Create refreshers based on the current contents of t.refresherContents.
// t.refresherErrors will be inspected only when Refresh is called.
func (t *MultiReadProxyTest) makeRefreshers() (refreshers []lease.Refresher) {
	for i := range t.refresherContents {
		iCopy := i
		contents := t.refresherContents[i]

		r := &funcRefresher{
			N: int64(len(contents)),
			F: func(ctx context.Context) (rc io.ReadCloser, err error) {
				rc = ioutil.NopCloser(strings.NewReader(contents))
				err = t.refresherErrors[iCopy]
				return
			},
		}

		refreshers = append(refreshers, r)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *MultiReadProxyTest) SizeZero_NoRefreshers() {
	t.refresherContents = []string{}
	t.refresherErrors = []error{}
	t.resetProxy()

	// Size
	ExpectEq(0, t.proxy.Size())

	// ReadAt
	eofMatcher := Equals(io.EOF)
	testCases := []readAtTestCase{
		readAtTestCase{0, 0, eofMatcher, ""},
		readAtTestCase{0, 10, eofMatcher, ""},
		readAtTestCase{5, 10, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) SizeZero_WithRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Size() {
	var expected int64
	for _, contents := range t.refresherContents {
		expected += int64(len(contents))
	}

	ExpectEq(expected, t.proxy.Size())
}

func (t *MultiReadProxyTest) ReadAt_NegativeOffset() {
	// Test cases.
	m := Error(HasSubstr("Invalid offset"))
	testCases := []readAtTestCase{
		readAtTestCase{-1, 0, m, ""},
		readAtTestCase{-1, 1, m, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_OneRefresherReturnsError() {
	AssertThat(
		t.refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(t.refresherContents[0]))
	AssertEq(7, len(t.refresherContents[1]))
	AssertEq(9, len(t.refresherContents[2]))

	// Configure an error for the middle read lease.
	someErr := errors.New("foobar")
	t.refresherErrors[1] = someErr

	// Test cases.
	someErrMatcher := Error(HasSubstr(someErr.Error()))
	eofMatcher := Equals(io.EOF)

	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, someErrMatcher, "taco"},
		readAtTestCase{1, 11, someErrMatcher, "aco"},

		// All read leases.
		readAtTestCase{0, 20, someErrMatcher, "taco"},
		readAtTestCase{1, 20, someErrMatcher, "aco"},
		readAtTestCase{1, 100, someErrMatcher, "aco"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, someErrMatcher, ""},
		readAtTestCase{4, 11, someErrMatcher, ""},

		// Second and third read leases.
		readAtTestCase{4, 12, someErrMatcher, ""},
		readAtTestCase{4, 20, someErrMatcher, ""},
		readAtTestCase{5, 100, someErrMatcher, ""},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, eofMatcher, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, eofMatcher, ""},

		// Past end.
		readAtTestCase{21, 21, eofMatcher, ""},
		readAtTestCase{21, 22, eofMatcher, ""},
		readAtTestCase{21, 100, eofMatcher, ""},
		readAtTestCase{100, 1000, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_AllSuccessful() {
	AssertThat(
		t.refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(t.refresherContents[0]))
	AssertEq(7, len(t.refresherContents[1]))
	AssertEq(9, len(t.refresherContents[2]))

	// Test cases.
	eofMatcher := Equals(io.EOF)
	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, nil, "tacob"},
		readAtTestCase{1, 11, nil, "acoburrito"},

		// All read leases.
		readAtTestCase{0, 20, nil, "tacoburritoenchilada"},
		readAtTestCase{1, 19, nil, "acoburritoenchilad"},
		readAtTestCase{3, 17, nil, "oburritoenchil"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, nil, "b"},
		readAtTestCase{4, 11, nil, "burrito"},

		// Second and third read leases.
		readAtTestCase{4, 12, nil, "burritoe"},
		readAtTestCase{4, 20, nil, "burritoenchilada"},
		readAtTestCase{5, 100, eofMatcher, "urritoenchilada"},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, eofMatcher, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, eofMatcher, ""},

		// Past end.
		readAtTestCase{21, 21, eofMatcher, ""},
		readAtTestCase{21, 22, eofMatcher, ""},
		readAtTestCase{21, 100, eofMatcher, ""},
		readAtTestCase{100, 1000, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_ContentAlreadyCached() {
	AssertThat(
		t.refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	// Read the entire contents, causing read leases to be issued for each
	// sub-proxy.
	buf := make([]byte, 1024)
	n, err := t.proxy.ReadAt(context.Background(), buf, 0)

	AssertThat(err, AnyOf(nil, io.EOF))
	AssertEq("tacoburritoenchilada", string(buf[:n]))

	// Set up all refreshers to return errors when invoked.
	for i, _ := range t.refresherErrors {
		t.refresherErrors[i] = errors.New("foo")
	}

	// Despite this, the content should still be available.
	n, err = t.proxy.ReadAt(context.Background(), buf, 0)

	AssertThat(err, AnyOf(nil, io.EOF))
	AssertEq("tacoburritoenchilada", string(buf[:n]))
}

func (t *MultiReadProxyTest) Upgrade_OneRefresherReturnsError() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_AllSuccessful() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_ContentAlreadyCached() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseValid() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseRevoked() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package status

import (
	"expvar"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strings"
	"time"
)

// Service provides HTTP status service.
type Service struct {
	addr string       // Bind address of the HTTP service.
	ln   net.Listener // Service listener

	start time.Time // Start up time.

	BuildInfo map[string]interface{}

	logger *log.Logger
}

// NewService returns an initialized Service object.
func NewService(addr string) *Service {
	return &Service{
		addr:   addr,
		store:  store,
		start:  time.Now(),
		logger: log.New(os.Stderr, "[status] ", log.LstdFlags),
	}
}

// Start starts the service.
func (s *Service) Start() error {
	server := http.Server{
		Handler: s,
	}

	ln, err = net.Listen("tcp", s.addr)
	if err != nil {
		return err
	}
	s.ln = ln

	go func() {
		err := server.Serve(s.ln)
		if err != nil {
			s.logger.Println("HTTP service Serve() returned:", err.Error())
		}
	}()
	s.logger.Println("service listening on", s.addr)

	return nil
}

// Close closes the service.
func (s *Service) Close() {
	s.ln.Close()
	return
}

// Addr returns the address on which the Service is listening
func (s *Service) Addr() net.Addr {
	return s.ln.Addr()
}

// ServeHTTP allows Service to serve HTTP requests.
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Add version header to every response, if available.
	if v, ok := s.BuildInfo["version"].(string); ok {
		w.Header().Add("X-EKANITE-VERSION", v)
	} else {
		w.Header().Add("X-EKANITE-VERSION", "unknown")
	}

	switch {
	case strings.HasPrefix(r.URL.Path, "/status"):
		s.handleStatus(w, r)
	case r.URL.Path == "/debug/vars" && s.Expvar:
		serveExpvar(w, r)
	case strings.HasPrefix(r.URL.Path, "/debug/pprof") && s.Pprof:
		servePprof(w, r)
	default:
		w.WriteHeader(http.StatusNotFound)
	}
}

// handleStatus returns status on the system.
func (s *Service) handleStatus(w http.ResponseWriter, r *http.Request) {
}

// serveExpvar serves registered expvar information over HTTP.
func serveExpvar(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	fmt.Fprintf(w, "{\n")
	first := true
	expvar.Do(func(kv expvar.KeyValue) {
		if !first {
			fmt.Fprintf(w, ",\n")
		}
		first = false
		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
	})
	fmt.Fprintf(w, "\n}\n")
}

// servePprof serves pprof information over HTTP.
func servePprof(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/debug/pprof/cmdline":
		pprof.Cmdline(w, r)
	case "/debug/pprof/profile":
		pprof.Profile(w, r)
	case "/debug/pprof/symbol":
		pprof.Symbol(w, r)
	default:
		pprof.Index(w, r)
	}
}
<commit_msg>New service now compiles<commit_after>package status

import (
	"expvar"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strings"
	"time"
)

// Service provides HTTP status service.
type Service struct {
	addr string       // Bind address of the HTTP service.
	ln   net.Listener // Service listener

	start time.Time // Start up time.

	BuildInfo map[string]interface{}

	logger *log.Logger
}

// NewService returns an initialized Service object.
func NewService(addr string) *Service {
	return &Service{
		addr:   addr,
		start:  time.Now(),
		logger: log.New(os.Stderr, "[status] ", log.LstdFlags),
	}
}

// Start starts the service.
func (s *Service) Start() error {
	server := http.Server{
		Handler: s,
	}

	ln, err := net.Listen("tcp", s.addr)
	if err != nil {
		return err
	}
	s.ln = ln

	go func() {
		err := server.Serve(s.ln)
		if err != nil {
			s.logger.Println("HTTP service Serve() returned:", err.Error())
		}
	}()
	s.logger.Println("service listening on", s.addr)

	return nil
}

// Close closes the service.
func (s *Service) Close() {
	s.ln.Close()
	return
}

// Addr returns the address on which the Service is listening
func (s *Service) Addr() net.Addr {
	return s.ln.Addr()
}

// ServeHTTP allows Service to serve HTTP requests.
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Add version header to every response, if available.
	if v, ok := s.BuildInfo["version"].(string); ok {
		w.Header().Add("X-EKANITE-VERSION", v)
	} else {
		w.Header().Add("X-EKANITE-VERSION", "unknown")
	}

	switch {
	case strings.HasPrefix(r.URL.Path, "/status"):
		s.handleStatus(w, r)
	case r.URL.Path == "/debug/vars":
		serveExpvar(w, r)
	case strings.HasPrefix(r.URL.Path, "/debug/pprof"):
		servePprof(w, r)
	default:
		w.WriteHeader(http.StatusNotFound)
	}
}

// handleStatus returns status on the system.
func (s *Service) handleStatus(w http.ResponseWriter, r *http.Request) {
}

// serveExpvar serves registered expvar information over HTTP.
func serveExpvar(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	fmt.Fprintf(w, "{\n")
	first := true
	expvar.Do(func(kv expvar.KeyValue) {
		if !first {
			fmt.Fprintf(w, ",\n")
		}
		first = false
		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
	})
	fmt.Fprintf(w, "\n}\n")
}

// servePprof serves pprof information over HTTP.
func servePprof(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/debug/pprof/cmdline":
		pprof.Cmdline(w, r)
	case "/debug/pprof/profile":
		pprof.Profile(w, r)
	case "/debug/pprof/symbol":
		pprof.Symbol(w, r)
	default:
		pprof.Index(w, r)
	}
}
<|endoftext|>