<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package stream

import (
	"errors"

	"testing"

	"github.com/getgauge/gauge/execution/event"
	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	gm "github.com/getgauge/gauge/gauge_messages"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestGetStatusForFailedScenario(c *C) {
	sce := &gm.ProtoScenario{
		Failed: proto.Bool(true),
	}
	res := result.NewScenarioResult(sce)

	status := getStatus(res)

	c.Assert(*status, Equals, gm.Result_FAILED)
}

func (s *MySuite) TestGetStatusForPassedScenario(c *C) {
	sce := &gm.ProtoScenario{
		Failed: proto.Bool(false),
	}
	res := result.NewScenarioResult(sce)

	status := getStatus(res)

	c.Assert(*status, Equals, gm.Result_PASSED)
}

func (s *MySuite) TestGetStatusForSkippedScenario(c *C) {
	sce := &gm.ProtoScenario{
		Skipped: proto.Bool(true),
	}
	res := result.NewScenarioResult(sce)

	status := getStatus(res)

	c.Assert(*status, Equals, gm.Result_SKIPPED)
}

func (s *MySuite) TestGetErrorResponse(c *C) {
	errs := getErrorExecutionResponse(errors.New("error1"), errors.New("error2"))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ErrorResult.Enum(),
		Result: &gm.Result{
			Errors: []*gm.Result_ExecutionError{
				{ErrorMessage: proto.String("error1")},
				{ErrorMessage: proto.String("error2")},
			},
		},
	}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestGetHookFailureWhenNoFailure(c *C) {
	failure := getHookFailure(nil)

	var expected *gm.Result_ExecutionError
	c.Assert(failure, DeepEquals, expected)
}

func (s *MySuite) TestGetHookFailureWhenHookFailure(c *C) {
	hookFailure := &gm.ProtoHookFailure{ErrorMessage: proto.String("err msg")}
	failure := getHookFailure(&hookFailure)

	expected := &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")}
	c.Assert(failure, DeepEquals, expected)
}

func (s *MySuite) TestGetErrors(c *C) {
	items := []*gm.ProtoItem{newFailedStep("msg1")}
	errors := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}}
	c.Assert(errors, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithMultipleStepFailures(c *C) {
	items := []*gm.ProtoItem{
		newFailedStep("msg1"),
		newFailedStep("msg2"),
	}
	errors := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}, {ErrorMessage: proto.String("msg2")}}
	c.Assert(errors, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithConceptFailures(c *C) {
	items := []*gm.ProtoItem{
		{
			ItemType: gm.ProtoItem_Concept.Enum(),
			Concept: &gm.ProtoConcept{
				Steps: []*gm.ProtoItem{newFailedStep("msg1")},
			},
		},
	}
	errors := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}}
	c.Assert(errors, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithNestedConceptFailures(c *C) {
	items := []*gm.ProtoItem{
		{
			ItemType: gm.ProtoItem_Concept.Enum(),
			Concept: &gm.ProtoConcept{
				Steps: []*gm.ProtoItem{
					newFailedStep("msg1"),
					{
						ItemType: gm.ProtoItem_Concept.Enum(),
						Concept: &gm.ProtoConcept{
							Steps: []*gm.ProtoItem{
								newFailedStep("msg2"),
							},
						},
					},
				},
			},
		},
	}
	errors := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}, {ErrorMessage: proto.String("msg2")}}
	c.Assert(errors, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithStepAndConceptFailures(c *C) {
	items := []*gm.ProtoItem{
		{
			ItemType: gm.ProtoItem_Concept.Enum(),
			Concept: &gm.ProtoConcept{
				Steps: []*gm.ProtoItem{
					newFailedStep("msg1"),
					{
						ItemType: gm.ProtoItem_Concept.Enum(),
						Concept: &gm.ProtoConcept{
							Steps: []*gm.ProtoItem{newFailedStep("msg2")},
						},
					},
				},
			},
		},
		newFailedStep("msg3"),
	}
	errors := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}, {ErrorMessage: proto.String("msg2")}, {ErrorMessage: proto.String("msg3")}}
	c.Assert(errors, DeepEquals, expected)
}

func (s *MySuite) TestListenSuiteStartExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)

	listenExecutionEvents(&dummyServer{response: actual})
	event.Notify(event.NewExecutionEvent(event.SuiteStart, nil, nil, 0, gm.ExecutionInfo{}))
	defer sendSuiteEnd(actual)

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SuiteStart.Enum(),
	}
	c.Assert(<-actual, DeepEquals, expected)

}

func (s *MySuite) TestListenSpecStartExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.SpecStart, nil, nil, 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SpecStart.Enum(),
		ID:   proto.String("example.spec"),
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenScenarioStartExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.ScenarioStart, &gauge.Scenario{Heading: &gauge.Heading{LineNo: 1}}, nil, 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ScenarioStart.Enum(),
		ID:   proto.String("example.spec:1"),
		Result: &gm.Result{
			TableRowNumber: proto.Int64(0),
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenSpecEndExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}
	hookFailure := &gm.ProtoHookFailure{ErrorMessage: proto.String("err msg")}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.SpecEnd, nil, &result.SpecResult{
		ProtoSpec: &gm.ProtoSpec{PreHookFailure: hookFailure, PostHookFailure: hookFailure},
	}, 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SpecEnd.Enum(),
		ID:   proto.String("example.spec"),
		Result: &gm.Result{
			BeforeHookFailure: &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
			AfterHookFailure:  &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenSuiteEndExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	hookFailure := &gm.ProtoHookFailure{ErrorMessage: proto.String("err msg")}

	listenExecutionEvents(&dummyServer{response: actual})
	event.Notify(event.NewExecutionEvent(event.SuiteEnd, nil, &result.SuiteResult{PreSuite: hookFailure, PostSuite: hookFailure}, 0, gm.ExecutionInfo{}))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SuiteEnd.Enum(),
		Result: &gm.Result{
			BeforeHookFailure: &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
			AfterHookFailure:  &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenScenarioEndExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}
	scn := &gm.ProtoScenario{
		ScenarioItems: []*gm.ProtoItem{},
		ExecutionTime: proto.Int64(1),
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.ScenarioEnd, &gauge.Scenario{Heading: &gauge.Heading{LineNo: 1}}, result.NewScenarioResult(scn), 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ScenarioEnd.Enum(),
		Result: &gm.Result{
			ExecutionTime:  proto.Int64(1),
			Status:         gm.Result_PASSED.Enum(),
			TableRowNumber: proto.Int64(0),
		},
		ID: proto.String("example.spec:1"),
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenScenarioEndExecutionEventForFailedScenario(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}
	scn := &gm.ProtoScenario{
		ScenarioItems: []*gm.ProtoItem{
			newFailedStep("error message"),
		},
		ExecutionTime: proto.Int64(1),
		Failed:        proto.Bool(true),
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.ScenarioEnd, &gauge.Scenario{Heading: &gauge.Heading{LineNo: 1}}, result.NewScenarioResult(scn), 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ScenarioEnd.Enum(),
		ID:   proto.String("example.spec:1"),
		Result: &gm.Result{
			ExecutionTime: proto.Int64(1),
			Status:        gm.Result_FAILED.Enum(),
			Errors: []*gm.Result_ExecutionError{
				{
					ErrorMessage: proto.String("error message"),
				},
			},
			TableRowNumber: proto.Int64(0),
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestGetDataTableRowNumberWhenDataTableIsNotPresent(c *C) {
	scn := &gauge.Scenario{}

	number := getDataTableRowNumber(scn)

	c.Assert(number, Equals, 0)
}

func (s *MySuite) TestGetDataTableRowNumberWhenDataTableIsPresent(c *C) {
	scn := &gauge.Scenario{
		DataTableRow:      gauge.Table{},
		DataTableRowIndex: 2,
	}
	scn.DataTableRow.AddHeaders([]string{})

	number := getDataTableRowNumber(scn)

	c.Assert(number, Equals, 3)
}

type dummyServer struct {
	response chan *gm.ExecutionResponse
}

func (d *dummyServer) Send(r *gm.ExecutionResponse) error {
	d.response <- r
	return nil
}

func (d *dummyServer) Context() context.Context {
	return nil
}
func (d *dummyServer) SendMsg(m interface{}) error {
	return nil
}
func (d *dummyServer) RecvMsg(m interface{}) error {
	return nil
}

func (d *dummyServer) SendHeader(metadata.MD) error {
	return nil
}
func (d *dummyServer) SetTrailer(metadata.MD) {

}

func newFailedStep(msg string) *gm.ProtoItem {
	return &gm.ProtoItem{
		ItemType: gm.ProtoItem_Step.Enum(),
		Step: &gm.ProtoStep{
			StepExecutionResult: &gm.ProtoStepExecutionResult{
				ExecutionResult: &gm.ProtoExecutionResult{
					Failed:       proto.Bool(true),
					ErrorMessage: proto.String(msg),
				},
			},
		},
	}
}

func sendSuiteEnd(actual chan *gm.ExecutionResponse) {
	event.Notify(event.NewExecutionEvent(event.SuiteEnd, nil, &result.SuiteResult{PreSuite: nil, PostSuite: nil}, 0, gm.ExecutionInfo{}))
	<-actual
}
<commit_msg>Fixing naming issue in stream_test.go #165<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package stream

import (
	"errors"

	"testing"

	"github.com/getgauge/gauge/execution/event"
	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	gm "github.com/getgauge/gauge/gauge_messages"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestGetStatusForFailedScenario(c *C) {
	sce := &gm.ProtoScenario{
		Failed: proto.Bool(true),
	}
	res := result.NewScenarioResult(sce)

	status := getStatus(res)

	c.Assert(*status, Equals, gm.Result_FAILED)
}

func (s *MySuite) TestGetStatusForPassedScenario(c *C) {
	sce := &gm.ProtoScenario{
		Failed: proto.Bool(false),
	}
	res := result.NewScenarioResult(sce)

	status := getStatus(res)

	c.Assert(*status, Equals, gm.Result_PASSED)
}

func (s *MySuite) TestGetStatusForSkippedScenario(c *C) {
	sce := &gm.ProtoScenario{
		Skipped: proto.Bool(true),
	}
	res := result.NewScenarioResult(sce)

	status := getStatus(res)

	c.Assert(*status, Equals, gm.Result_SKIPPED)
}

func (s *MySuite) TestGetErrorResponse(c *C) {
	errs := getErrorExecutionResponse(errors.New("error1"), errors.New("error2"))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ErrorResult.Enum(),
		Result: &gm.Result{
			Errors: []*gm.Result_ExecutionError{
				{ErrorMessage: proto.String("error1")},
				{ErrorMessage: proto.String("error2")},
			},
		},
	}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestGetHookFailureWhenNoFailure(c *C) {
	failure := getHookFailure(nil)

	var expected *gm.Result_ExecutionError
	c.Assert(failure, DeepEquals, expected)
}

func (s *MySuite) TestGetHookFailureWhenHookFailure(c *C) {
	hookFailure := &gm.ProtoHookFailure{ErrorMessage: proto.String("err msg")}
	failure := getHookFailure(&hookFailure)

	expected := &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")}
	c.Assert(failure, DeepEquals, expected)
}

func (s *MySuite) TestGetErrors(c *C) {
	items := []*gm.ProtoItem{newFailedStep("msg1")}
	errs := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithMultipleStepFailures(c *C) {
	items := []*gm.ProtoItem{
		newFailedStep("msg1"),
		newFailedStep("msg2"),
	}
	errs := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}, {ErrorMessage: proto.String("msg2")}}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithConceptFailures(c *C) {
	items := []*gm.ProtoItem{
		{
			ItemType: gm.ProtoItem_Concept.Enum(),
			Concept: &gm.ProtoConcept{
				Steps: []*gm.ProtoItem{newFailedStep("msg1")},
			},
		},
	}
	errs := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithNestedConceptFailures(c *C) {
	items := []*gm.ProtoItem{
		{
			ItemType: gm.ProtoItem_Concept.Enum(),
			Concept: &gm.ProtoConcept{
				Steps: []*gm.ProtoItem{
					newFailedStep("msg1"),
					{
						ItemType: gm.ProtoItem_Concept.Enum(),
						Concept: &gm.ProtoConcept{
							Steps: []*gm.ProtoItem{
								newFailedStep("msg2"),
							},
						},
					},
				},
			},
		},
	}
	errs := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}, {ErrorMessage: proto.String("msg2")}}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestGetErrorsWithStepAndConceptFailures(c *C) {
	items := []*gm.ProtoItem{
		{
			ItemType: gm.ProtoItem_Concept.Enum(),
			Concept: &gm.ProtoConcept{
				Steps: []*gm.ProtoItem{
					newFailedStep("msg1"),
					{
						ItemType: gm.ProtoItem_Concept.Enum(),
						Concept: &gm.ProtoConcept{
							Steps: []*gm.ProtoItem{newFailedStep("msg2")},
						},
					},
				},
			},
		},
		newFailedStep("msg3"),
	}
	errs := getErrors(items)

	expected := []*gm.Result_ExecutionError{{ErrorMessage: proto.String("msg1")}, {ErrorMessage: proto.String("msg2")}, {ErrorMessage: proto.String("msg3")}}
	c.Assert(errs, DeepEquals, expected)
}

func (s *MySuite) TestListenSuiteStartExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)

	listenExecutionEvents(&dummyServer{response: actual})
	event.Notify(event.NewExecutionEvent(event.SuiteStart, nil, nil, 0, gm.ExecutionInfo{}))
	defer sendSuiteEnd(actual)

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SuiteStart.Enum(),
	}
	c.Assert(<-actual, DeepEquals, expected)

}

func (s *MySuite) TestListenSpecStartExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.SpecStart, nil, nil, 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SpecStart.Enum(),
		ID:   proto.String("example.spec"),
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenScenarioStartExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.ScenarioStart, &gauge.Scenario{Heading: &gauge.Heading{LineNo: 1}}, nil, 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ScenarioStart.Enum(),
		ID:   proto.String("example.spec:1"),
		Result: &gm.Result{
			TableRowNumber: proto.Int64(0),
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenSpecEndExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}
	hookFailure := &gm.ProtoHookFailure{ErrorMessage: proto.String("err msg")}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.SpecEnd, nil, &result.SpecResult{
		ProtoSpec: &gm.ProtoSpec{PreHookFailure: hookFailure, PostHookFailure: hookFailure},
	}, 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SpecEnd.Enum(),
		ID:   proto.String("example.spec"),
		Result: &gm.Result{
			BeforeHookFailure: &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
			AfterHookFailure:  &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenSuiteEndExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	hookFailure := &gm.ProtoHookFailure{ErrorMessage: proto.String("err msg")}

	listenExecutionEvents(&dummyServer{response: actual})
	event.Notify(event.NewExecutionEvent(event.SuiteEnd, nil, &result.SuiteResult{PreSuite: hookFailure, PostSuite: hookFailure}, 0, gm.ExecutionInfo{}))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_SuiteEnd.Enum(),
		Result: &gm.Result{
			BeforeHookFailure: &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
			AfterHookFailure:  &gm.Result_ExecutionError{ErrorMessage: proto.String("err msg")},
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenScenarioEndExecutionEvent(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}
	scn := &gm.ProtoScenario{
		ScenarioItems: []*gm.ProtoItem{},
		ExecutionTime: proto.Int64(1),
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.ScenarioEnd, &gauge.Scenario{Heading: &gauge.Heading{LineNo: 1}}, result.NewScenarioResult(scn), 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ScenarioEnd.Enum(),
		Result: &gm.Result{
			ExecutionTime:  proto.Int64(1),
			Status:         gm.Result_PASSED.Enum(),
			TableRowNumber: proto.Int64(0),
		},
		ID: proto.String("example.spec:1"),
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestListenScenarioEndExecutionEventForFailedScenario(c *C) {
	event.InitRegistry()
	actual := make(chan *gm.ExecutionResponse)
	ei := gm.ExecutionInfo{
		CurrentSpec: &gm.SpecInfo{FileName: proto.String("example.spec")},
	}
	scn := &gm.ProtoScenario{
		ScenarioItems: []*gm.ProtoItem{
			newFailedStep("error message"),
		},
		ExecutionTime: proto.Int64(1),
		Failed:        proto.Bool(true),
	}

	listenExecutionEvents(&dummyServer{response: actual})
	defer sendSuiteEnd(actual)
	event.Notify(event.NewExecutionEvent(event.ScenarioEnd, &gauge.Scenario{Heading: &gauge.Heading{LineNo: 1}}, result.NewScenarioResult(scn), 0, ei))

	expected := &gm.ExecutionResponse{
		Type: gm.ExecutionResponse_ScenarioEnd.Enum(),
		ID:   proto.String("example.spec:1"),
		Result: &gm.Result{
			ExecutionTime: proto.Int64(1),
			Status:        gm.Result_FAILED.Enum(),
			Errors: []*gm.Result_ExecutionError{
				{
					ErrorMessage: proto.String("error message"),
				},
			},
			TableRowNumber: proto.Int64(0),
		},
	}
	c.Assert(<-actual, DeepEquals, expected)
}

func (s *MySuite) TestGetDataTableRowNumberWhenDataTableIsNotPresent(c *C) {
	scn := &gauge.Scenario{}

	number := getDataTableRowNumber(scn)

	c.Assert(number, Equals, 0)
}

func (s *MySuite) TestGetDataTableRowNumberWhenDataTableIsPresent(c *C) {
	scn := &gauge.Scenario{
		DataTableRow:      gauge.Table{},
		DataTableRowIndex: 2,
	}
	scn.DataTableRow.AddHeaders([]string{})

	number := getDataTableRowNumber(scn)

	c.Assert(number, Equals, 3)
}

type dummyServer struct {
	response chan *gm.ExecutionResponse
}

func (d *dummyServer) Send(r *gm.ExecutionResponse) error {
	d.response <- r
	return nil
}

func (d *dummyServer) Context() context.Context {
	return nil
}
func (d *dummyServer) SendMsg(m interface{}) error {
	return nil
}
func (d *dummyServer) RecvMsg(m interface{}) error {
	return nil
}

func (d *dummyServer) SendHeader(metadata.MD) error {
	return nil
}
func (d *dummyServer) SetTrailer(metadata.MD) {

}

func newFailedStep(msg string) *gm.ProtoItem {
	return &gm.ProtoItem{
		ItemType: gm.ProtoItem_Step.Enum(),
		Step: &gm.ProtoStep{
			StepExecutionResult: &gm.ProtoStepExecutionResult{
				ExecutionResult: &gm.ProtoExecutionResult{
					Failed:       proto.Bool(true),
					ErrorMessage: proto.String(msg),
				},
			},
		},
	}
}

func sendSuiteEnd(actual chan *gm.ExecutionResponse) {
	event.Notify(event.NewExecutionEvent(event.SuiteEnd, nil, &result.SuiteResult{PreSuite: nil, PostSuite: nil}, 0, gm.ExecutionInfo{}))
	<-actual
}
<|endoftext|>


<commit_before>// Copyright 2022 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 storage

import (
	"context"
	"errors"
	"strings"
	"testing"

	"cloud.google.com/go/storage"
	"github.com/jacobsa/gcloud/gcs"
	. "github.com/jacobsa/ogletest"
)

const missingObjectName string = "test/foo"
const dstObjectName string = "gcsfuse/dst.txt"

// FakeGCSServer is not handling generation and metageneration checks for Delete flow.
// Hence, we are not writing tests for these flows.
// https://github.com/GoogleCloudPlatform/gcsfuse/blob/master/vendor/github.com/fsouza/fake-gcs-server/fakestorage/object.go#L515

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

type BucketHandleTest struct {
	bucketHandle  *bucketHandle
	storageHandle StorageHandle
	fakeStorage   FakeStorage
}

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

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

func (t *BucketHandleTest) SetUp(_ *TestInfo) {
	var err error
	t.fakeStorage = NewFakeStorage()
	t.storageHandle = t.fakeStorage.CreateStorageHandle()
	t.bucketHandle, err = t.storageHandle.BucketHandle(TestBucketName)

	AssertEq(nil, err)
	AssertNe(nil, t.bucketHandle)
}

func (t *BucketHandleTest) TearDown() {
	t.fakeStorage.ShutDown()
}

func (t *BucketHandleTest) TestNewReaderMethodWithCompleteRead() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
		})

	AssertEq(nil, err)
	defer rc.Close()
	buf := make([]byte, len(ContentInTestObject))
	_, err = rc.Read(buf)
	AssertEq(nil, err)
	ExpectEq(string(buf[:]), ContentInTestObject)
}

func (t *BucketHandleTest) TestNewReaderMethodWithRangeRead() {
	start := uint64(2)
	limit := uint64(8)

	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: start,
				Limit: limit,
			},
		})

	AssertEq(nil, err)
	defer rc.Close()
	buf := make([]byte, limit-start)
	_, err = rc.Read(buf)
	AssertEq(nil, err)
	ExpectEq(string(buf[:]), ContentInTestObject[start:limit])
}

func (t *BucketHandleTest) TestNewReaderMethodWithInValidObject() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: missingObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
		})

	AssertNe(nil, err)
	AssertEq(nil, rc)
}

func (t *BucketHandleTest) TestNewReaderMethodWithValidGeneration() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
			Generation: TestObjectGeneration,
		})

	AssertEq(nil, err)
	defer rc.Close()
	buf := make([]byte, len(ContentInTestObject))
	_, err = rc.Read(buf)
	AssertEq(nil, err)
	ExpectEq(string(buf[:]), ContentInTestObject)
}

func (t *BucketHandleTest) TestNewReaderMethodWithInvalidGeneration() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
			Generation: 222, // other than TestObjectGeneration, doesn't exist.
		})

	AssertNe(nil, err)
	AssertEq(nil, rc)
}

func (t *BucketHandleTest) TestDeleteObjectMethodWithValidObject() {
	err := t.bucketHandle.DeleteObject(context.Background(),
		&gcs.DeleteObjectRequest{
			Name:                       TestObjectName,
			Generation:                 TestObjectGeneration,
			MetaGenerationPrecondition: nil,
		})

	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestDeleteObjectMethodWithMissingObject() {
	err := t.bucketHandle.DeleteObject(context.Background(),
		&gcs.DeleteObjectRequest{
			Name:                       missingObjectName,
			Generation:                 TestObjectGeneration,
			MetaGenerationPrecondition: nil,
		})

	AssertEq("storage: object doesn't exist", err.Error())
}

func (t *BucketHandleTest) TestStatObjectMethodWithValidObject() {
	_, err := t.bucketHandle.StatObject(context.Background(),
		&gcs.StatObjectRequest{
			Name: TestObjectName,
		})

	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestStatObjectMethodWithMissingObject() {
	var notfound *gcs.NotFoundError

	_, err := t.bucketHandle.StatObject(context.Background(),
		&gcs.StatObjectRequest{
			Name: missingObjectName,
		})

	AssertTrue(errors.As(err, &notfound))
}

func (t *BucketHandleTest) TestCopyObjectMethodWithValidObject() {
	_, err := t.bucketHandle.CopyObject(context.Background(),
		&gcs.CopyObjectRequest{
			SrcName:                       TestObjectName,
			DstName:                       dstObjectName,
			SrcGeneration:                 TestObjectGeneration,
			SrcMetaGenerationPrecondition: nil,
		})

	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestCopyObjectMethodWithMissingObject() {
	var notfound *gcs.NotFoundError

	_, err := t.bucketHandle.CopyObject(context.Background(),
		&gcs.CopyObjectRequest{
			SrcName:                       missingObjectName,
			DstName:                       dstObjectName,
			SrcGeneration:                 TestObjectGeneration,
			SrcMetaGenerationPrecondition: nil,
		})

	AssertTrue(errors.As(err, &notfound))
}

func (t *BucketHandleTest) TestCopyObjectMethodWithInvalidGeneration() {
	var notfound *gcs.NotFoundError

	_, err := t.bucketHandle.CopyObject(context.Background(),
		&gcs.CopyObjectRequest{
			SrcName:                       TestObjectName,
			DstName:                       dstObjectName,
			SrcGeneration:                 222, // Other than testObjectGeneration, no other generation exists.
			SrcMetaGenerationPrecondition: nil,
		})

	AssertTrue(errors.As(err, &notfound))
}

func (t *BucketHandleTest) TestCreateObjectMethodWithValidObject() {
	content := "Creating a new object"
	obj, err := t.bucketHandle.CreateObject(context.Background(),
		&gcs.CreateObjectRequest{
			Name:     "test_object",
			Contents: strings.NewReader(content),
		})

	AssertEq(obj.Name, "test_object")
	AssertEq(obj.Size, len(content))
	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestCreateObjectMethodWhenGivenGenerationObjectNotExist() {
	content := "Creating a new object"
	var crc32 uint32 = 45
	var generation int64 = 786

	obj, err := t.bucketHandle.CreateObject(context.Background(),
		&gcs.CreateObjectRequest{
			Name:                   "test_object",
			Contents:               strings.NewReader(content),
			CRC32C:                 &crc32,
			GenerationPrecondition: &generation,
		})

	AssertEq(nil, obj)
	AssertTrue(strings.Contains(err.Error(), "Error 412: Precondition failed"))
}

func (t *BucketHandleTest) TestGetProjectValueWhenGcloudProjectionIsNoAcl() {
	proj := getProjectionValue(gcs.Projection(storage.ProjectionNoACL))

	AssertEq(storage.Projection(1), proj)
}

func (t *BucketHandleTest) TestGetProjectValueWhenGcloudProjectionIsFull() {
	proj := getProjectionValue(gcs.Projection(storage.ProjectionFull))

	AssertEq(storage.Projection(2), proj)
}

func (t *BucketHandleTest) TestGetProjectValueWhenGcloudProjectionIsDefault() {
	proj := getProjectionValue(gcs.Projection(storage.ProjectionDefault))

	AssertEq(storage.Projection(1), proj)
}

func (t *BucketHandleTest) TestListObjectMethodWithPrefixObjectExist() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "gcsfuse/",
			Delimiter:                "/",
			IncludeTrailingDelimiter: true,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(TestObjectRootFolderName, obj.Objects[0].Name)
	AssertEq(TestObjectSubRootFolderName, obj.Objects[1].Name)
	AssertEq(TestObjectName, obj.Objects[2].Name)
	AssertEq(TestObjectGeneration, obj.Objects[0].Generation)
	AssertEq(TestObjectSubRootFolderName, obj.CollapsedRuns[0])
}

func (t *BucketHandleTest) TestListObjectMethodWithPrefixObjectDoesNotExist() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "PrefixObjectDoesNotExist",
			Delimiter:                "/",
			IncludeTrailingDelimiter: true,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(nil, obj.Objects)
	AssertEq(nil, obj.CollapsedRuns)
}

func (t *BucketHandleTest) TestListObjectMethodWithIncludeTrailingDelimiterFalse() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "gcsfuse/",
			Delimiter:                "/",
			IncludeTrailingDelimiter: false,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(TestObjectRootFolderName, obj.Objects[0].Name)
	AssertEq(TestObjectName, obj.Objects[1].Name)
	AssertEq(TestObjectSubRootFolderName, obj.CollapsedRuns[0])
}

func (t *BucketHandleTest) TestListObjectMethodWithEmptyDelimiter() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "gcsfuse/",
			Delimiter:                "",
			IncludeTrailingDelimiter: true,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(TestObjectRootFolderName, obj.Objects[0].Name)
	AssertEq(TestObjectSubRootFolderName, obj.Objects[1].Name)
	AssertEq(TestSubObjectName, obj.Objects[2].Name)
	AssertEq(TestObjectName, obj.Objects[3].Name)
	AssertEq(TestObjectGeneration, obj.Objects[0].Generation)
	AssertEq(nil, obj.CollapsedRuns)
}
<commit_msg>Create ListObjects method<commit_after>// Copyright 2022 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 storage

import (
	"context"
	"errors"
	"strings"
	"testing"

	"cloud.google.com/go/storage"
	"github.com/jacobsa/gcloud/gcs"
	. "github.com/jacobsa/ogletest"
)

const missingObjectName string = "test/foo"
const dstObjectName string = "gcsfuse/dst.txt"

// FakeGCSServer is not handling generation and metageneration checks for Delete flow.
// Hence, we are not writing tests for these flows.
// https://github.com/GoogleCloudPlatform/gcsfuse/blob/master/vendor/github.com/fsouza/fake-gcs-server/fakestorage/object.go#L515

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

type BucketHandleTest struct {
	bucketHandle  *bucketHandle
	storageHandle StorageHandle
	fakeStorage   FakeStorage
}

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

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

func (t *BucketHandleTest) SetUp(_ *TestInfo) {
	var err error
	t.fakeStorage = NewFakeStorage()
	t.storageHandle = t.fakeStorage.CreateStorageHandle()
	t.bucketHandle, err = t.storageHandle.BucketHandle(TestBucketName)

	AssertEq(nil, err)
	AssertNe(nil, t.bucketHandle)
}

func (t *BucketHandleTest) TearDown() {
	t.fakeStorage.ShutDown()
}

func (t *BucketHandleTest) TestNewReaderMethodWithCompleteRead() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
		})

	AssertEq(nil, err)
	defer rc.Close()
	buf := make([]byte, len(ContentInTestObject))
	_, err = rc.Read(buf)
	AssertEq(nil, err)
	ExpectEq(string(buf[:]), ContentInTestObject)
}

func (t *BucketHandleTest) TestNewReaderMethodWithRangeRead() {
	start := uint64(2)
	limit := uint64(8)

	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: start,
				Limit: limit,
			},
		})

	AssertEq(nil, err)
	defer rc.Close()
	buf := make([]byte, limit-start)
	_, err = rc.Read(buf)
	AssertEq(nil, err)
	ExpectEq(string(buf[:]), ContentInTestObject[start:limit])
}

func (t *BucketHandleTest) TestNewReaderMethodWithInValidObject() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: missingObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
		})

	AssertNe(nil, err)
	AssertEq(nil, rc)
}

func (t *BucketHandleTest) TestNewReaderMethodWithValidGeneration() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
			Generation: TestObjectGeneration,
		})

	AssertEq(nil, err)
	defer rc.Close()
	buf := make([]byte, len(ContentInTestObject))
	_, err = rc.Read(buf)
	AssertEq(nil, err)
	ExpectEq(string(buf[:]), ContentInTestObject)
}

func (t *BucketHandleTest) TestNewReaderMethodWithInvalidGeneration() {
	rc, err := t.bucketHandle.NewReader(context.Background(),
		&gcs.ReadObjectRequest{
			Name: TestObjectName,
			Range: &gcs.ByteRange{
				Start: uint64(0),
				Limit: uint64(len(ContentInTestObject)),
			},
			Generation: 222, // other than TestObjectGeneration, doesn't exist.
		})

	AssertNe(nil, err)
	AssertEq(nil, rc)
}

func (t *BucketHandleTest) TestDeleteObjectMethodWithValidObject() {
	err := t.bucketHandle.DeleteObject(context.Background(),
		&gcs.DeleteObjectRequest{
			Name:                       TestObjectName,
			Generation:                 TestObjectGeneration,
			MetaGenerationPrecondition: nil,
		})

	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestDeleteObjectMethodWithMissingObject() {
	err := t.bucketHandle.DeleteObject(context.Background(),
		&gcs.DeleteObjectRequest{
			Name:                       missingObjectName,
			Generation:                 TestObjectGeneration,
			MetaGenerationPrecondition: nil,
		})

	AssertEq("storage: object doesn't exist", err.Error())
}

func (t *BucketHandleTest) TestStatObjectMethodWithValidObject() {
	_, err := t.bucketHandle.StatObject(context.Background(),
		&gcs.StatObjectRequest{
			Name: TestObjectName,
		})

	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestStatObjectMethodWithMissingObject() {
	var notfound *gcs.NotFoundError

	_, err := t.bucketHandle.StatObject(context.Background(),
		&gcs.StatObjectRequest{
			Name: missingObjectName,
		})

	AssertTrue(errors.As(err, &notfound))
}

func (t *BucketHandleTest) TestCopyObjectMethodWithValidObject() {
	_, err := t.bucketHandle.CopyObject(context.Background(),
		&gcs.CopyObjectRequest{
			SrcName:                       TestObjectName,
			DstName:                       dstObjectName,
			SrcGeneration:                 TestObjectGeneration,
			SrcMetaGenerationPrecondition: nil,
		})

	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestCopyObjectMethodWithMissingObject() {
	var notfound *gcs.NotFoundError

	_, err := t.bucketHandle.CopyObject(context.Background(),
		&gcs.CopyObjectRequest{
			SrcName:                       missingObjectName,
			DstName:                       dstObjectName,
			SrcGeneration:                 TestObjectGeneration,
			SrcMetaGenerationPrecondition: nil,
		})

	AssertTrue(errors.As(err, &notfound))
}

func (t *BucketHandleTest) TestCopyObjectMethodWithInvalidGeneration() {
	var notfound *gcs.NotFoundError

	_, err := t.bucketHandle.CopyObject(context.Background(),
		&gcs.CopyObjectRequest{
			SrcName:                       TestObjectName,
			DstName:                       dstObjectName,
			SrcGeneration:                 222, // Other than testObjectGeneration, no other generation exists.
			SrcMetaGenerationPrecondition: nil,
		})

	AssertTrue(errors.As(err, &notfound))
}

func (t *BucketHandleTest) TestCreateObjectMethodWithValidObject() {
	content := "Creating a new object"
	obj, err := t.bucketHandle.CreateObject(context.Background(),
		&gcs.CreateObjectRequest{
			Name:     "test_object",
			Contents: strings.NewReader(content),
		})

	AssertEq(obj.Name, "test_object")
	AssertEq(obj.Size, len(content))
	AssertEq(nil, err)
}

func (t *BucketHandleTest) TestCreateObjectMethodWhenGivenGenerationObjectNotExist() {
	content := "Creating a new object"
	var crc32 uint32 = 45
	var generation int64 = 786

	obj, err := t.bucketHandle.CreateObject(context.Background(),
		&gcs.CreateObjectRequest{
			Name:                   "test_object",
			Contents:               strings.NewReader(content),
			CRC32C:                 &crc32,
			GenerationPrecondition: &generation,
		})

	AssertEq(nil, obj)
	AssertTrue(strings.Contains(err.Error(), "Error 412: Precondition failed"))
}

func (t *BucketHandleTest) TestGetProjectValueWhenGcloudProjectionIsNoAcl() {
	proj := getProjectionValue(gcs.Projection(storage.ProjectionNoACL))

	AssertEq(storage.Projection(1), proj)
}

func (t *BucketHandleTest) TestGetProjectValueWhenGcloudProjectionIsFull() {
	proj := getProjectionValue(gcs.Projection(storage.ProjectionFull))

	AssertEq(storage.Projection(2), proj)
}

func (t *BucketHandleTest) TestGetProjectValueWhenGcloudProjectionIsDefault() {
	proj := getProjectionValue(gcs.Projection(storage.ProjectionDefault))

	AssertEq(storage.Projection(1), proj)
}

func (t *BucketHandleTest) TestListObjectMethodWithPrefixObjectExist() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "gcsfuse/",
			Delimiter:                "/",
			IncludeTrailingDelimiter: true,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(TestObjectRootFolderName, obj.Objects[0].Name)
	AssertEq(TestObjectSubRootFolderName, obj.Objects[1].Name)
	AssertEq(TestObjectName, obj.Objects[2].Name)
	AssertEq(TestObjectGeneration, obj.Objects[0].Generation)
	AssertEq(TestObjectSubRootFolderName, obj.CollapsedRuns[0])
}

func (t *BucketHandleTest) TestListObjectMethodWithPrefixObjectDoesNotExist() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "PrefixObjectDoesNotExist",
			Delimiter:                "/",
			IncludeTrailingDelimiter: true,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(nil, obj.Objects)
	AssertEq(nil, obj.CollapsedRuns)
}

func (t *BucketHandleTest) TestListObjectMethodWithIncludeTrailingDelimiterFalse() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "gcsfuse/",
			Delimiter:                "/",
			IncludeTrailingDelimiter: false,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(TestObjectRootFolderName, obj.Objects[0].Name)
	AssertEq(TestObjectName, obj.Objects[1].Name)
	AssertEq(TestObjectSubRootFolderName, obj.CollapsedRuns[0])
}

// If Delimiter is empty, all the objects will appear with same prefix.
func (t *BucketHandleTest) TestListObjectMethodWithEmptyDelimiter() {
	obj, err := t.bucketHandle.ListObjects(context.Background(),
		&gcs.ListObjectsRequest{
			Prefix:                   "gcsfuse/",
			Delimiter:                "",
			IncludeTrailingDelimiter: true,
			ContinuationToken:        "ContinuationToken",
			MaxResults:               7,
			ProjectionVal:            0,
		})

	AssertEq(nil, err)
	AssertEq(TestObjectRootFolderName, obj.Objects[0].Name)
	AssertEq(TestObjectSubRootFolderName, obj.Objects[1].Name)
	AssertEq(TestSubObjectName, obj.Objects[2].Name)
	AssertEq(TestObjectName, obj.Objects[3].Name)
	AssertEq(TestObjectGeneration, obj.Objects[0].Generation)
	AssertEq(nil, obj.CollapsedRuns)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/rand"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"golang.org/x/net/context"

	"github.com/bmatsuo/lmdb-go/exp/lmdbsync"
	"github.com/bmatsuo/lmdb-go/lmdb"
)

func main() {
	numitems := flag.Int64("n", 5<<10, "the number of items to write")
	chunksize := flag.Int64("c", 100, "the number of items to write per txn")
	flag.Parse()

	failed := false
	defer func() {
		if failed {
			os.Exit(1)
		}
	}()
	fail := func(err error) {
		failed = true
		log.Print(err)
	}

	err := WriteRandomItems("db", *numitems, *chunksize)
	if err != nil {
		fail(err)
	} else {
		log.Printf("success")
	}
}

// WriteRandomItems writes numitem items with chunksize sized values full of
// random data.
func WriteRandomItems(path string, numitem, chunksize int64) (err error) {
	env, err := OpenEnv(path)
	if err != nil {
		return err
	}
	defer env.Close()

	numResize := 0
	numResized := 0
	defer func() {
		log.Printf("%d resizes", numResize)
		log.Printf("%d size adoptions", numResized)
		if err == nil {
			if numResize == 0 {
				err = fmt.Errorf("process did not resize the memory map")
			} else if numResized == 0 {
				err = fmt.Errorf("process did not adopt a new map size")
			}
		}
	}()
	mapResizedLogger := func(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error) {
		if lmdb.IsMapResized(err) {
			log.Printf("map resized")
			numResized++
		}
		return ctx, err
	}
	mapFullLogger := func(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error) {
		if lmdb.IsMapFull(err) {
			log.Printf("resize required")
			numResize++
		}
		return ctx, err
	}
	env.Handlers = env.Handlers.Append(
		handlerFunc(mapResizedLogger),
		lmdbsync.MapResizedHandler(2, func(int) time.Duration { return 100 * time.Microsecond }),
		handlerFunc(mapFullLogger),
		lmdbsync.MapFullHandler(func(size int64) (int64, bool) {
			newsize := size + 128<<10 // linear scale is bad -- but useful to test
			log.Printf("oldsize=%d newsize=%d", size, newsize)
			return newsize, true
		}),
	)

	pid := os.Getpid()

	scanner := bufio.NewScanner(os.Stdin)
	for i := int64(0); i < numitem; {
		if !scanner.Scan() {
			return scanner.Err()
		}

		start := i
		chunkmax := i + chunksize
		if chunkmax > numitem {
			chunkmax = numitem
		}
		v := make([]byte, 512)
		_, err := io.ReadFull(rand.Reader, v)
		if err != nil {
			return err
		}
		err = env.Update(func(txn *lmdb.Txn) (err error) {
			dbi, err := txn.OpenRoot(0)
			if err != nil {
				return err
			}

			for i = start; i < chunkmax; i++ {
				k := fmt.Sprintf("%06d-%016x", pid, i)
				err = txn.Put(dbi, []byte(k), v, 0)
				if err != nil {
					return err
				}
			}

			return nil
		})
		if err != nil {
			return err
		}
		fmt.Println("ok")
	}

	return nil
}

// OpenEnv is a helper for opening an lmdbsync.Env.
func OpenEnv(path string) (*lmdbsync.Env, error) {
	env, err := lmdbsync.NewEnv(nil)
	if err != nil {
		return nil, err
	}
	err = env.Open(path, 0, 0644)
	if err != nil {
		env.Close()
		return nil, err
	}
	return env, nil
}

type handlerFunc func(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error)

func (fn handlerFunc) HandleTxnErr(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error) {
	return fn(ctx, env, err)
}
<commit_msg>lmdbsync: document the testresize command<commit_after>/*
Command testresize is a utility used by the lmdbsync tests to validate its
multiprocessing capabilities.  An external command like testresize command is
required because a process is not allowed to map the same process twice.

Testresize writes batches of updates into a databaes, transparently handling
any lmdb.MapResized or lmdb.MapFull errors that occur.  To ensure that resizing
behavior is observed testresize waits for input before updating the environment
and writes a line to stdout after the update is committed.  If testresize
process observes zero of either error it will exit with a non-zero exit code.

Two testresize processes can communicate to each other using two unix pipes, if
the output of each pipe is connected to the input of the other.  Writing a
single line to one of the pipes will cause updates to ping-pong back and forth
between processes.
*/
package main

import (
	"bufio"
	"crypto/rand"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"golang.org/x/net/context"

	"github.com/bmatsuo/lmdb-go/exp/lmdbsync"
	"github.com/bmatsuo/lmdb-go/lmdb"
)

func main() {
	numitems := flag.Int64("n", 5<<10, "the number of items to write")
	chunksize := flag.Int64("c", 100, "the number of items to write per txn")
	flag.Parse()

	failed := false
	defer func() {
		if failed {
			os.Exit(1)
		}
	}()
	fail := func(err error) {
		failed = true
		log.Print(err)
	}

	err := WriteRandomItems("db", *numitems, *chunksize)
	if err != nil {
		fail(err)
	} else {
		log.Printf("success")
	}
}

// WriteRandomItems writes numitem items with chunksize sized values full of
// random data.
func WriteRandomItems(path string, numitem, chunksize int64) (err error) {
	env, err := OpenEnv(path)
	if err != nil {
		return err
	}
	defer env.Close()

	numResize := 0
	numResized := 0
	defer func() {
		log.Printf("%d resizes", numResize)
		log.Printf("%d size adoptions", numResized)
		if err == nil {
			if numResize == 0 {
				err = fmt.Errorf("process did not resize the memory map")
			} else if numResized == 0 {
				err = fmt.Errorf("process did not adopt a new map size")
			}
		}
	}()
	mapResizedLogger := func(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error) {
		if lmdb.IsMapResized(err) {
			log.Printf("map resized")
			numResized++
		}
		return ctx, err
	}
	mapFullLogger := func(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error) {
		if lmdb.IsMapFull(err) {
			log.Printf("resize required")
			numResize++
		}
		return ctx, err
	}
	env.Handlers = env.Handlers.Append(
		handlerFunc(mapResizedLogger),
		lmdbsync.MapResizedHandler(2, func(int) time.Duration { return 100 * time.Microsecond }),
		handlerFunc(mapFullLogger),
		lmdbsync.MapFullHandler(func(size int64) (int64, bool) {
			newsize := size + 128<<10 // linear scale is bad -- but useful to test
			log.Printf("oldsize=%d newsize=%d", size, newsize)
			return newsize, true
		}),
	)

	pid := os.Getpid()

	scanner := bufio.NewScanner(os.Stdin)
	for i := int64(0); i < numitem; {
		if !scanner.Scan() {
			return scanner.Err()
		}

		start := i
		chunkmax := i + chunksize
		if chunkmax > numitem {
			chunkmax = numitem
		}
		v := make([]byte, 512)
		_, err := io.ReadFull(rand.Reader, v)
		if err != nil {
			return err
		}
		err = env.Update(func(txn *lmdb.Txn) (err error) {
			dbi, err := txn.OpenRoot(0)
			if err != nil {
				return err
			}

			for i = start; i < chunkmax; i++ {
				k := fmt.Sprintf("%06d-%016x", pid, i)
				err = txn.Put(dbi, []byte(k), v, 0)
				if err != nil {
					return err
				}
			}

			return nil
		})
		if err != nil {
			return err
		}
		fmt.Println("ok")
	}

	return nil
}

// OpenEnv is a helper for opening an lmdbsync.Env.
func OpenEnv(path string) (*lmdbsync.Env, error) {
	env, err := lmdbsync.NewEnv(nil)
	if err != nil {
		return nil, err
	}
	err = env.Open(path, 0, 0644)
	if err != nil {
		env.Close()
		return nil, err
	}
	return env, nil
}

type handlerFunc func(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error)

func (fn handlerFunc) HandleTxnErr(ctx context.Context, env *lmdbsync.Env, err error) (context.Context, error) {
	return fn(ctx, env, err)
}
<|endoftext|>


<commit_before>package graylog

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/config"
	"github.com/influxdata/telegraf/plugins/common/tls"
	"github.com/influxdata/telegraf/plugins/inputs"
)

type ResponseMetrics struct {
	Metrics []Metric `json:"metrics"`
}

type Metric struct {
	FullName string                 `json:"full_name"`
	Name     string                 `json:"name"`
	Type     string                 `json:"type"`
	Fields   map[string]interface{} `json:"metric"`
}

type GrayLog struct {
	Servers  []string
	Metrics  []string
	Username string
	Password string
	Timeout  config.Duration
	tls.ClientConfig

	client HTTPClient
}

type HTTPClient interface {
	// Returns the result of an http request
	//
	// Parameters:
	// req: HTTP request object
	//
	// Returns:
	// http.Response:  HTTP response object
	// error        :  Any error that may have occurred
	MakeRequest(req *http.Request) (*http.Response, error)

	SetHTTPClient(client *http.Client)
	HTTPClient() *http.Client
}

type Messagebody struct {
	Metrics []string `json:"metrics"`
}

type RealHTTPClient struct {
	client *http.Client
}

func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
	return c.client.Do(req)
}

func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *RealHTTPClient) HTTPClient() *http.Client {
	return c.client
}

var sampleConfig = `
  ## API endpoint, currently supported API:
  ##
  ##   - multiple  (e.g. http://<host>:9000/api/system/metrics/multiple)
  ##   - namespace (e.g. http://<host>:9000/api/system/metrics/namespace/{namespace})
  ##
  ## For namespace endpoint, the metrics array will be ignored for that call.
  ## Endpoint can contain namespace and multiple type calls.
  ##
  ## Please check http://[graylog-server-ip]:9000/api/api-browser for full list
  ## of endpoints
  servers = [
    "http://[graylog-server-ip]:9000/api/system/metrics/multiple",
  ]

  ## Set timeout (default 5 seconds)
  # timeout = "5s"

  ## Metrics list
  ## List of metrics can be found on Graylog webservice documentation.
  ## Or by hitting the web service api at:
  ##   http://[graylog-host]:9000/api/system/metrics
  metrics = [
    "jvm.cl.loaded",
    "jvm.memory.pools.Metaspace.committed"
  ]

  ## Username and password
  username = ""
  password = ""

  ## Optional TLS Config
  # tls_ca = "/etc/telegraf/ca.pem"
  # tls_cert = "/etc/telegraf/cert.pem"
  # tls_key = "/etc/telegraf/key.pem"
  ## Use TLS but skip chain & host verification
  # insecure_skip_verify = false
`

func (h *GrayLog) SampleConfig() string {
	return sampleConfig
}

func (h *GrayLog) Description() string {
	return "Read flattened metrics from one or more GrayLog HTTP endpoints"
}

// Gathers data for all servers.
func (h *GrayLog) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup

	if h.client.HTTPClient() == nil {
		tlsCfg, err := h.ClientConfig.TLSConfig()
		if err != nil {
			return err
		}
		tr := &http.Transport{
			ResponseHeaderTimeout: time.Duration(h.Timeout),
			TLSClientConfig:       tlsCfg,
		}
		client := &http.Client{
			Transport: tr,
			Timeout:   time.Duration(h.Timeout),
		}
		h.client.SetHTTPClient(client)
	}

	for _, server := range h.Servers {
		wg.Add(1)
		go func(server string) {
			defer wg.Done()
			acc.AddError(h.gatherServer(acc, server))
		}(server)
	}

	wg.Wait()

	return nil
}

// Gathers data from a particular server
// Parameters:
//     acc      : The telegraf Accumulator to use
//     serverURL: endpoint to send request to
//     service  : the service being queried
//
// Returns:
//     error: Any error that may have occurred
func (h *GrayLog) gatherServer(
	acc telegraf.Accumulator,
	serverURL string,
) error {
	resp, _, err := h.sendRequest(serverURL)
	if err != nil {
		return err
	}
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return fmt.Errorf("unable to parse address '%s': %s", serverURL, err)
	}

	host, port, _ := net.SplitHostPort(requestURL.Host)
	var dat ResponseMetrics
	if err := json.Unmarshal([]byte(resp), &dat); err != nil {
		return err
	}
	for _, mItem := range dat.Metrics {
		fields := make(map[string]interface{})
		tags := map[string]string{
			"server": host,
			"port":   port,
			"name":   mItem.Name,
			"type":   mItem.Type,
		}
		h.flatten(mItem.Fields, fields, "")
		acc.AddFields(mItem.FullName, fields, tags)
	}
	return nil
}

// Flatten JSON hierarchy to produce field name and field value
// Parameters:
//    item: Item map to flatten
//    fields: Map to store generated fields.
//    id: Prefix for top level metric (empty string "")
// Returns:
//    void
func (h *GrayLog) flatten(item map[string]interface{}, fields map[string]interface{}, id string) {
	if id != "" {
		id = id + "_"
	}
	for k, i := range item {
		switch i := i.(type) {
		case int:
			fields[id+k] = float64(i)
		case float64:
			fields[id+k] = i
		case map[string]interface{}:
			h.flatten(i, fields, id+k)
		default:
		}
	}
}

// Sends an HTTP request to the server using the GrayLog object's HTTPClient.
// Parameters:
//     serverURL: endpoint to send request to
//
// Returns:
//     string: body of the response
//     error : Any error that may have occurred
func (h *GrayLog) sendRequest(serverURL string) (string, float64, error) {
	headers := map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/json",
	}
	method := "GET"
	content := bytes.NewBufferString("")
	headers["Authorization"] = "Basic " + base64.URLEncoding.EncodeToString([]byte(h.Username+":"+h.Password))
	// Prepare URL
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return "", -1, fmt.Errorf("invalid server URL \"%s\"", serverURL)
	}
	// Add X-Requested-By header
	headers["X-Requested-By"] = "Telegraf"

	if strings.Contains(requestURL.String(), "multiple") {
		m := &Messagebody{Metrics: h.Metrics}
		httpBody, err := json.Marshal(m)
		if err != nil {
			return "", -1, fmt.Errorf("invalid list of Metrics %s", h.Metrics)
		}
		method = "POST"
		content = bytes.NewBuffer(httpBody)
	}
	req, err := http.NewRequest(method, requestURL.String(), content)
	if err != nil {
		return "", -1, err
	}
	// Add header parameters
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	start := time.Now()
	resp, err := h.client.MakeRequest(req)
	if err != nil {
		return "", -1, err
	}

	defer resp.Body.Close()
	responseTime := time.Since(start).Seconds()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return string(body), responseTime, err
	}

	// Process response
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("response from url \"%s\" has status code %d (%s), expected %d (%s)",
			requestURL.String(),
			resp.StatusCode,
			http.StatusText(resp.StatusCode),
			http.StatusOK,
			http.StatusText(http.StatusOK))
		return string(body), responseTime, err
	}
	return string(body), responseTime, err
}

func init() {
	inputs.Add("graylog", func() telegraf.Input {
		return &GrayLog{
			client:  &RealHTTPClient{},
			Timeout: config.Duration(5 * time.Second),
		}
	})
}
<commit_msg>fix: add graylog toml tags (#10660)<commit_after>package graylog

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/config"
	"github.com/influxdata/telegraf/plugins/common/tls"
	"github.com/influxdata/telegraf/plugins/inputs"
)

type ResponseMetrics struct {
	Metrics []Metric `json:"metrics"`
}

type Metric struct {
	FullName string                 `json:"full_name"`
	Name     string                 `json:"name"`
	Type     string                 `json:"type"`
	Fields   map[string]interface{} `json:"metric"`
}

type GrayLog struct {
	Servers  []string        `toml:"servers"`
	Metrics  []string        `toml:"metrics"`
	Username string          `toml:"username"`
	Password string          `toml:"password"`
	Timeout  config.Duration `toml:"timeout"`

	tls.ClientConfig
	client HTTPClient
}

type HTTPClient interface {
	// Returns the result of an http request
	//
	// Parameters:
	// req: HTTP request object
	//
	// Returns:
	// http.Response:  HTTP response object
	// error        :  Any error that may have occurred
	MakeRequest(req *http.Request) (*http.Response, error)

	SetHTTPClient(client *http.Client)
	HTTPClient() *http.Client
}

type Messagebody struct {
	Metrics []string `json:"metrics"`
}

type RealHTTPClient struct {
	client *http.Client
}

func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
	return c.client.Do(req)
}

func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *RealHTTPClient) HTTPClient() *http.Client {
	return c.client
}

var sampleConfig = `
  ## API endpoint, currently supported API:
  ##
  ##   - multiple  (e.g. http://<host>:9000/api/system/metrics/multiple)
  ##   - namespace (e.g. http://<host>:9000/api/system/metrics/namespace/{namespace})
  ##
  ## For namespace endpoint, the metrics array will be ignored for that call.
  ## Endpoint can contain namespace and multiple type calls.
  ##
  ## Please check http://[graylog-server-ip]:9000/api/api-browser for full list
  ## of endpoints
  servers = [
    "http://[graylog-server-ip]:9000/api/system/metrics/multiple",
  ]

  ## Set timeout (default 5 seconds)
  # timeout = "5s"

  ## Metrics list
  ## List of metrics can be found on Graylog webservice documentation.
  ## Or by hitting the web service api at:
  ##   http://[graylog-host]:9000/api/system/metrics
  metrics = [
    "jvm.cl.loaded",
    "jvm.memory.pools.Metaspace.committed"
  ]

  ## Username and password
  username = ""
  password = ""

  ## Optional TLS Config
  # tls_ca = "/etc/telegraf/ca.pem"
  # tls_cert = "/etc/telegraf/cert.pem"
  # tls_key = "/etc/telegraf/key.pem"
  ## Use TLS but skip chain & host verification
  # insecure_skip_verify = false
`

func (h *GrayLog) SampleConfig() string {
	return sampleConfig
}

func (h *GrayLog) Description() string {
	return "Read flattened metrics from one or more GrayLog HTTP endpoints"
}

// Gathers data for all servers.
func (h *GrayLog) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup

	if h.client.HTTPClient() == nil {
		tlsCfg, err := h.ClientConfig.TLSConfig()
		if err != nil {
			return err
		}
		tr := &http.Transport{
			ResponseHeaderTimeout: time.Duration(h.Timeout),
			TLSClientConfig:       tlsCfg,
		}
		client := &http.Client{
			Transport: tr,
			Timeout:   time.Duration(h.Timeout),
		}
		h.client.SetHTTPClient(client)
	}

	for _, server := range h.Servers {
		wg.Add(1)
		go func(server string) {
			defer wg.Done()
			acc.AddError(h.gatherServer(acc, server))
		}(server)
	}

	wg.Wait()

	return nil
}

// Gathers data from a particular server
// Parameters:
//     acc      : The telegraf Accumulator to use
//     serverURL: endpoint to send request to
//     service  : the service being queried
//
// Returns:
//     error: Any error that may have occurred
func (h *GrayLog) gatherServer(
	acc telegraf.Accumulator,
	serverURL string,
) error {
	resp, _, err := h.sendRequest(serverURL)
	if err != nil {
		return err
	}
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return fmt.Errorf("unable to parse address '%s': %s", serverURL, err)
	}

	host, port, _ := net.SplitHostPort(requestURL.Host)
	var dat ResponseMetrics
	if err := json.Unmarshal([]byte(resp), &dat); err != nil {
		return err
	}
	for _, mItem := range dat.Metrics {
		fields := make(map[string]interface{})
		tags := map[string]string{
			"server": host,
			"port":   port,
			"name":   mItem.Name,
			"type":   mItem.Type,
		}
		h.flatten(mItem.Fields, fields, "")
		acc.AddFields(mItem.FullName, fields, tags)
	}
	return nil
}

// Flatten JSON hierarchy to produce field name and field value
// Parameters:
//    item: Item map to flatten
//    fields: Map to store generated fields.
//    id: Prefix for top level metric (empty string "")
// Returns:
//    void
func (h *GrayLog) flatten(item map[string]interface{}, fields map[string]interface{}, id string) {
	if id != "" {
		id = id + "_"
	}
	for k, i := range item {
		switch i := i.(type) {
		case int:
			fields[id+k] = float64(i)
		case float64:
			fields[id+k] = i
		case map[string]interface{}:
			h.flatten(i, fields, id+k)
		default:
		}
	}
}

// Sends an HTTP request to the server using the GrayLog object's HTTPClient.
// Parameters:
//     serverURL: endpoint to send request to
//
// Returns:
//     string: body of the response
//     error : Any error that may have occurred
func (h *GrayLog) sendRequest(serverURL string) (string, float64, error) {
	headers := map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/json",
	}
	method := "GET"
	content := bytes.NewBufferString("")
	headers["Authorization"] = "Basic " + base64.URLEncoding.EncodeToString([]byte(h.Username+":"+h.Password))
	// Prepare URL
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return "", -1, fmt.Errorf("invalid server URL \"%s\"", serverURL)
	}
	// Add X-Requested-By header
	headers["X-Requested-By"] = "Telegraf"

	if strings.Contains(requestURL.String(), "multiple") {
		m := &Messagebody{Metrics: h.Metrics}
		httpBody, err := json.Marshal(m)
		if err != nil {
			return "", -1, fmt.Errorf("invalid list of Metrics %s", h.Metrics)
		}
		method = "POST"
		content = bytes.NewBuffer(httpBody)
	}
	req, err := http.NewRequest(method, requestURL.String(), content)
	if err != nil {
		return "", -1, err
	}
	// Add header parameters
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	start := time.Now()
	resp, err := h.client.MakeRequest(req)
	if err != nil {
		return "", -1, err
	}

	defer resp.Body.Close()
	responseTime := time.Since(start).Seconds()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return string(body), responseTime, err
	}

	// Process response
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("response from url \"%s\" has status code %d (%s), expected %d (%s)",
			requestURL.String(),
			resp.StatusCode,
			http.StatusText(resp.StatusCode),
			http.StatusOK,
			http.StatusText(http.StatusOK))
		return string(body), responseTime, err
	}
	return string(body), responseTime, err
}

func init() {
	inputs.Add("graylog", func() telegraf.Input {
		return &GrayLog{
			client:  &RealHTTPClient{},
			Timeout: config.Duration(5 * time.Second),
		}
	})
}
<|endoftext|>


<commit_before>package watcher

import (
	"autoscale"
	"fmt"
	"pkg/do"
	"pkg/util/rand"
	"strconv"
	"sync"

	"golang.org/x/oauth2"

	"github.com/Sirupsen/logrus"
	"github.com/digitalocean/godo"
)

// doClient is our interface to digitalocean
type doClient struct {
	TagsService     do.TagsService
	DropletsService do.DropletsService
}

// dropletConfig is a configurtion for building a droplet.
type dropletConfig struct {
	doc   *doClient
	wg    *sync.WaitGroup
	log   *logrus.Entry
	group autoscale.Group
	tmpl  autoscale.Template
	tag   string
}

// Watcher watches groups.
type Watcher struct {
	repo     autoscale.Repository
	log      *logrus.Entry
	doClient *doClient
}

// New creates an instance of Watcher.
func New(pat string, repo autoscale.Repository) *Watcher {
	godoClient := createClient(pat)

	dc := &doClient{
		DropletsService: do.NewDropletsService(godoClient),
		TagsService:     do.NewTagsService(godoClient),
	}

	return &Watcher{
		repo:     repo,
		log:      logrus.WithField("action", "watcher"),
		doClient: dc,
	}
}

// Watch starts the watching process.
func (w *Watcher) Watch() {
	log := w.log

	groups, err := w.repo.ListGroups()
	if err != nil {
		log.WithError(err).Error("list groups")
	}

	for _, group := range groups {
		log := log.WithField("group-name", group.Name)
		log.Info("watching group")

		w.Check(group, log)
	}
}

// Check group to make sure it is at capacity.
func (w *Watcher) Check(g autoscale.Group, log *logrus.Entry) {
	checkMinStatus(g, log, w.doClient, w.repo)
}

func checkMinStatus(g autoscale.Group, log *logrus.Entry, doc *doClient, repo autoscale.Repository) error {

	tag := fmt.Sprintf("do:as:%s", g.ID)
	if err := verifyTag(tag, doc, log); err != nil {
		return err
	}

	droplets, err := doc.DropletsService.ListByTag(tag)
	if err != nil {
		return err
	}

	dCount := len(droplets)
	if dCount < g.BaseSize {
		log.WithFields(logrus.Fields{
			"wanted-droplets": g.BaseSize,
			"actual-droplets": dCount,
		}).Info("group is missing resources")

		var wg sync.WaitGroup
		n := g.BaseSize - dCount
		wg.Add(n)

		tmpl, err := repo.GetTemplate(g.TemplateName)
		if err != nil {
			return err
		}

		dc := dropletConfig{
			doc:   doc,
			wg:    &wg,
			log:   log,
			group: g,
			tmpl:  tmpl,
			tag:   tag,
		}

		for i := 0; i < n; i++ {
			go bootDroplet(&dc)
		}

		log.Info("waiting droplets to be created")
		wg.Wait()
		log.Info("droplets have been created")
	} else {
		log.Info("autoscale group exists")
	}

	return nil
}

func bootDroplet(dc *dropletConfig) {
	defer dc.wg.Done()
	id := rand.String(5)

	name := fmt.Sprintf("%s-%s", dc.group.BaseName, id)
	log := dc.log.WithFields(logrus.Fields{
		"droplet-name": name,
	})

	keys := []godo.DropletCreateSSHKey{}
	for _, k := range dc.tmpl.SSHKeys {
		str, _ := strconv.Atoi(k)
		dcs := godo.DropletCreateSSHKey{ID: str}
		keys = append(keys, dcs)
	}

	dcr := godo.DropletCreateRequest{
		Name:    name,
		Region:  dc.tmpl.Region,
		Size:    dc.tmpl.Size,
		Image:   godo.DropletCreateImage{Slug: dc.tmpl.Image},
		SSHKeys: keys,
	}

	log.Info("creating droplet")

	droplet, err := dc.doc.DropletsService.Create(&dcr, true)
	if err != nil {
		log.WithError(err).Error("unable to create droplet")
	}

	log.Info("created droplet")

	trr := &godo.TagResourcesRequest{
		Resources: []godo.Resource{
			{ID: strconv.Itoa(droplet.ID), Type: godo.DropletResourceType},
		},
	}

	logWithTag := log.WithFields(logrus.Fields{
		"tag-name":   dc.tag,
		"droplet-id": droplet.ID,
	})

	logWithTag.Info("tagging droplet")
	if err := dc.doc.TagsService.TagResources(dc.tag, trr); err != nil {
		logWithTag.WithError(err).Error("unable to tag droplet")
	}
}

func verifyTag(tag string, doc *doClient, log *logrus.Entry) error {
	log.WithField("tag", tag).Info("checking if tag exists")
	tags, err := doc.TagsService.List()
	if err != nil {
		log.WithError(err).Error("unable to list tags")
		return err
	}

	tagFound := false
	for _, t := range tags {
		if t.Name == tag {
			tagFound = true
			break
		}
	}

	if !tagFound {
		log.WithField("tag", tag).Info("creating tag")
		tcr := godo.TagCreateRequest{Name: tag}
		if _, err := doc.TagsService.Create(&tcr); err != nil {
			log.WithError(err).WithField("tag", tag).Error("could not create tag")
			return err
		}
	}

	return nil
}

type tokenSource struct {
	AccessToken string
}

// Token creates a token
func (t *tokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func createClient(pat string) *godo.Client {
	ts := &tokenSource{
		AccessToken: pat,
	}

	oc := oauth2.NewClient(oauth2.NoContext, ts)
	return godo.NewClient(oc)
}
<commit_msg>check metrics periodically<commit_after>package watcher

import (
	"autoscale"
	"fmt"
	"pkg/do"
	"pkg/util/rand"
	"strconv"
	"sync"
	"time"

	"golang.org/x/oauth2"

	"github.com/Sirupsen/logrus"
	"github.com/digitalocean/godo"
)

// doClient is our interface to digitalocean
type doClient struct {
	TagsService     do.TagsService
	DropletsService do.DropletsService
}

// dropletConfig is a configurtion for building a droplet.
type dropletConfig struct {
	doc   *doClient
	wg    *sync.WaitGroup
	log   *logrus.Entry
	group autoscale.Group
	tmpl  autoscale.Template
	tag   string
}

// Watcher watches groups.
type Watcher struct {
	repo     autoscale.Repository
	log      *logrus.Entry
	doClient *doClient
}

// New creates an instance of Watcher.
func New(pat string, repo autoscale.Repository) *Watcher {
	godoClient := createClient(pat)

	dc := &doClient{
		DropletsService: do.NewDropletsService(godoClient),
		TagsService:     do.NewTagsService(godoClient),
	}

	return &Watcher{
		repo:     repo,
		log:      logrus.WithField("action", "watcher"),
		doClient: dc,
	}
}

// Watch starts the watching process.
func (w *Watcher) Watch() {
	log := w.log

	groups, err := w.repo.ListGroups()
	if err != nil {
		log.WithError(err).Error("list groups")
	}

	for _, group := range groups {
		log := log.WithField("group-name", group.Name)
		log.Info("watching group")

		go w.Check(group, log)
	}
}

// Check group to make sure it is at capacity.
func (w *Watcher) Check(g autoscale.Group, log *logrus.Entry) {
	if err := checkMinStatus(g, log, w.doClient, w.repo); err != nil {
		log.Error("check failed")
	}

	timer := time.NewTimer(10 * time.Second)
	<-timer.C
	w.Check(g, log)
}

func checkMinStatus(g autoscale.Group, log *logrus.Entry, doc *doClient, repo autoscale.Repository) error {

	tag := fmt.Sprintf("do:as:%s", g.ID)
	if err := verifyTag(tag, doc, log); err != nil {
		return err
	}

	droplets, err := doc.DropletsService.ListByTag(tag)
	if err != nil {
		return err
	}

	dCount := len(droplets)
	if dCount < g.BaseSize {
		log.WithFields(logrus.Fields{
			"wanted-droplets": g.BaseSize,
			"actual-droplets": dCount,
		}).Info("group is missing resources")

		var wg sync.WaitGroup
		n := g.BaseSize - dCount
		wg.Add(n)

		tmpl, err := repo.GetTemplate(g.TemplateName)
		if err != nil {
			return err
		}

		dc := dropletConfig{
			doc:   doc,
			wg:    &wg,
			log:   log,
			group: g,
			tmpl:  tmpl,
			tag:   tag,
		}

		for i := 0; i < n; i++ {
			go bootDroplet(&dc)
		}

		log.Info("waiting droplets to be created")
		wg.Wait()
		log.Info("droplets have been created")
	} else {
		log.Info("autoscale group exists")
	}

	return nil
}

func bootDroplet(dc *dropletConfig) {
	defer dc.wg.Done()
	id := rand.String(5)

	name := fmt.Sprintf("%s-%s", dc.group.BaseName, id)
	log := dc.log.WithFields(logrus.Fields{
		"droplet-name": name,
	})

	keys := []godo.DropletCreateSSHKey{}
	for _, k := range dc.tmpl.SSHKeys {
		str, _ := strconv.Atoi(k)
		dcs := godo.DropletCreateSSHKey{ID: str}
		keys = append(keys, dcs)
	}

	dcr := godo.DropletCreateRequest{
		Name:    name,
		Region:  dc.tmpl.Region,
		Size:    dc.tmpl.Size,
		Image:   godo.DropletCreateImage{Slug: dc.tmpl.Image},
		SSHKeys: keys,
	}

	log.Info("creating droplet")

	droplet, err := dc.doc.DropletsService.Create(&dcr, true)
	if err != nil {
		log.WithError(err).Error("unable to create droplet")
	}

	log.Info("created droplet")

	trr := &godo.TagResourcesRequest{
		Resources: []godo.Resource{
			{ID: strconv.Itoa(droplet.ID), Type: godo.DropletResourceType},
		},
	}

	logWithTag := log.WithFields(logrus.Fields{
		"tag-name":   dc.tag,
		"droplet-id": droplet.ID,
	})

	logWithTag.Info("tagging droplet")
	if err := dc.doc.TagsService.TagResources(dc.tag, trr); err != nil {
		logWithTag.WithError(err).Error("unable to tag droplet")
	}
}

func verifyTag(tag string, doc *doClient, log *logrus.Entry) error {
	log.WithField("tag", tag).Info("checking if tag exists")
	tags, err := doc.TagsService.List()
	if err != nil {
		log.WithError(err).Error("unable to list tags")
		return err
	}

	tagFound := false
	for _, t := range tags {
		if t.Name == tag {
			tagFound = true
			break
		}
	}

	if !tagFound {
		log.WithField("tag", tag).Info("creating tag")
		tcr := godo.TagCreateRequest{Name: tag}
		if _, err := doc.TagsService.Create(&tcr); err != nil {
			log.WithError(err).WithField("tag", tag).Error("could not create tag")
			return err
		}
	}

	return nil
}

type tokenSource struct {
	AccessToken string
}

// Token creates a token
func (t *tokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func createClient(pat string) *godo.Client {
	ts := &tokenSource{
		AccessToken: pat,
	}

	oc := oauth2.NewClient(oauth2.NoContext, ts)
	return godo.NewClient(oc)
}
<|endoftext|>


<commit_before>/*
 * Minio Cloud Storage, (C) 2018 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"fmt"
	"net/http"
	"os"
	"runtime"

	"github.com/minio/minio/cmd/logger"
)

const (
	minioHealthGoroutineThreshold = 1000
)

// ReadinessCheckHandler -- checks if there are more than threshold number of goroutines running,
// returns service unavailable.
// Readiness probes are used to detect situations where application is under heavy load
// and temporarily unable to serve. In a orchestrated setup like Kubernetes, containers reporting
// that they are not ready do not receive traffic through Kubernetes Services.
func ReadinessCheckHandler(w http.ResponseWriter, r *http.Request) {
	if err := goroutineCountCheck(minioHealthGoroutineThreshold); err != nil {
		writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
		return
	}
	writeResponse(w, http.StatusOK, nil, mimeNone)
}

// LivenessCheckHandler -- checks if server can reach its disks internally.
// If not, server is considered to have failed and needs to be restarted.
// Liveness probes are used to detect situations where application (minio)
// has gone into a state where it can not recover except by being restarted.
func LivenessCheckHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "LivenessCheckHandler")

	objLayer := newObjectLayerFn()
	// Service not initialized yet
	if objLayer == nil {
		writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
		return
	}

	s := objLayer.StorageInfo(ctx)
	// Gateways don't provide disk info, also handle special case for NAS gateway.
	if s.Backend.Type == Unknown || s.Backend.Type == BackendFS {
		// ListBuckets to confirm gateway backend is up
		if _, err := objLayer.ListBuckets(ctx); err != nil {
			writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
			return
		}
		writeResponse(w, http.StatusOK, nil, mimeNone)
		return
	}

	var totalLocalDisks int
	var erroredDisks int
	for _, endpoint := range globalEndpoints {
		// Check only if local disks are accessible, we do not have
		// to reach to rest of the other servers in a distributed setup.
		if endpoint.IsLocal {
			totalLocalDisks++
			// Attempt a stat to backend, any error resulting
			// from this Stat() operation is considered as backend
			// is not available, count them as errors.
			if _, err := os.Stat(endpoint.Path); err != nil {
				logger.LogIf(ctx, err)
				erroredDisks++
			}
		}
	}

	// If all exported local disks have errored, we simply let kubernetes
	// take us down.
	if totalLocalDisks == erroredDisks {
		writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
		return
	}
	writeResponse(w, http.StatusOK, nil, mimeNone)
}

// checks threshold against total number of go-routines in the system and throws error if
// more than threshold go-routines are running.
func goroutineCountCheck(threshold int) error {
	count := runtime.NumGoroutine()
	if count > threshold {
		return fmt.Errorf("too many goroutines (%d > %d)", count, threshold)
	}
	return nil
}
<commit_msg>Do not do StorageInfo() and ListBuckets() for FS/Erasure in health check handler (#7090)<commit_after>/*
 * Minio Cloud Storage, (C) 2018 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"fmt"
	"net/http"
	"os"
	"runtime"

	"github.com/minio/minio/cmd/logger"
)

const (
	minioHealthGoroutineThreshold = 1000
)

// ReadinessCheckHandler -- checks if there are more than threshold number of goroutines running,
// returns service unavailable.
// Readiness probes are used to detect situations where application is under heavy load
// and temporarily unable to serve. In a orchestrated setup like Kubernetes, containers reporting
// that they are not ready do not receive traffic through Kubernetes Services.
func ReadinessCheckHandler(w http.ResponseWriter, r *http.Request) {
	if err := goroutineCountCheck(minioHealthGoroutineThreshold); err != nil {
		writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
		return
	}
	writeResponse(w, http.StatusOK, nil, mimeNone)
}

// LivenessCheckHandler -- checks if server can reach its disks internally.
// If not, server is considered to have failed and needs to be restarted.
// Liveness probes are used to detect situations where application (minio)
// has gone into a state where it can not recover except by being restarted.
func LivenessCheckHandler(w http.ResponseWriter, r *http.Request) {
	ctx := newContext(r, w, "LivenessCheckHandler")

	objLayer := newObjectLayerFn()
	// Service not initialized yet
	if objLayer == nil {
		writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
		return
	}

	if !globalIsXL && !globalIsDistXL {
		s := objLayer.StorageInfo(ctx)
		// Gateways don't provide disk info.
		if s.Backend.Type == Unknown {
			// ListBuckets to confirm gateway backend is up
			if _, err := objLayer.ListBuckets(ctx); err != nil {
				writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
				return
			}
			writeResponse(w, http.StatusOK, nil, mimeNone)
			return
		}
	}

	// For FS and Erasure backend, check if local disks are up.
	var totalLocalDisks int
	var erroredDisks int
	for _, endpoint := range globalEndpoints {
		// Check only if local disks are accessible, we do not have
		// to reach to rest of the other servers in a distributed setup.
		if endpoint.IsLocal {
			totalLocalDisks++
			// Attempt a stat to backend, any error resulting
			// from this Stat() operation is considered as backend
			// is not available, count them as errors.
			if _, err := os.Stat(endpoint.Path); err != nil {
				logger.LogIf(ctx, err)
				erroredDisks++
			}
		}
	}

	// If all exported local disks have errored, we simply let kubernetes
	// take us down.
	if totalLocalDisks == erroredDisks {
		writeResponse(w, http.StatusServiceUnavailable, nil, mimeNone)
		return
	}
	writeResponse(w, http.StatusOK, nil, mimeNone)
}

// checks threshold against total number of go-routines in the system and throws error if
// more than threshold go-routines are running.
func goroutineCountCheck(threshold int) error {
	count := runtime.NumGoroutine()
	if count > threshold {
		return fmt.Errorf("too many goroutines (%d > %d)", count, threshold)
	}
	return nil
}
<|endoftext|>


<commit_before>package main_test

import (
	"reflect"
	"testing"
	"time"

	"github.com/influxdb/influxdb/cmd/influxd"
)

// Ensure that megabyte sizes can be parsed.
func TestSize_UnmarshalText_MB(t *testing.T) {
	var s main.Size
	if err := s.UnmarshalText([]byte("200m")); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if s != 200*(1<<20) {
		t.Fatalf("unexpected size: %d", s)
	}
}

// Ensure that gigabyte sizes can be parsed.
func TestSize_UnmarshalText_GB(t *testing.T) {
	if typ := reflect.TypeOf(0); typ.Size() != 8 {
		t.Skip("large gigabyte parsing on 64-bit arch only")
	}

	var s main.Size
	if err := s.UnmarshalText([]byte("10g")); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if s != 10*(1<<30) {
		t.Fatalf("unexpected size: %d", s)
	}
}

// Ensure that a TOML configuration file can be parsed into a Config.
func TestParseConfig(t *testing.T) {
	c, err := main.ParseConfig(testFile)
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if c.Hostname != "myserver.com" {
		t.Fatalf("hostname mismatch: %v", c.Hostname)
	}

	if c.Logging.File != "influxdb.log" {
		t.Fatalf("logging file mismatch: %v", c.Logging.File)
	} else if c.Logging.Level != "info" {
		t.Fatalf("logging level mismatch: %v", c.Logging.Level)
	}

	if c.Admin.Port != 8083 {
		t.Fatalf("admin port mismatch: %v", c.Admin.Port)
	} else if c.Admin.Assets != "./admin" {
		t.Fatalf("admin assets mismatch: %v", c.Admin.Assets)
	}

	if c.HTTPAPI.Port != 8086 {
		t.Fatalf("http api port mismatch: %v", c.HTTPAPI.Port)
	} else if c.HTTPAPI.SSLPort != 8087 {
		t.Fatalf("http api ssl port mismatch: %v", c.HTTPAPI.SSLPort)
	} else if c.HTTPAPI.SSLCertPath != "../cert.pem" {
		t.Fatalf("http api ssl cert path mismatch: %v", c.HTTPAPI.SSLCertPath)
	}

	if c.InputPlugins.Graphite.Enabled != false {
		t.Fatalf("graphite enabled mismatch: %v", c.InputPlugins.Graphite.Enabled)
	} else if c.InputPlugins.Graphite.Port != 2003 {
		t.Fatalf("graphite port mismatch: %v", c.InputPlugins.Graphite.Enabled)
	} else if c.InputPlugins.Graphite.Database != "" {
		t.Fatalf("graphite database mismatch: %v", c.InputPlugins.Graphite.Database)
	}

	if c.Broker.Port != 8090 {
		t.Fatalf("broker port mismatch: %v", c.Broker.Port)
	} else if c.Broker.Dir != "/tmp/influxdb/development/broker" {
		t.Fatalf("broker dir mismatch: %v", c.Broker.Dir)
	} else if time.Duration(c.Broker.Timeout) != time.Second {
		t.Fatalf("broker duration mismatch: %v", c.Broker.Timeout)
	}

	if c.Data.Dir != "/tmp/influxdb/development/db" {
		t.Fatalf("data dir mismatch: %v", c.Data.Dir)
	}

	if c.Cluster.ProtobufPort != 8099 {
		t.Fatalf("protobuf port mismatch: %v", c.Cluster.ProtobufPort)
	} else if time.Duration(c.Cluster.ProtobufTimeout) != 2*time.Second {
		t.Fatalf("protobuf timeout mismatch: %v", c.Cluster.ProtobufTimeout)
	} else if time.Duration(c.Cluster.ProtobufHeartbeatInterval) != 200*time.Millisecond {
		t.Fatalf("protobuf heartbeat interval mismatch: %v", c.Cluster.ProtobufHeartbeatInterval)
	} else if time.Duration(c.Cluster.MinBackoff) != 100*time.Millisecond {
		t.Fatalf("min backoff mismatch: %v", c.Cluster.MinBackoff)
	} else if time.Duration(c.Cluster.MaxBackoff) != 1*time.Second {
		t.Fatalf("max backoff mismatch: %v", c.Cluster.MaxBackoff)
	} else if c.Cluster.MaxResponseBufferSize != 5 {
		t.Fatalf("max response buffer size mismatch: %v", c.Cluster.MaxResponseBufferSize)
	}

	// TODO: UDP Servers testing.
	/*
		c.Assert(config.UdpServers, HasLen, 1)
		c.Assert(config.UdpServers[0].Enabled, Equals, true)
		c.Assert(config.UdpServers[0].Port, Equals, 4444)
		c.Assert(config.UdpServers[0].Database, Equals, "test")
	*/
}

// Testing configuration file.
const testFile = `
# Welcome to the InfluxDB configuration file.

# If hostname (on the OS) doesn't return a name that can be resolved by the other
# systems in the cluster, you'll have to set the hostname to an IP or something
# that can be resolved here.
hostname = "myserver.com"

[logging]
# logging level can be one of "debug", "info", "warn" or "error"
level  = "info"
file   = "influxdb.log"

# Configure the admin server
[admin]
port   = 8083                   # binding is disabled if the port isn't set
assets = "./admin"

# Configure the http api
[api]
ssl-port = 8087    # Ssl support is enabled if you set a port and cert
ssl-cert = "../cert.pem"

# connections will timeout after this amount of time. Ensures that clients that misbehave
# and keep alive connections they don't use won't end up connection a million times.
# However, if a request is taking longer than this to complete, could be a problem.
read-timeout = "5s"

[input_plugins]

  # Configure the graphite api
  [input_plugins.graphite]
  enabled = false
  port = 2003
  database = ""  # store graphite data in this database

  [input_plugins.udp]
  enabled = true
  port = 4444
  database = "test"

# Broker configuration
[broker]
# The broker port should be open between all servers in a cluster.
# However, this port shouldn't be accessible from the internet.

port = 8090

# Where the broker logs are stored. The user running InfluxDB will need read/write access.
dir  = "/tmp/influxdb/development/broker"

# election-timeout = "2s"

[data]
dir = "/tmp/influxdb/development/db"

# How many requests to potentially buffer in memory. If the buffer gets filled then writes
# will still be logged and once the local storage has caught up (or compacted) the writes
# will be replayed from the WAL
write-buffer-size = 10000

# The server will check this often for shards that have expired and should be cleared.
retention-sweep-period = "10m"

[cluster]
# A comma separated list of servers to seed
# this server. this is only relevant when the
# server is joining a new cluster. Otherwise
# the server will use the list of known servers
# prior to shutting down. Any server can be pointed to
# as a seed. It will find the Raft leader automatically.

# Here's an example. Note that the port on the host is the same as the broker port.
seed-servers = ["hosta:8090", "hostb:8090"]

# Replication happens over a TCP connection with a Protobuf protocol.
# This port should be reachable between all servers in a cluster.
# However, this port shouldn't be accessible from the internet.

protobuf_port = 8099
protobuf_timeout = "2s" # the write timeout on the protobuf conn any duration parseable by time.ParseDuration
protobuf_heartbeat = "200ms" # the heartbeat interval between the servers. must be parseable by time.ParseDuration
protobuf_min_backoff = "100ms" # the minimum backoff after a failed heartbeat attempt
protobuf_max_backoff = "1s" # the maxmimum backoff after a failed heartbeat attempt

# How many write requests to potentially buffer in memory per server. If the buffer gets filled then writes
# will still be logged and once the server has caught up (or come back online) the writes
# will be replayed from the WAL
write-buffer-size = 10000

# the maximum number of responses to buffer from remote nodes, if the
# expected number of responses exceed this number then querying will
# happen sequentially and the buffer size will be limited to this
# number
max-response-buffer-size = 5

# When queries get distributed out to shards, they go in parallel. This means that results can get buffered
# in memory since results will come in any order, but have to be processed in the correct time order.
# Setting this higher will give better performance, but you'll need more memory. Setting this to 1 will ensure
# that you don't need to buffer in memory, but you won't get the best performance.
concurrent-shard-query-limit = 10

[leveldb]

# Maximum mmap open files, this will affect the virtual memory used by
# the process
# max-open-files = 40
lru-cache-size = "200m"

# The default setting on this is 0, which means unlimited. Set this to
# something if you want to limit the max number of open
# files. max-open-files is per shard so this * that will be max.
# max-open-shards = 0

# The default setting is 100. This option tells how many points will be fetched from LevelDb before
# they get flushed into backend.
point-batch-size = 50
`
<commit_msg>Remove magic number.<commit_after>package main_test

import (
	"reflect"
	"testing"
	"time"

	"github.com/influxdb/influxdb/cmd/influxd"
)

// Ensure that megabyte sizes can be parsed.
func TestSize_UnmarshalText_MB(t *testing.T) {
	var s main.Size
	if err := s.UnmarshalText([]byte("200m")); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if s != 200*(1<<20) {
		t.Fatalf("unexpected size: %d", s)
	}
}

// Ensure that gigabyte sizes can be parsed.
func TestSize_UnmarshalText_GB(t *testing.T) {
	if typ := reflect.TypeOf(0); typ.Size() != 8 {
		t.Skip("large gigabyte parsing on 64-bit arch only")
	}

	var s main.Size
	if err := s.UnmarshalText([]byte("10g")); err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if s != 10*(1<<30) {
		t.Fatalf("unexpected size: %d", s)
	}
}

// Ensure that a TOML configuration file can be parsed into a Config.
func TestParseConfig(t *testing.T) {
	c, err := main.ParseConfig(testFile)
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	} else if c.Hostname != "myserver.com" {
		t.Fatalf("hostname mismatch: %v", c.Hostname)
	}

	if c.Logging.File != "influxdb.log" {
		t.Fatalf("logging file mismatch: %v", c.Logging.File)
	} else if c.Logging.Level != "info" {
		t.Fatalf("logging level mismatch: %v", c.Logging.Level)
	}

	if c.Admin.Port != 8083 {
		t.Fatalf("admin port mismatch: %v", c.Admin.Port)
	} else if c.Admin.Assets != "./admin" {
		t.Fatalf("admin assets mismatch: %v", c.Admin.Assets)
	}

	if c.HTTPAPI.Port != main.DefaultBrokerPort {
		t.Fatalf("http api port mismatch: %v", c.HTTPAPI.Port)
	} else if c.HTTPAPI.SSLPort != 8087 {
		t.Fatalf("http api ssl port mismatch: %v", c.HTTPAPI.SSLPort)
	} else if c.HTTPAPI.SSLCertPath != "../cert.pem" {
		t.Fatalf("http api ssl cert path mismatch: %v", c.HTTPAPI.SSLCertPath)
	}

	if c.InputPlugins.Graphite.Enabled != false {
		t.Fatalf("graphite enabled mismatch: %v", c.InputPlugins.Graphite.Enabled)
	} else if c.InputPlugins.Graphite.Port != 2003 {
		t.Fatalf("graphite port mismatch: %v", c.InputPlugins.Graphite.Enabled)
	} else if c.InputPlugins.Graphite.Database != "" {
		t.Fatalf("graphite database mismatch: %v", c.InputPlugins.Graphite.Database)
	}

	if c.Broker.Port != 8090 {
		t.Fatalf("broker port mismatch: %v", c.Broker.Port)
	} else if c.Broker.Dir != "/tmp/influxdb/development/broker" {
		t.Fatalf("broker dir mismatch: %v", c.Broker.Dir)
	} else if time.Duration(c.Broker.Timeout) != time.Second {
		t.Fatalf("broker duration mismatch: %v", c.Broker.Timeout)
	}

	if c.Data.Dir != "/tmp/influxdb/development/db" {
		t.Fatalf("data dir mismatch: %v", c.Data.Dir)
	}

	if c.Cluster.ProtobufPort != 8099 {
		t.Fatalf("protobuf port mismatch: %v", c.Cluster.ProtobufPort)
	} else if time.Duration(c.Cluster.ProtobufTimeout) != 2*time.Second {
		t.Fatalf("protobuf timeout mismatch: %v", c.Cluster.ProtobufTimeout)
	} else if time.Duration(c.Cluster.ProtobufHeartbeatInterval) != 200*time.Millisecond {
		t.Fatalf("protobuf heartbeat interval mismatch: %v", c.Cluster.ProtobufHeartbeatInterval)
	} else if time.Duration(c.Cluster.MinBackoff) != 100*time.Millisecond {
		t.Fatalf("min backoff mismatch: %v", c.Cluster.MinBackoff)
	} else if time.Duration(c.Cluster.MaxBackoff) != 1*time.Second {
		t.Fatalf("max backoff mismatch: %v", c.Cluster.MaxBackoff)
	} else if c.Cluster.MaxResponseBufferSize != 5 {
		t.Fatalf("max response buffer size mismatch: %v", c.Cluster.MaxResponseBufferSize)
	}

	// TODO: UDP Servers testing.
	/*
		c.Assert(config.UdpServers, HasLen, 1)
		c.Assert(config.UdpServers[0].Enabled, Equals, true)
		c.Assert(config.UdpServers[0].Port, Equals, 4444)
		c.Assert(config.UdpServers[0].Database, Equals, "test")
	*/
}

// Testing configuration file.
const testFile = `
# Welcome to the InfluxDB configuration file.

# If hostname (on the OS) doesn't return a name that can be resolved by the other
# systems in the cluster, you'll have to set the hostname to an IP or something
# that can be resolved here.
hostname = "myserver.com"

[logging]
# logging level can be one of "debug", "info", "warn" or "error"
level  = "info"
file   = "influxdb.log"

# Configure the admin server
[admin]
port   = 8083                   # binding is disabled if the port isn't set
assets = "./admin"

# Configure the http api
[api]
ssl-port = 8087    # Ssl support is enabled if you set a port and cert
ssl-cert = "../cert.pem"

# connections will timeout after this amount of time. Ensures that clients that misbehave
# and keep alive connections they don't use won't end up connection a million times.
# However, if a request is taking longer than this to complete, could be a problem.
read-timeout = "5s"

[input_plugins]

  # Configure the graphite api
  [input_plugins.graphite]
  enabled = false
  port = 2003
  database = ""  # store graphite data in this database

  [input_plugins.udp]
  enabled = true
  port = 4444
  database = "test"

# Broker configuration
[broker]
# The broker port should be open between all servers in a cluster.
# However, this port shouldn't be accessible from the internet.

port = 8090

# Where the broker logs are stored. The user running InfluxDB will need read/write access.
dir  = "/tmp/influxdb/development/broker"

# election-timeout = "2s"

[data]
dir = "/tmp/influxdb/development/db"

# How many requests to potentially buffer in memory. If the buffer gets filled then writes
# will still be logged and once the local storage has caught up (or compacted) the writes
# will be replayed from the WAL
write-buffer-size = 10000

# The server will check this often for shards that have expired and should be cleared.
retention-sweep-period = "10m"

[cluster]
# A comma separated list of servers to seed
# this server. this is only relevant when the
# server is joining a new cluster. Otherwise
# the server will use the list of known servers
# prior to shutting down. Any server can be pointed to
# as a seed. It will find the Raft leader automatically.

# Here's an example. Note that the port on the host is the same as the broker port.
seed-servers = ["hosta:8090", "hostb:8090"]

# Replication happens over a TCP connection with a Protobuf protocol.
# This port should be reachable between all servers in a cluster.
# However, this port shouldn't be accessible from the internet.

protobuf_port = 8099
protobuf_timeout = "2s" # the write timeout on the protobuf conn any duration parseable by time.ParseDuration
protobuf_heartbeat = "200ms" # the heartbeat interval between the servers. must be parseable by time.ParseDuration
protobuf_min_backoff = "100ms" # the minimum backoff after a failed heartbeat attempt
protobuf_max_backoff = "1s" # the maxmimum backoff after a failed heartbeat attempt

# How many write requests to potentially buffer in memory per server. If the buffer gets filled then writes
# will still be logged and once the server has caught up (or come back online) the writes
# will be replayed from the WAL
write-buffer-size = 10000

# the maximum number of responses to buffer from remote nodes, if the
# expected number of responses exceed this number then querying will
# happen sequentially and the buffer size will be limited to this
# number
max-response-buffer-size = 5

# When queries get distributed out to shards, they go in parallel. This means that results can get buffered
# in memory since results will come in any order, but have to be processed in the correct time order.
# Setting this higher will give better performance, but you'll need more memory. Setting this to 1 will ensure
# that you don't need to buffer in memory, but you won't get the best performance.
concurrent-shard-query-limit = 10

[leveldb]

# Maximum mmap open files, this will affect the virtual memory used by
# the process
# max-open-files = 40
lru-cache-size = "200m"

# The default setting on this is 0, which means unlimited. Set this to
# something if you want to limit the max number of open
# files. max-open-files is per shard so this * that will be max.
# max-open-shards = 0

# The default setting is 100. This option tells how many points will be fetched from LevelDb before
# they get flushed into backend.
point-batch-size = 50
`
<|endoftext|>