<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 verify_test

import (
	"errors"
	"fmt"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/blob/mock"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/graph"
	"github.com/jacobsa/comeback/repr"
	"github.com/jacobsa/comeback/verify"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
)

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

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

func makeNodeName(
	dir bool,
	score blob.Score) (s string) {
	n := verify.Node{
		Dir:   dir,
		Score: score,
	}

	s = n.String()
	return
}

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

const namePrefix = "blobs/"

type superTest struct {
	ctx            context.Context
	records        chan verify.Record
	knownStructure map[verify.Node][]verify.Node
	blobStore      mock_blob.MockStore
	clock          timeutil.SimulatedClock

	visitor graph.Visitor
}

func (t *superTest) setUp(
	ti *TestInfo,
	readFiles bool,
	allScores []blob.Score) {
	t.ctx = ti.Ctx
	t.records = make(chan verify.Record, 1000)
	t.knownStructure = make(map[verify.Node][]verify.Node)
	t.blobStore = mock_blob.NewMockStore(ti.MockController, "blobStore")
	t.clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))

	t.visitor = verify.NewVisitor(
		readFiles,
		allScores,
		t.knownStructure,
		t.records,
		&t.clock,
		t.blobStore)
}

func (t *superTest) getRecords() (records []verify.Record) {
	close(t.records)
	for r := range t.records {
		records = append(records, r)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Common
////////////////////////////////////////////////////////////////////////

type CommonTest struct {
	superTest
}

var _ SetUpInterface = &CommonTest{}

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

func (t *CommonTest) SetUp(ti *TestInfo) {
	t.superTest.setUp(ti, false, nil)
}

func (t *CommonTest) InvalidNodeName() {
	_, err := t.visitor.Visit(t.ctx, "taco")

	ExpectThat(err, Error(HasSubstr("ParseNode")))
	ExpectThat(err, Error(HasSubstr("taco")))
}

////////////////////////////////////////////////////////////////////////
// Directories
////////////////////////////////////////////////////////////////////////

type DirsTest struct {
	superTest

	listing  []*fs.DirectoryEntry
	contents []byte
	score    blob.Score
	node     string
}

var _ SetUpInterface = &DirsTest{}

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

func (t *DirsTest) SetUp(ti *TestInfo) {
	t.superTest.setUp(ti, false, nil)

	// Set up canned data for a valid listing.
	t.listing = []*fs.DirectoryEntry{
		&fs.DirectoryEntry{
			Type: fs.TypeFile,
			Name: "foo",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("0")),
				blob.ComputeScore([]byte("1")),
			},
		},

		&fs.DirectoryEntry{
			Type: fs.TypeDirectory,
			Name: "bar",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("2")),
			},
		},

		&fs.DirectoryEntry{
			Type:   fs.TypeSymlink,
			Name:   "baz",
			Target: "asdf",
		},
	}

	var err error
	t.contents, err = repr.MarshalDir(t.listing)
	AssertEq(nil, err)

	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)
}

func (t *DirsTest) NodeVisitedOnPastRun_ScoreAbsent() {
	AssertTrue(false, "TODO")
}

func (t *DirsTest) NodeVisitedOnPastRun_ScorePresent() {
	AssertTrue(false, "TODO")
}

func (t *DirsTest) CallsBlobStore() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), t.score).
		WillOnce(Return(nil, errors.New("")))

	// Call
	t.visitor.Visit(t.ctx, t.node)
}

func (t *DirsTest) BlobStoreReturnsError() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err := t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr("Load")))
	ExpectThat(err, Error(HasSubstr("taco")))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) BlobIsJunk() {
	// Set up junk contents.
	t.contents = append(t.contents, 'a')
	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)

	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	_, err := t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr(t.score.Hex())))
	ExpectThat(err, Error(HasSubstr("UnmarshalDir")))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) UnknownEntryType() {
	// Set up a listing with an unsupported entry type.
	t.listing = []*fs.DirectoryEntry{
		&fs.DirectoryEntry{
			Type: fs.TypeCharDevice,
			Name: "foo",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("0")),
			},
		},
	}

	var err error
	t.contents, err = repr.MarshalDir(t.listing)
	AssertEq(nil, err)

	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)

	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	_, err = t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr("entry type")))
	ExpectThat(err, Error(HasSubstr(fmt.Sprintf("%d", fs.TypeCharDevice))))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) SymlinkWithScores() {
	// Set up a listing with a symlink that unexpectedly has associated scores.
	t.listing = []*fs.DirectoryEntry{
		&fs.DirectoryEntry{
			Type: fs.TypeSymlink,
			Name: "foo",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("0")),
			},
		},
	}

	var err error
	t.contents, err = repr.MarshalDir(t.listing)
	AssertEq(nil, err)

	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)

	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	_, err = t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr(t.score.Hex())))
	ExpectThat(err, Error(HasSubstr("symlink")))
	ExpectThat(err, Error(HasSubstr("scores")))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) ReturnsAppropriateAdjacentNodesAndRecords() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	adjacent, err := t.visitor.Visit(t.ctx, t.node)
	AssertEq(nil, err)

	var expected []interface{}
	for _, entry := range t.listing {
		dir := entry.Type == fs.TypeDirectory
		for _, score := range entry.Scores {
			expected = append(expected, makeNodeName(dir, score))
		}
	}

	// Check adjacent.
	ExpectThat(adjacent, ElementsAre(expected...))

	// Check records.
	records := t.getRecords()
	AssertEq(1, len(records))
	var r verify.Record

	r = records[0]
	ExpectThat(r.Time, timeutil.TimeEq(t.clock.Now()))
	ExpectTrue(r.Node.Dir)
	ExpectEq(t.score, r.Node.Score)

	var childNames []string
	for _, child := range r.Children {
		childNames = append(childNames, child.String())
	}

	ExpectThat(childNames, ElementsAre(expected...))
}

////////////////////////////////////////////////////////////////////////
// Files, no read
////////////////////////////////////////////////////////////////////////

type filesCommonTest struct {
	superTest

	contents []byte

	knownScore blob.Score
	knownNode  string

	unknownScore blob.Score
	unknownNode  string
}

func (t *filesCommonTest) setUp(ti *TestInfo, readFiles bool) {
	t.contents = []byte("foobarbaz")

	t.knownScore = blob.ComputeScore(t.contents)
	t.knownNode = makeNodeName(false, t.knownScore)

	t.unknownScore = blob.ComputeScore(append(t.contents, 'a'))
	t.unknownNode = makeNodeName(false, t.unknownScore)

	t.superTest.setUp(ti, readFiles, []blob.Score{t.knownScore})
}

type FilesLiteTest struct {
	filesCommonTest
}

var _ SetUpInterface = &FilesLiteTest{}

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

func (t *FilesLiteTest) SetUp(ti *TestInfo) {
	t.filesCommonTest.setUp(ti, false)
}

func (t *FilesLiteTest) NodeVisitedOnPastRun_ScoreAbsent() {
	AssertTrue(false, "TODO")
}

func (t *FilesLiteTest) NodeVisitedOnPastRun_ScorePresent() {
	AssertTrue(false, "TODO")
}

func (t *FilesLiteTest) ScoreNotInList() {
	// Call
	_, err := t.visitor.Visit(t.ctx, t.unknownNode)

	ExpectThat(err, Error(HasSubstr("Unknown")))
	ExpectThat(err, Error(HasSubstr("score")))
	ExpectThat(err, Error(HasSubstr(t.unknownScore.Hex())))

	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *FilesLiteTest) ScoreIsInList() {
	// Call
	adjacent, err := t.visitor.Visit(t.ctx, t.knownNode)

	AssertEq(nil, err)
	ExpectThat(adjacent, ElementsAre())
	ExpectThat(t.getRecords(), ElementsAre())
}

////////////////////////////////////////////////////////////////////////
// Files, read
////////////////////////////////////////////////////////////////////////

type FilesFullTest struct {
	filesCommonTest
}

var _ SetUpInterface = &FilesFullTest{}

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

func (t *FilesFullTest) SetUp(ti *TestInfo) {
	t.filesCommonTest.setUp(ti, true)
}

func (t *FilesFullTest) NodeVisitedOnPastRun_ScoreAbsent() {
	AssertTrue(false, "TODO")
}

func (t *FilesFullTest) NodeVisitedOnPastRun_ScorePresent() {
	AssertTrue(false, "TODO")
}

func (t *FilesFullTest) CallsBlobStore() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), t.knownScore).
		WillOnce(Return(nil, errors.New("")))

	// Call
	t.visitor.Visit(t.ctx, t.knownNode)
}

func (t *FilesFullTest) BlobStoreReturnsError() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err := t.visitor.Visit(t.ctx, t.knownNode)

	ExpectThat(err, Error(HasSubstr("Load")))
	ExpectThat(err, Error(HasSubstr("taco")))

	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *FilesFullTest) BlobStoreSucceeds() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	adjacent, err := t.visitor.Visit(t.ctx, t.knownNode)

	AssertEq(nil, err)
	ExpectThat(adjacent, ElementsAre())

	records := t.getRecords()
	AssertEq(1, len(records))

	r := records[0]
	ExpectThat(r.Time, timeutil.TimeEq(t.clock.Now()))
	ExpectFalse(r.Node.Dir)
	ExpectEq(t.knownScore, r.Node.Score)
	ExpectThat(r.Children, ElementsAre())
}
<commit_msg>Refactored filesCommonTest.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 verify_test

import (
	"errors"
	"fmt"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/blob/mock"
	"github.com/jacobsa/comeback/fs"
	"github.com/jacobsa/comeback/graph"
	"github.com/jacobsa/comeback/repr"
	"github.com/jacobsa/comeback/verify"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
)

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

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

func makeNodeName(
	dir bool,
	score blob.Score) (s string) {
	n := verify.Node{
		Dir:   dir,
		Score: score,
	}

	s = n.String()
	return
}

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

const namePrefix = "blobs/"

type superTest struct {
	ctx            context.Context
	records        chan verify.Record
	knownStructure map[verify.Node][]verify.Node
	blobStore      mock_blob.MockStore
	clock          timeutil.SimulatedClock

	visitor graph.Visitor
}

func (t *superTest) setUp(
	ti *TestInfo,
	readFiles bool,
	allScores []blob.Score) {
	t.ctx = ti.Ctx
	t.records = make(chan verify.Record, 1000)
	t.knownStructure = make(map[verify.Node][]verify.Node)
	t.blobStore = mock_blob.NewMockStore(ti.MockController, "blobStore")
	t.clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))

	t.visitor = verify.NewVisitor(
		readFiles,
		allScores,
		t.knownStructure,
		t.records,
		&t.clock,
		t.blobStore)
}

func (t *superTest) getRecords() (records []verify.Record) {
	close(t.records)
	for r := range t.records {
		records = append(records, r)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Common
////////////////////////////////////////////////////////////////////////

type CommonTest struct {
	superTest
}

var _ SetUpInterface = &CommonTest{}

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

func (t *CommonTest) SetUp(ti *TestInfo) {
	t.superTest.setUp(ti, false, nil)
}

func (t *CommonTest) InvalidNodeName() {
	_, err := t.visitor.Visit(t.ctx, "taco")

	ExpectThat(err, Error(HasSubstr("ParseNode")))
	ExpectThat(err, Error(HasSubstr("taco")))
}

////////////////////////////////////////////////////////////////////////
// Directories
////////////////////////////////////////////////////////////////////////

type DirsTest struct {
	superTest

	listing  []*fs.DirectoryEntry
	contents []byte
	score    blob.Score
	node     string
}

var _ SetUpInterface = &DirsTest{}

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

func (t *DirsTest) SetUp(ti *TestInfo) {
	t.superTest.setUp(ti, false, nil)

	// Set up canned data for a valid listing.
	t.listing = []*fs.DirectoryEntry{
		&fs.DirectoryEntry{
			Type: fs.TypeFile,
			Name: "foo",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("0")),
				blob.ComputeScore([]byte("1")),
			},
		},

		&fs.DirectoryEntry{
			Type: fs.TypeDirectory,
			Name: "bar",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("2")),
			},
		},

		&fs.DirectoryEntry{
			Type:   fs.TypeSymlink,
			Name:   "baz",
			Target: "asdf",
		},
	}

	var err error
	t.contents, err = repr.MarshalDir(t.listing)
	AssertEq(nil, err)

	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)
}

func (t *DirsTest) NodeVisitedOnPastRun_ScoreAbsent() {
	AssertTrue(false, "TODO")
}

func (t *DirsTest) NodeVisitedOnPastRun_ScorePresent() {
	AssertTrue(false, "TODO")
}

func (t *DirsTest) CallsBlobStore() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), t.score).
		WillOnce(Return(nil, errors.New("")))

	// Call
	t.visitor.Visit(t.ctx, t.node)
}

func (t *DirsTest) BlobStoreReturnsError() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err := t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr("Load")))
	ExpectThat(err, Error(HasSubstr("taco")))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) BlobIsJunk() {
	// Set up junk contents.
	t.contents = append(t.contents, 'a')
	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)

	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	_, err := t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr(t.score.Hex())))
	ExpectThat(err, Error(HasSubstr("UnmarshalDir")))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) UnknownEntryType() {
	// Set up a listing with an unsupported entry type.
	t.listing = []*fs.DirectoryEntry{
		&fs.DirectoryEntry{
			Type: fs.TypeCharDevice,
			Name: "foo",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("0")),
			},
		},
	}

	var err error
	t.contents, err = repr.MarshalDir(t.listing)
	AssertEq(nil, err)

	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)

	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	_, err = t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr("entry type")))
	ExpectThat(err, Error(HasSubstr(fmt.Sprintf("%d", fs.TypeCharDevice))))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) SymlinkWithScores() {
	// Set up a listing with a symlink that unexpectedly has associated scores.
	t.listing = []*fs.DirectoryEntry{
		&fs.DirectoryEntry{
			Type: fs.TypeSymlink,
			Name: "foo",
			Scores: []blob.Score{
				blob.ComputeScore([]byte("0")),
			},
		},
	}

	var err error
	t.contents, err = repr.MarshalDir(t.listing)
	AssertEq(nil, err)

	t.score = blob.ComputeScore(t.contents)
	t.node = makeNodeName(true, t.score)

	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	_, err = t.visitor.Visit(t.ctx, t.node)

	ExpectThat(err, Error(HasSubstr(t.score.Hex())))
	ExpectThat(err, Error(HasSubstr("symlink")))
	ExpectThat(err, Error(HasSubstr("scores")))
	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *DirsTest) ReturnsAppropriateAdjacentNodesAndRecords() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	adjacent, err := t.visitor.Visit(t.ctx, t.node)
	AssertEq(nil, err)

	var expected []interface{}
	for _, entry := range t.listing {
		dir := entry.Type == fs.TypeDirectory
		for _, score := range entry.Scores {
			expected = append(expected, makeNodeName(dir, score))
		}
	}

	// Check adjacent.
	ExpectThat(adjacent, ElementsAre(expected...))

	// Check records.
	records := t.getRecords()
	AssertEq(1, len(records))
	var r verify.Record

	r = records[0]
	ExpectThat(r.Time, timeutil.TimeEq(t.clock.Now()))
	ExpectTrue(r.Node.Dir)
	ExpectEq(t.score, r.Node.Score)

	var childNames []string
	for _, child := range r.Children {
		childNames = append(childNames, child.String())
	}

	ExpectThat(childNames, ElementsAre(expected...))
}

////////////////////////////////////////////////////////////////////////
// Files, no read
////////////////////////////////////////////////////////////////////////

type filesCommonTest struct {
	superTest

	contents []byte

	knownNode   verify.Node
	unknownNode verify.Node
}

func (t *filesCommonTest) setUp(ti *TestInfo, readFiles bool) {
	t.contents = []byte("foobarbaz")

	// Set up canned nodes.
	t.knownNode = verify.Node{
		Dir:   false,
		Score: blob.ComputeScore(t.contents),
	}

	t.unknownNode = verify.Node{
		Dir:   false,
		Score: blob.ComputeScore(append(t.contents, 'a')),
	}

	t.superTest.setUp(ti, readFiles, []blob.Score{t.knownNode.Score})
}

type FilesLiteTest struct {
	filesCommonTest
}

var _ SetUpInterface = &FilesLiteTest{}

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

func (t *FilesLiteTest) SetUp(ti *TestInfo) {
	t.filesCommonTest.setUp(ti, false)
}

func (t *FilesLiteTest) NodeVisitedOnPastRun_ScoreAbsent() {
	AssertTrue(false, "TODO")
}

func (t *FilesLiteTest) NodeVisitedOnPastRun_ScorePresent() {
	AssertTrue(false, "TODO")
}

func (t *FilesLiteTest) ScoreNotInList() {
	// Call
	_, err := t.visitor.Visit(t.ctx, t.unknownNode.String())

	ExpectThat(err, Error(HasSubstr("Unknown")))
	ExpectThat(err, Error(HasSubstr("score")))
	ExpectThat(err, Error(HasSubstr(t.unknownNode.Score.Hex())))

	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *FilesLiteTest) ScoreIsInList() {
	// Call
	adjacent, err := t.visitor.Visit(t.ctx, t.knownNode.String())

	AssertEq(nil, err)
	ExpectThat(adjacent, ElementsAre())
	ExpectThat(t.getRecords(), ElementsAre())
}

////////////////////////////////////////////////////////////////////////
// Files, read
////////////////////////////////////////////////////////////////////////

type FilesFullTest struct {
	filesCommonTest
}

var _ SetUpInterface = &FilesFullTest{}

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

func (t *FilesFullTest) SetUp(ti *TestInfo) {
	t.filesCommonTest.setUp(ti, true)
}

func (t *FilesFullTest) NodeVisitedOnPastRun_ScoreAbsent() {
	AssertTrue(false, "TODO")
}

func (t *FilesFullTest) NodeVisitedOnPastRun_ScorePresent() {
	AssertTrue(false, "TODO")
}

func (t *FilesFullTest) CallsBlobStore() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), t.knownNode.Score).
		WillOnce(Return(nil, errors.New("")))

	// Call
	t.visitor.Visit(t.ctx, t.knownNode.String())
}

func (t *FilesFullTest) BlobStoreReturnsError() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err := t.visitor.Visit(t.ctx, t.knownNode.String())

	ExpectThat(err, Error(HasSubstr("Load")))
	ExpectThat(err, Error(HasSubstr("taco")))

	ExpectThat(t.getRecords(), ElementsAre())
}

func (t *FilesFullTest) BlobStoreSucceeds() {
	// Load
	ExpectCall(t.blobStore, "Load")(Any(), Any()).
		WillOnce(Return(t.contents, nil))

	// Call
	adjacent, err := t.visitor.Visit(t.ctx, t.knownNode.String())

	AssertEq(nil, err)
	ExpectThat(adjacent, ElementsAre())

	records := t.getRecords()
	AssertEq(1, len(records))

	r := records[0]
	ExpectThat(r.Time, timeutil.TimeEq(t.clock.Now()))
	ExpectFalse(r.Node.Dir)
	ExpectEq(t.knownNode.Score, r.Node.Score)
	ExpectThat(r.Children, ElementsAre())
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"sync"
	"time"

	blocks "github.com/ipfs/go-ipfs/blocks"
	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"

	floodsub "gx/ipfs/QmZ3zAKL7KkPdnd5dWrkiEgC2H22kaa7QBiYSAhM2xQhb4/floodsub"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
	key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key"
	pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore"
	cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid"
)

var PubsubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "An experimental publish-subscribe system on ipfs.",
		ShortDescription: `
ipfs pubsub allows you to publish messages to a given topic, and also to
subscribe to new messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"pub":   PubsubPubCmd,
		"sub":   PubsubSubCmd,
		"ls":    PubsubLsCmd,
		"peers": PubsubPeersCmd,
	},
}

var PubsubSubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Subscribe to messages on a given topic.",
		ShortDescription: `
ipfs pubsub sub subscribes to messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", true, false, "String name of topic to subscribe to."),
	},
	Options: []cmds.Option{
		cmds.BoolOption("discover", "try to discover other peers subscribed to the same topic"),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		topic := req.Arguments()[0]
		msgs, err := n.Floodsub.Subscribe(req.Context(), topic)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		out := make(chan interface{})
		res.SetOutput((<-chan interface{})(out))

		ctx := req.Context()
		go func() {
			defer close(out)
			for {
				select {
				case msg, ok := <-msgs:
					if !ok {
						return
					}
					out <- msg
				case <-ctx.Done():
					n.Floodsub.Unsub(topic)
				}
			}
		}()

		discover, _, _ := req.Option("discover").Bool()
		if discover {
			blk := blocks.NewBlock([]byte("floodsub:" + topic))
			cid, err := n.Blocks.AddObject(blk)
			if err != nil {
				log.Error("pubsub discovery: ", err)
				return
			}

			connectToPubSubPeers(req.Context(), n, cid)
		}
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: getPsMsgMarshaler(func(m *floodsub.Message) (io.Reader, error) {
			return bytes.NewReader(m.Data), nil
		}),
		"ndpayload": getPsMsgMarshaler(func(m *floodsub.Message) (io.Reader, error) {
			m.Data = append(m.Data, '\n')
			return bytes.NewReader(m.Data), nil
		}),
		"lenpayload": getPsMsgMarshaler(func(m *floodsub.Message) (io.Reader, error) {
			buf := make([]byte, 8)
			n := binary.PutUvarint(buf, uint64(len(m.Data)))
			return io.MultiReader(bytes.NewReader(buf[:n]), bytes.NewReader(m.Data)), nil
		}),
	},
	Type: floodsub.Message{},
}

func connectToPubSubPeers(ctx context.Context, n *core.IpfsNode, cid *cid.Cid) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	provs := n.Routing.FindProvidersAsync(ctx, key.Key(cid.Hash()), 10)
	wg := &sync.WaitGroup{}
	for p := range provs {
		wg.Add(1)
		go func(pi pstore.PeerInfo) {
			defer wg.Done()
			ctx, cancel := context.WithTimeout(ctx, time.Second*10)
			defer cancel()
			err := n.PeerHost.Connect(ctx, pi)
			if err != nil {
				log.Info("pubsub discover: ", err)
				return
			}
			log.Info("connected to pubsub peer:", pi.ID)
		}(p)
	}

	wg.Wait()
}

func getPsMsgMarshaler(f func(m *floodsub.Message) (io.Reader, error)) func(cmds.Response) (io.Reader, error) {
	return func(res cmds.Response) (io.Reader, error) {
		outChan, ok := res.Output().(<-chan interface{})
		if !ok {
			return nil, u.ErrCast()
		}

		marshal := func(v interface{}) (io.Reader, error) {
			obj, ok := v.(*floodsub.Message)
			if !ok {
				return nil, u.ErrCast()
			}

			return f(obj)
		}

		return &cmds.ChannelMarshaler{
			Channel:   outChan,
			Marshaler: marshal,
			Res:       res,
		}, nil
	}
}

var PubsubPubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Publish a message to a given pubsub topic.",
		ShortDescription: `
ipfs pubsub pub publishes a message to a specified topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", true, false, "Topic to publish to."),
		cmds.StringArg("data", true, true, "Payload of message to publish.").EnableStdin(),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		topic := req.Arguments()[0]

		for _, data := range req.Arguments()[1:] {
			if err := n.Floodsub.Publish(topic, []byte(data)); err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
		}
	},
}

var PubsubLsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List subscribed topics by name.",
		ShortDescription: `
ipfs pubsub ls lists out the names of topics you are currently subscribed to.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		res.SetOutput(&stringList{n.Floodsub.GetTopics()})
	},
	Type: stringList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
}

var PubsubPeersCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List all peers we are currently pubsubbing with.",
		ShortDescription: `
ipfs pubsub peers lists out the pubsub peers you are currently connected to.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		var out []string
		for _, p := range n.Floodsub.ListPeers() {
			out = append(out, p.Pretty())
		}
		res.SetOutput(&stringList{out})
	},
	Type: stringList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
}
<commit_msg>send discovery off into its own goroutine<commit_after>package commands

import (
	"bytes"
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"sync"
	"time"

	blocks "github.com/ipfs/go-ipfs/blocks"
	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"

	floodsub "gx/ipfs/QmZ3zAKL7KkPdnd5dWrkiEgC2H22kaa7QBiYSAhM2xQhb4/floodsub"
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
	key "gx/ipfs/Qmce4Y4zg3sYr7xKM5UueS67vhNni6EeWgCRnb7MbLJMew/go-key"
	pstore "gx/ipfs/QmdMfSLMDBDYhtc4oF3NYGCZr5dy4wQb6Ji26N4D4mdxa2/go-libp2p-peerstore"
	cid "gx/ipfs/QmfSc2xehWmWLnwwYR91Y8QF4xdASypTFVknutoKQS3GHp/go-cid"
)

var PubsubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "An experimental publish-subscribe system on ipfs.",
		ShortDescription: `
ipfs pubsub allows you to publish messages to a given topic, and also to
subscribe to new messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"pub":   PubsubPubCmd,
		"sub":   PubsubSubCmd,
		"ls":    PubsubLsCmd,
		"peers": PubsubPeersCmd,
	},
}

var PubsubSubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Subscribe to messages on a given topic.",
		ShortDescription: `
ipfs pubsub sub subscribes to messages on a given topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", true, false, "String name of topic to subscribe to."),
	},
	Options: []cmds.Option{
		cmds.BoolOption("discover", "try to discover other peers subscribed to the same topic"),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		topic := req.Arguments()[0]
		msgs, err := n.Floodsub.Subscribe(req.Context(), topic)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		out := make(chan interface{})
		res.SetOutput((<-chan interface{})(out))

		ctx := req.Context()
		go func() {
			defer close(out)
			for {
				select {
				case msg, ok := <-msgs:
					if !ok {
						return
					}
					out <- msg
				case <-ctx.Done():
					n.Floodsub.Unsub(topic)
				}
			}
		}()

		discover, _, _ := req.Option("discover").Bool()
		if discover {
			go func() {
				blk := blocks.NewBlock([]byte("floodsub:" + topic))
				cid, err := n.Blocks.AddObject(blk)
				if err != nil {
					log.Error("pubsub discovery: ", err)
					return
				}

				connectToPubSubPeers(req.Context(), n, cid)
			}()
		}
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: getPsMsgMarshaler(func(m *floodsub.Message) (io.Reader, error) {
			return bytes.NewReader(m.Data), nil
		}),
		"ndpayload": getPsMsgMarshaler(func(m *floodsub.Message) (io.Reader, error) {
			m.Data = append(m.Data, '\n')
			return bytes.NewReader(m.Data), nil
		}),
		"lenpayload": getPsMsgMarshaler(func(m *floodsub.Message) (io.Reader, error) {
			buf := make([]byte, 8)
			n := binary.PutUvarint(buf, uint64(len(m.Data)))
			return io.MultiReader(bytes.NewReader(buf[:n]), bytes.NewReader(m.Data)), nil
		}),
	},
	Type: floodsub.Message{},
}

func connectToPubSubPeers(ctx context.Context, n *core.IpfsNode, cid *cid.Cid) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	provs := n.Routing.FindProvidersAsync(ctx, key.Key(cid.Hash()), 10)
	wg := &sync.WaitGroup{}
	for p := range provs {
		wg.Add(1)
		go func(pi pstore.PeerInfo) {
			defer wg.Done()
			ctx, cancel := context.WithTimeout(ctx, time.Second*10)
			defer cancel()
			err := n.PeerHost.Connect(ctx, pi)
			if err != nil {
				log.Info("pubsub discover: ", err)
				return
			}
			log.Info("connected to pubsub peer:", pi.ID)
		}(p)
	}

	wg.Wait()
}

func getPsMsgMarshaler(f func(m *floodsub.Message) (io.Reader, error)) func(cmds.Response) (io.Reader, error) {
	return func(res cmds.Response) (io.Reader, error) {
		outChan, ok := res.Output().(<-chan interface{})
		if !ok {
			return nil, u.ErrCast()
		}

		marshal := func(v interface{}) (io.Reader, error) {
			obj, ok := v.(*floodsub.Message)
			if !ok {
				return nil, u.ErrCast()
			}

			return f(obj)
		}

		return &cmds.ChannelMarshaler{
			Channel:   outChan,
			Marshaler: marshal,
			Res:       res,
		}, nil
	}
}

var PubsubPubCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Publish a message to a given pubsub topic.",
		ShortDescription: `
ipfs pubsub pub publishes a message to a specified topic.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("topic", true, false, "Topic to publish to."),
		cmds.StringArg("data", true, true, "Payload of message to publish.").EnableStdin(),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		topic := req.Arguments()[0]

		for _, data := range req.Arguments()[1:] {
			if err := n.Floodsub.Publish(topic, []byte(data)); err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
		}
	},
}

var PubsubLsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List subscribed topics by name.",
		ShortDescription: `
ipfs pubsub ls lists out the names of topics you are currently subscribed to.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		res.SetOutput(&stringList{n.Floodsub.GetTopics()})
	},
	Type: stringList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
}

var PubsubPeersCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List all peers we are currently pubsubbing with.",
		ShortDescription: `
ipfs pubsub peers lists out the pubsub peers you are currently connected to.

This is an experimental feature. It is not intended in its current state
to be used in a production environment.

To use, the daemon must be run with '--enable-pubsub-experiment'.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Must be online!
		if !n.OnlineMode() {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		if n.Floodsub == nil {
			res.SetError(fmt.Errorf("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use."), cmds.ErrNormal)
			return
		}

		var out []string
		for _, p := range n.Floodsub.ListPeers() {
			out = append(out, p.Pretty())
		}
		res.SetOutput(&stringList{out})
	},
	Type: stringList{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
}
<|endoftext|>


<commit_before>package managed

import (
	"fmt"
	"io"
	"sync"
)

// A Process is a Component paired with a name and stateful data.
type Process struct {
	Name      string
	Component Component

	Writer func(*Process, io.Writer) error

	data map[string]string

	lock sync.RWMutex
}

func (p *Process) SetMany(a ...string) {
	if len(a)&2 != 0 {
		fmt.Errorf("SetMany arguments is not even, dropping last")
		a = a[0 : len(a)-1]
	}

	p.lock.Lock()
	defer p.lock.Unlock()
	for i := 0; i <= len(a); i += 2 {
		k := a[i]
		v := a[i+1]
		p.data[k] = v
	}

}

func (p *Process) Set(key, val string) {
	p.lock.Lock()
	defer p.lock.Unlock()
	p.data[key] = val
}

func (p *Process) Get(key string) (string, bool) {
	p.lock.RLock()
	defer p.lock.RUnlock()
	val, ok := p.data[key]
	return val, ok
}
<commit_msg>managed - fix process, update API<commit_after>package managed

import (
	"fmt"
	"io"
	"os"
	"sync"
)

// A Process is a Component paired with a name and stateful data.
type Process struct {
	Name      string
	Component Component

	Writer func(*Process, io.Writer) error

	data map[string]string

	lock sync.RWMutex
}

// SetMany sets the key/value pairs on the data
func (p *Process) SetMany(a ...string) {
	if len(a)&2 != 0 {
		fmt.Fprintf(os.Stderr, "SetMany arguments is not even, dropping last")
		a = a[0 : len(a)-1]
	}

	p.lock.Lock()
	defer p.lock.Unlock()
	for i := 0; i <= len(a); i += 2 {
		k := a[i]
		v := a[i+1]
		p.data[k] = v
	}

}

// Set sets or overwrites the key value pair to the process data
func (p *Process) Set(key, val string) bool {
	p.lock.Lock()
	defer p.lock.Unlock()
	_, ok := p.data[key]
	p.data[key] = key
	return ok
}

// Get gets the value for the key in the process data, if available
func (p *Process) Get(key string) (string, bool) {
	p.lock.RLock()
	defer p.lock.RUnlock()
	val, ok := p.data[key]
	return val, ok
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/coreos/go-etcd/etcd"
	"github.com/headzoo/etcdsh/config"
	"github.com/headzoo/etcdsh/handlers"
)

// Main method.
func main() {
	config := config.New()

	help := flag.Bool("help", false, "Prints command line options and exit.")
	version := flag.Bool("version", false, "Prints the etcdsh version and exit.")
	flag.StringVar(&config.Machine, "machine", config.Machine, "Connect to this etcd server.")
	flag.BoolVar(&config.Colors, "colors", config.Colors, "Use colors in display.")
	flag.Parse()

	if *help {
		printHelp()
		os.Exit(0)
	}
	if *version {
		printVersion()
		os.Exit(0)
	}

	fmt.Printf("Connecting to %s\n", config.Machine)
	client := etcd.NewClient([]string{config.Machine})

	controller := handlers.NewController(config, client, os.Stdout, os.Stderr, os.Stdin)
	controller.Add(handlers.NewLsHandler(controller))
	controller.Add(handlers.NewSetHandler(controller))
	controller.Add(handlers.NewExitHandler(controller))
	controller.Add(handlers.NewHelpHandler(controller))
	controller.Add(handlers.NewCdHandler(controller))
	controller.Add(handlers.NewGetHandler(controller))
	code := controller.Start()

	os.Exit(code)
}

// printHelp prints the command line help information.
func printHelp() {
	printVersion()
	fmt.Println("USAGE:")
	fmt.Println("\tetcdsh [OPTIONS]")

	fmt.Println("")
	fmt.Println("OPTIONS:")
	flag.VisitAll(func(f *flag.Flag) {
		fmt.Printf("\t-%-10s%s\n", f.Name, f.Usage)
	})

	fmt.Println("")
	fmt.Println("EXAMPLES:")
	fmt.Println("\tetcdsh -machine='http://192.168.1.23:4001'")

	fmt.Println("")
}

// printVersion prints the app version information.
func printVersion() {
	fmt.Printf("etcdsh %s - An interactive shell for the etcd server.\n\n", handlers.Version)
}
<commit_msg>Formatting<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/coreos/go-etcd/etcd"
	"github.com/headzoo/etcdsh/config"
	"github.com/headzoo/etcdsh/handlers"
)

// Main method.
func main() {
	config := config.New()

	help := flag.Bool("help", false, "Prints command line options and exit.")
	version := flag.Bool("version", false, "Prints the etcdsh version and exit.")
	flag.StringVar(&config.Machine, "machine", config.Machine, "Connect to this etcd server.")
	flag.BoolVar(&config.Colors, "colors", config.Colors, "Use colors in display.")
	flag.Parse()

	if *help {
		printHelp()
		os.Exit(0)
	}
	if *version {
		printVersion()
		os.Exit(0)
	}

	fmt.Printf("Connecting to %s\n", config.Machine)
	client := etcd.NewClient([]string{config.Machine})

	controller := handlers.NewController(config, client, os.Stdout, os.Stderr, os.Stdin)
	controller.Add(handlers.NewLsHandler(controller))
	controller.Add(handlers.NewSetHandler(controller))
	controller.Add(handlers.NewExitHandler(controller))
	controller.Add(handlers.NewHelpHandler(controller))
	controller.Add(handlers.NewCdHandler(controller))
	controller.Add(handlers.NewGetHandler(controller))
	code := controller.Start()

	os.Exit(code)
}

// printHelp prints the command line help information.
func printHelp() {
	printVersion()
	fmt.Println("USAGE:")
	fmt.Println("\tetcdsh [OPTIONS]")

	fmt.Println("")
	fmt.Println("OPTIONS:")
	flag.VisitAll(func(f *flag.Flag) {
		fmt.Printf("\t-%-10s%s\n", f.Name, f.Usage)
	})

	fmt.Println("")
	fmt.Println("EXAMPLES:")
	fmt.Println("\tetcdsh -machine='http://192.168.1.23:4001'")

	fmt.Println("")
}

// printVersion prints the app version information.
func printVersion() {
	fmt.Printf("etcdsh %s - An interactive shell for the etcd server.\n\n", handlers.Version)
}
<|endoftext|>


<commit_before>package sharedaction

import (
	"context"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	logcache "code.cloudfoundry.org/go-log-cache"
	"code.cloudfoundry.org/go-log-cache/rpc/logcache_v1"
	"code.cloudfoundry.org/go-loggregator/rpc/loggregator_v2"
	"github.com/sirupsen/logrus"
)

const (
	StagingLog      = "STG"
	RecentLogsLines = 1000

	retryCount    = 5
	retryInterval = time.Millisecond * 250
	walkDelay     = time.Second * 2
)

type LogMessage struct {
	message        string
	messageType    string
	timestamp      time.Time
	sourceType     string
	sourceInstance string
}

func (log LogMessage) Message() string {
	return log.message
}

func (log LogMessage) Type() string {
	return log.messageType
}

func (log LogMessage) Staging() bool {
	return log.sourceType == StagingLog
}

func (log LogMessage) Timestamp() time.Time {
	return log.timestamp
}

func (log LogMessage) SourceType() string {
	return log.sourceType
}

func (log LogMessage) SourceInstance() string {
	return log.sourceInstance
}

func NewLogMessage(message string, messageType string, timestamp time.Time, sourceType string, sourceInstance string) *LogMessage {
	return &LogMessage{
		message:        message,
		messageType:    messageType,
		timestamp:      timestamp,
		sourceType:     sourceType,
		sourceInstance: sourceInstance,
	}
}

type LogMessages []*LogMessage

func (lm LogMessages) Len() int { return len(lm) }

func (lm LogMessages) Less(i, j int) bool {
	return lm[i].timestamp.Before(lm[j].timestamp)
}

func (lm LogMessages) Swap(i, j int) {
	lm[i], lm[j] = lm[j], lm[i]
}

type channelWriter struct {
	errChannel chan error
}

func (c channelWriter) Write(bytes []byte) (n int, err error) {
	c.errChannel <- errors.New(strings.Trim(string(bytes), "\n"))

	return len(bytes), nil
}

// cliRetryBackoff returns true for OnErr after sleeping the given interval for a limited number of times,
// and returns true for OnEmpty always.
// Basically: retry x times on connection failures, and wait forever for logs to show up.
type cliRetryBackoff struct {
	interval time.Duration
	maxCount int
	count    int
}

func newCliRetryBackoff(interval time.Duration, maxCount int) *cliRetryBackoff {
	return &cliRetryBackoff{
		interval: interval,
		maxCount: maxCount,
	}
}

func (b *cliRetryBackoff) OnErr(error) bool {
	b.count++
	if b.count >= b.maxCount {
		return false
	}

	time.Sleep(b.interval)
	return true
}

func (b *cliRetryBackoff) OnEmpty() bool {
	return true
}

func (b *cliRetryBackoff) Reset() {
	b.count = 0
}

func GetStreamingLogs(appGUID string, client LogCacheClient) (<-chan LogMessage, <-chan error, context.CancelFunc) {

	logrus.Info("Start Tailing Logs")

	outgoingLogStream := make(chan LogMessage, 1000)
	outgoingErrStream := make(chan error, 1000)
	ctx, cancelFunc := context.WithCancel(context.Background())
	go func() {
		defer close(outgoingLogStream)
		defer close(outgoingErrStream)

		ts := latestEnvelopeTimestamp(client, outgoingErrStream, ctx, appGUID)

		// if the context was cancelled we may not have seen an envelope
		if ts.IsZero() {
			return
		}

		logcache.Walk(
			ctx,
			appGUID,
			logcache.Visitor(func(envelopes []*loggregator_v2.Envelope) bool {
				logMessages := convertEnvelopesToLogMessages(envelopes)
				for _, logMessage := range logMessages {
					select {
					case <-ctx.Done():
						return false
					default:
						outgoingLogStream <- *logMessage
					}
				}
				return true
			}),
			client.Read,
			logcache.WithWalkDelay(walkDelay),
			logcache.WithWalkStartTime(walkStartTime),
			logcache.WithWalkEnvelopeTypes(logcache_v1.EnvelopeType_LOG),
			logcache.WithWalkBackoff(newCliRetryBackoff(retryInterval, retryCount)),
			logcache.WithWalkLogger(log.New(channelWriter{
				errChannel: outgoingErrStream,
			}, "", 0)),
		)
	}()

	return outgoingLogStream, outgoingErrStream, cancelFunc
}

func latestEnvelopeTimestamp(client LogCacheClient, errs chan error, ctx context.Context, sourceID string) time.Time {

	// Fetching the most recent timestamp could be implemented with client.Read directly rather than using logcache.Walk
	// We use Walk because we want the extra retry behavior provided through Walk

	// Wrap client.Read in our own function to allow us to specify our own read options
	// https://github.com/cloudfoundry/go-log-cache/issues/27
	r := func(ctx context.Context, sourceID string, _ time.Time, opts ...logcache.ReadOption) ([]*loggregator_v2.Envelope, error) {
		os := []logcache.ReadOption{
			logcache.WithLimit(1),
			logcache.WithDescending(),
		}
		for _, o := range opts {
			os = append(os, o)
		}
		return client.Read(ctx, sourceID, time.Time{}, os...)
	}

	var timestamp time.Time

	logcache.Walk(
		ctx,
		sourceID,
		logcache.Visitor(func(envelopes []*loggregator_v2.Envelope) bool {
			timestamp = time.Unix(0, envelopes[0].Timestamp)
			return false
		}),
		r,
		logcache.WithWalkDelay(walkDelay),
		logcache.WithWalkBackoff(newCliRetryBackoff(retryInterval, retryCount)),
		logcache.WithWalkLogger(log.New(channelWriter{
			errChannel: errs,
		}, "", 0)),
	)

	return timestamp
}

func GetRecentLogs(appGUID string, client LogCacheClient) ([]LogMessage, error) {
	logLineRequestCount := RecentLogsLines
	var envelopes []*loggregator_v2.Envelope
	var err error

	for logLineRequestCount >= 1 {
		envelopes, err = client.Read(
			context.Background(),
			appGUID,
			time.Time{},
			logcache.WithEnvelopeTypes(logcache_v1.EnvelopeType_LOG),
			logcache.WithLimit(logLineRequestCount),
			logcache.WithDescending(),
		)
		if err == nil || err.Error() != "unexpected status code 429" {
			break
		}
		logLineRequestCount /= 2
	}
	if err != nil {
		return nil, fmt.Errorf("Failed to retrieve logs from Log Cache: %s", err)
	}

	logMessages := convertEnvelopesToLogMessages(envelopes)
	var reorderedLogMessages []LogMessage
	for i := len(logMessages) - 1; i >= 0; i-- {
		reorderedLogMessages = append(reorderedLogMessages, *logMessages[i])
	}

	return reorderedLogMessages, nil
}

func convertEnvelopesToLogMessages(envelopes []*loggregator_v2.Envelope) []*LogMessage {
	var logMessages []*LogMessage
	for _, envelope := range envelopes {
		logEnvelope, ok := envelope.GetMessage().(*loggregator_v2.Envelope_Log)
		if !ok {
			continue
		}
		log := logEnvelope.Log

		logMessages = append(logMessages, NewLogMessage(
			string(log.Payload),
			loggregator_v2.Log_Type_name[int32(log.Type)],
			time.Unix(0, envelope.GetTimestamp()),
			envelope.GetTags()["source_type"],
			envelope.GetInstanceId(),
		))
	}
	return logMessages
}
<commit_msg>Use correct walkStartTime in GetStreamingLogs<commit_after>package sharedaction

import (
	"context"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	logcache "code.cloudfoundry.org/go-log-cache"
	"code.cloudfoundry.org/go-log-cache/rpc/logcache_v1"
	"code.cloudfoundry.org/go-loggregator/rpc/loggregator_v2"
	"github.com/sirupsen/logrus"
)

const (
	StagingLog      = "STG"
	RecentLogsLines = 1000

	retryCount    = 5
	retryInterval = time.Millisecond * 250
	walkDelay     = time.Second * 2
)

type LogMessage struct {
	message        string
	messageType    string
	timestamp      time.Time
	sourceType     string
	sourceInstance string
}

func (log LogMessage) Message() string {
	return log.message
}

func (log LogMessage) Type() string {
	return log.messageType
}

func (log LogMessage) Staging() bool {
	return log.sourceType == StagingLog
}

func (log LogMessage) Timestamp() time.Time {
	return log.timestamp
}

func (log LogMessage) SourceType() string {
	return log.sourceType
}

func (log LogMessage) SourceInstance() string {
	return log.sourceInstance
}

func NewLogMessage(message string, messageType string, timestamp time.Time, sourceType string, sourceInstance string) *LogMessage {
	return &LogMessage{
		message:        message,
		messageType:    messageType,
		timestamp:      timestamp,
		sourceType:     sourceType,
		sourceInstance: sourceInstance,
	}
}

type LogMessages []*LogMessage

func (lm LogMessages) Len() int { return len(lm) }

func (lm LogMessages) Less(i, j int) bool {
	return lm[i].timestamp.Before(lm[j].timestamp)
}

func (lm LogMessages) Swap(i, j int) {
	lm[i], lm[j] = lm[j], lm[i]
}

type channelWriter struct {
	errChannel chan error
}

func (c channelWriter) Write(bytes []byte) (n int, err error) {
	c.errChannel <- errors.New(strings.Trim(string(bytes), "\n"))

	return len(bytes), nil
}

// cliRetryBackoff returns true for OnErr after sleeping the given interval for a limited number of times,
// and returns true for OnEmpty always.
// Basically: retry x times on connection failures, and wait forever for logs to show up.
type cliRetryBackoff struct {
	interval time.Duration
	maxCount int
	count    int
}

func newCliRetryBackoff(interval time.Duration, maxCount int) *cliRetryBackoff {
	return &cliRetryBackoff{
		interval: interval,
		maxCount: maxCount,
	}
}

func (b *cliRetryBackoff) OnErr(error) bool {
	b.count++
	if b.count >= b.maxCount {
		return false
	}

	time.Sleep(b.interval)
	return true
}

func (b *cliRetryBackoff) OnEmpty() bool {
	return true
}

func (b *cliRetryBackoff) Reset() {
	b.count = 0
}

func GetStreamingLogs(appGUID string, client LogCacheClient) (<-chan LogMessage, <-chan error, context.CancelFunc) {

	logrus.Info("Start Tailing Logs")

	outgoingLogStream := make(chan LogMessage, 1000)
	outgoingErrStream := make(chan error, 1000)
	ctx, cancelFunc := context.WithCancel(context.Background())
	go func() {
		defer close(outgoingLogStream)
		defer close(outgoingErrStream)

		ts := latestEnvelopeTimestamp(client, outgoingErrStream, ctx, appGUID)

		// if the context was cancelled we may not have seen an envelope
		if ts.IsZero() {
			return
		}

		logcache.Walk(
			ctx,
			appGUID,
			logcache.Visitor(func(envelopes []*loggregator_v2.Envelope) bool {
				logMessages := convertEnvelopesToLogMessages(envelopes)
				for _, logMessage := range logMessages {
					select {
					case <-ctx.Done():
						return false
					default:
						outgoingLogStream <- *logMessage
					}
				}
				return true
			}),
			client.Read,
			logcache.WithWalkDelay(walkDelay),
			logcache.WithWalkStartTime(ts),
			logcache.WithWalkEnvelopeTypes(logcache_v1.EnvelopeType_LOG),
			logcache.WithWalkBackoff(newCliRetryBackoff(retryInterval, retryCount)),
			logcache.WithWalkLogger(log.New(channelWriter{
				errChannel: outgoingErrStream,
			}, "", 0)),
		)
	}()

	return outgoingLogStream, outgoingErrStream, cancelFunc
}

func latestEnvelopeTimestamp(client LogCacheClient, errs chan error, ctx context.Context, sourceID string) time.Time {

	// Fetching the most recent timestamp could be implemented with client.Read directly rather than using logcache.Walk
	// We use Walk because we want the extra retry behavior provided through Walk

	// Wrap client.Read in our own function to allow us to specify our own read options
	// https://github.com/cloudfoundry/go-log-cache/issues/27
	r := func(ctx context.Context, sourceID string, _ time.Time, opts ...logcache.ReadOption) ([]*loggregator_v2.Envelope, error) {
		os := []logcache.ReadOption{
			logcache.WithLimit(1),
			logcache.WithDescending(),
		}
		for _, o := range opts {
			os = append(os, o)
		}
		return client.Read(ctx, sourceID, time.Time{}, os...)
	}

	var timestamp time.Time

	logcache.Walk(
		ctx,
		sourceID,
		logcache.Visitor(func(envelopes []*loggregator_v2.Envelope) bool {
			timestamp = time.Unix(0, envelopes[0].Timestamp)
			return false
		}),
		r,
		logcache.WithWalkDelay(walkDelay),
		logcache.WithWalkBackoff(newCliRetryBackoff(retryInterval, retryCount)),
		logcache.WithWalkLogger(log.New(channelWriter{
			errChannel: errs,
		}, "", 0)),
	)

	return timestamp
}

func GetRecentLogs(appGUID string, client LogCacheClient) ([]LogMessage, error) {
	logLineRequestCount := RecentLogsLines
	var envelopes []*loggregator_v2.Envelope
	var err error

	for logLineRequestCount >= 1 {
		envelopes, err = client.Read(
			context.Background(),
			appGUID,
			time.Time{},
			logcache.WithEnvelopeTypes(logcache_v1.EnvelopeType_LOG),
			logcache.WithLimit(logLineRequestCount),
			logcache.WithDescending(),
		)
		if err == nil || err.Error() != "unexpected status code 429" {
			break
		}
		logLineRequestCount /= 2
	}
	if err != nil {
		return nil, fmt.Errorf("Failed to retrieve logs from Log Cache: %s", err)
	}

	logMessages := convertEnvelopesToLogMessages(envelopes)
	var reorderedLogMessages []LogMessage
	for i := len(logMessages) - 1; i >= 0; i-- {
		reorderedLogMessages = append(reorderedLogMessages, *logMessages[i])
	}

	return reorderedLogMessages, nil
}

func convertEnvelopesToLogMessages(envelopes []*loggregator_v2.Envelope) []*LogMessage {
	var logMessages []*LogMessage
	for _, envelope := range envelopes {
		logEnvelope, ok := envelope.GetMessage().(*loggregator_v2.Envelope_Log)
		if !ok {
			continue
		}
		log := logEnvelope.Log

		logMessages = append(logMessages, NewLogMessage(
			string(log.Payload),
			loggregator_v2.Log_Type_name[int32(log.Type)],
			time.Unix(0, envelope.GetTimestamp()),
			envelope.GetTags()["source_type"],
			envelope.GetInstanceId(),
		))
	}
	return logMessages
}
<|endoftext|>


<commit_before>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

func ECBalance(key string) (int64, error) {
	str := fmt.Sprintf("http://%s/v1/entry-credit-balance/%s", serverFct, key)
	resp, err := http.Get(str)
	if err != nil {
		return 0, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	if !b.Success {
		return 0, fmt.Errorf(b.Response)
	}

	v, err := strconv.ParseInt(b.Response, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	return v, nil
}

func FctBalance(key string) (int64, error) {
	str := fmt.Sprintf("http://%s/v1/factoid-balance/%s", serverFct, key)
	resp, err := http.Get(str)
	if err != nil {
		return 0, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	if !b.Success {
		return 0, fmt.Errorf(b.Response)
	}

	v, err := strconv.ParseInt(b.Response, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	return v, nil
}

func GenerateFactoidAddress(name string) (string, error) {
	name = strings.TrimSpace(name)

	str := fmt.Sprintf("http://%s/v1/factoid-generate-address/%s", serverFct, name)

	resp, err := http.Get(str)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return "", fmt.Errorf("Error attempting to create %s", name)
	}

	if !b.Success {
		return "", fmt.Errorf(b.Response)
	}

	return b.Response, nil
}

func GenerateEntryCreditAddress(name string) (string, error) {
	name = strings.TrimSpace(name)

	str := fmt.Sprintf("http://%s/v1/factoid-generate-ec-address/%s", serverFct, name)

	resp, err := http.Get(str)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return "", fmt.Errorf("Error attempting to create %s", name)
	}

	if !b.Success {
		return "", fmt.Errorf(b.Response)
	}

	return b.Response, nil
}
<commit_msg>Added address generation from private key.<commit_after>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

func ECBalance(key string) (int64, error) {
	str := fmt.Sprintf("http://%s/v1/entry-credit-balance/%s", serverFct, key)
	resp, err := http.Get(str)
	if err != nil {
		return 0, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	if !b.Success {
		return 0, fmt.Errorf(b.Response)
	}

	v, err := strconv.ParseInt(b.Response, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	return v, nil
}

func FctBalance(key string) (int64, error) {
	str := fmt.Sprintf("http://%s/v1/factoid-balance/%s", serverFct, key)
	resp, err := http.Get(str)
	if err != nil {
		return 0, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	if !b.Success {
		return 0, fmt.Errorf(b.Response)
	}

	v, err := strconv.ParseInt(b.Response, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("Error getting the balance of %s", key)
	}

	return v, nil
}

func GenerateFactoidAddress(name string) (string, error) {
	name = strings.TrimSpace(name)

	str := fmt.Sprintf("http://%s/v1/factoid-generate-address/%s", serverFct, name)

	resp, err := http.Get(str)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return "", fmt.Errorf("Error attempting to create %s", name)
	}

	if !b.Success {
		return "", fmt.Errorf(b.Response)
	}

	return b.Response, nil
}

func GenerateEntryCreditAddress(name string) (string, error) {
	name = strings.TrimSpace(name)

	str := fmt.Sprintf("http://%s/v1/factoid-generate-ec-address/%s", serverFct, name)

	resp, err := http.Get(str)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return "", fmt.Errorf("Error attempting to create %s", name)
	}

	if !b.Success {
		return "", fmt.Errorf(b.Response)
	}

	return b.Response, nil
}

func GenerateFactoidAddressFromPrivateKey(name string, privateKey string) (string, error) {
	name = strings.TrimSpace(name)

	str := fmt.Sprintf("http://%s/v1/factoid-generate-address-from-private-key/?name=%s&privateKey=%s", serverFct, name, privateKey)

	resp, err := http.Get(str)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return "", fmt.Errorf("Error attempting to create %s", name)
	}

	if !b.Success {
		return "", fmt.Errorf(b.Response)
	}

	return b.Response, nil
}

func GenerateEntryCreditAddressFromPrivateKey(name string, privateKey string) (string, error) {
	name = strings.TrimSpace(name)

	str := fmt.Sprintf("http://%s/v1/factoid-generate-ec-address-from-private-key/?name=%s&privateKey=%s", serverFct, name, privateKey)

	resp, err := http.Get(str)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	resp.Body.Close()

	type x struct {
		Response string
		Success  bool
	}
	b := new(x)
	if err := json.Unmarshal(body, b); err != nil {
		return "", fmt.Errorf("Error attempting to create %s", name)
	}

	if !b.Success {
		return "", fmt.Errorf(b.Response)
	}

	return b.Response, nil
}
<|endoftext|>


<commit_before>package model

import (
	"testing"
    "appengine/aetest"
)

func TestPlayerSaveNoSeason(t *testing.T) {
	c, err := aetest.NewContext(nil)
    if err != nil {
            t.Fatal(err)
    }
    defer c.Close()
    name := "Player Name"
    email := "player.name@somewhere.com"
    faction := "Skorne"
	err = SavePlayer(c, nil, name, email, faction)
	if err != nil {
		t.Fatalf("Error saving player: %v", err)
	}
	p := LoadPlayer(c, nil, email)
	if p == nil {
		t.Fatal("Loading player gave us a nil player")
	}
	if p.Name != name {
		t.Errorf("Expected player name to be '%s' not '%s'", name, p.Name)
	}
	if p.Email != email {
		t.Errorf("Expected player email to be '%s' instead it was '%s'", email, p.Email)
	}
	if p.Faction != faction {
		t.Errorf("Expected player faction to be '%s' instead it was '%s'", faction, p.Faction)
	}
}
<commit_msg>Added a unit test for adding in csv values of players<commit_after>package model

import (
	"testing"
    "appengine/aetest"
)

func TestPlayerSaveNoSeason(t *testing.T) {
	c, err := aetest.NewContext(nil)
    if err != nil {
            t.Fatal(err)
    }
    defer c.Close()
    name := "Player Name"
    email := "player.name@somewhere.com"
    faction := "Skorne"
	err = SavePlayer(c, nil, name, email, faction)
	if err != nil {
		t.Fatalf("Error saving player: %v", err)
	}
	p := LoadPlayer(c, nil, email)
	if p == nil {
		t.Fatal("Loading player gave us a nil player")
	}
	if p.Name != name {
		t.Errorf("Expected player name to be '%s' not '%s'", name, p.Name)
	}
	if p.Email != email {
		t.Errorf("Expected player email to be '%s' instead it was '%s'", email, p.Email)
	}
	if p.Faction != faction {
		t.Errorf("Expected player faction to be '%s' instead it was '%s'", faction, p.Faction)
	}
}

func TestPlayerCsvSaveNilSeason(t *testing.T) {
	c, err := aetest.NewContext(nil)
    if err != nil {
            t.Fatal(err)
    }
    defer c.Close()
    csvString := `Player1,Skorne,player@somewhere.com,406-244-6613
Player2,Circle,player2@somewhereelse.com,503-882-9933
`
	CreatePlayersFromCsv(c, nil, csvString)
	p := LoadPlayer(c, nil, "player@somewhere.com")
	if p == nil {
		t.Fatal("Loading the first player gave us an unexpected nil player")
	}
	if p.Name != "Player1" {
		t.Errorf("Expected player name to be '%s' not '%s'", "Player1", p.Name)
	}
	if p.Email != "player@somewhere.com" {
		t.Errorf("Expected player email to be '%s' instead it was '%s'", "player@somewhere.com", p.Email)
	}
	if p.Faction != "Skorne" {
		t.Errorf("Expected player faction to be '%s' instead it was '%s'", "Skorne", p.Faction)
	}

	p = LoadPlayer(c, nil, "player2@somewhereelse.com")
	if p == nil {
		t.Fatal("Loading the second player gave us an unexpected nil player")
	}
	if p.Name != "Player2" {
		t.Errorf("Expected player name to be '%s' not '%s'", "Player2", p.Name)
	}
	if p.Email != "player2@somewhereelse.com" {
		t.Errorf("Expected player email to be '%s' instead it was '%s'", "player2@somewhereelse.com", p.Email)
	}
	if p.Faction != "Circle" {
		t.Errorf("Expected player faction to be '%s' instead it was '%s'", "Circle", p.Faction)
	}

}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"strings"
	"time"
)

// Currencies from database
type Currencies struct {
	Base  string             `json:"base"`
	Date  string             `json:"date"`
	Rates map[string]float32 `json:"rates"`
}

// LatestRates query db for most updated results for each currency
// supports different queries based on request parameters (base, symbols)
func LatestRates(baseParam, symbolsParam string) (*Currencies, error) {
	base := strings.ToUpper(baseParam)
	symbols := strings.ToUpper(symbolsParam)
	if base == "EUR" {
		base = ""
	}

	var rows *sql.Rows
	var err error

	switch {
	case base != "" && symbols != "":
		if strings.Contains(symbols, "EUR") {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates WHERE ratedate = (SELECT max(ratedate) FROM rates)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT max(ratedate) FROM rates)
			UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
			WHERE ratedate = (SELECT max(ratedate) FROM rates)
			AND currency = $1)`, base, symbols)
			if err != nil {
				return nil, err
			}
		} else {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates WHERE ratedate = (SELECT max(ratedate) FROM rates)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT max(ratedate) FROM rates)`, base, symbols)
			if err != nil {
				return nil, err
			}
		}
	case base != "" && symbols == "":
		rows, err = db.Query(`SELECT currency,
		rate / (SELECT rate FROM rates WHERE ratedate = (SELECT max(ratedate) FROM rates)
		AND currency = $1) AS rate FROM rates WHERE currency !=$1
		AND ratedate = (SELECT max(ratedate) FROM rates)
		UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
		WHERE ratedate = (SELECT max(ratedate) FROM rates) AND currency = $1)`, base)
		if err != nil {
			return nil, err
		}
	case base == "" && symbols != "":
		rows, err = db.Query(`SELECT currency, rate
		FROM rates WHERE currency = ANY(string_to_array($1, ','))
		AND ratedate = (SELECT max(ratedate) FROM rates)`, symbols)
		if err != nil {
			return nil, err
		}
	default:
		rows, err = db.Query(`SELECT currency, rate FROM rates
		WHERE ratedate = (SELECT max(ratedate) FROM rates)`)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	currencies := &Currencies{Rates: make(map[string]float32)}
	for rows.Next() {
		var (
			currency string
			rate     float32
		)
		if err := rows.Scan(&currency, &rate); err != nil {
			return nil, err
		}
		currencies.Rates[currency] = rate
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	var date time.Time
	err = db.QueryRow("SELECT max(ratedate) FROM rates").Scan(&date)
	currencies.Date = date.Format("2006-01-02")

	if base != "" {
		currencies.Base = base
	} else {
		currencies.Base = "EUR"
	}

	return currencies, nil
}

// HistoricalRates query db for historical results for each currency
// supports different queries based on request parameters (base, symbols, date)
func HistoricalRates(baseParam, symbolsParam, dateParam string) (*Currencies, error) {
	base := strings.ToUpper(baseParam)
	symbols := strings.ToUpper(symbolsParam)
	if base == "EUR" {
		base = ""
	}

	var rows *sql.Rows
	var err error

	switch {
	case base != "" && symbols != "":
		if strings.Contains(symbols, "EUR") {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates
			WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
			WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			AND currency = $1)`, base, symbols, dateParam)
			if err != nil {
				return nil, err
			}
		} else {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates
			WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT ratedate FROM rates
			WHERE ratedate <= $3 LIMIT 1)`, base, symbols, dateParam)
			if err != nil {
				return nil, err
			}
		}
	case base != "" && symbols == "":
		rows, err = db.Query(`SELECT currency,
		rate / (SELECT rate FROM rates
		WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $2 LIMIT 1)
		AND currency = $1) AS rate FROM rates WHERE currency !=$1
		AND ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $2 LIMIT 1)
		UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
		WHERE ratedate = (SELECT ratedate FROM rates
		WHERE ratedate <= $2 LIMIT 1) AND currency = $1)`, base, dateParam)
		if err != nil {
			return nil, err
		}
	case base == "" && symbols != "":
		rows, err = db.Query(`SELECT currency, rate
		FROM rates WHERE currency = ANY(string_to_array($1, ','))
		AND ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $2 LIMIT 1)`, symbols, dateParam)
		if err != nil {
			return nil, err
		}
	default:
		rows, err = db.Query(`SELECT currency, rate FROM rates
		WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $1 LIMIT 1)`,
			dateParam)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	currencies := &Currencies{Rates: make(map[string]float32)}
	for rows.Next() {
		var (
			currency string
			rate     float32
		)
		if err := rows.Scan(&currency, &rate); err != nil {
			return nil, err
		}
		currencies.Rates[currency] = rate
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	var date string
	err = db.QueryRow("SELECT ratedate FROM rates WHERE ratedate <= $1 LIMIT 1", dateParam).Scan(&date)
	currencies.Date = date

	if base != "" {
		currencies.Base = base
	} else {
		currencies.Base = "EUR"
	}

	return currencies, nil
}
<commit_msg>Variable names, format response date<commit_after>package models

import (
	"database/sql"
	"strings"
	"time"
)

// Currencies from database
type Currencies struct {
	Base  string             `json:"base"`
	Date  string             `json:"date"`
	Rates map[string]float32 `json:"rates"`
}

// LatestRates query db for most updated results for each currency
// supports different queries based on request parameters (base, symbols)
func LatestRates(baseParam, symbolsParam string) (*Currencies, error) {
	base := strings.ToUpper(baseParam)
	symbols := strings.ToUpper(symbolsParam)
	if base == "EUR" {
		base = ""
	}

	var rows *sql.Rows
	var err error

	switch {
	case base != "" && symbols != "":
		if strings.Contains(symbols, "EUR") {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates WHERE ratedate = (SELECT max(ratedate) FROM rates)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT max(ratedate) FROM rates)
			UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
			WHERE ratedate = (SELECT max(ratedate) FROM rates)
			AND currency = $1)`, base, symbols)
			if err != nil {
				return nil, err
			}
		} else {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates WHERE ratedate = (SELECT max(ratedate) FROM rates)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT max(ratedate) FROM rates)`, base, symbols)
			if err != nil {
				return nil, err
			}
		}
	case base != "" && symbols == "":
		rows, err = db.Query(`SELECT currency,
		rate / (SELECT rate FROM rates WHERE ratedate = (SELECT max(ratedate) FROM rates)
		AND currency = $1) AS rate FROM rates WHERE currency !=$1
		AND ratedate = (SELECT max(ratedate) FROM rates)
		UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
		WHERE ratedate = (SELECT max(ratedate) FROM rates) AND currency = $1)`, base)
		if err != nil {
			return nil, err
		}
	case base == "" && symbols != "":
		rows, err = db.Query(`SELECT currency, rate
		FROM rates WHERE currency = ANY(string_to_array($1, ','))
		AND ratedate = (SELECT max(ratedate) FROM rates)`, symbols)
		if err != nil {
			return nil, err
		}
	default:
		rows, err = db.Query(`SELECT currency, rate FROM rates
		WHERE ratedate = (SELECT max(ratedate) FROM rates)`)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	currencies := &Currencies{Rates: make(map[string]float32)}
	for rows.Next() {
		var (
			currency string
			rate     float32
		)
		if err := rows.Scan(&currency, &rate); err != nil {
			return nil, err
		}
		currencies.Rates[currency] = rate
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	var date time.Time
	err = db.QueryRow("SELECT max(ratedate) FROM rates").Scan(&date)
	currencies.Date = date.Format("2006-01-02")

	if base != "" {
		currencies.Base = base
	} else {
		currencies.Base = "EUR"
	}

	return currencies, nil
}

// HistoricalRates query db for historical results for each currency
// supports different queries based on request parameters (base, symbols, date)
func HistoricalRates(baseParam, symbolsParam, dateParam string) (*Currencies, error) {
	base := strings.ToUpper(baseParam)
	symbols := strings.ToUpper(symbolsParam)
	date := dateParam
	if base == "EUR" {
		base = ""
	}

	var rows *sql.Rows
	var err error

	switch {
	case base != "" && symbols != "":
		if strings.Contains(symbols, "EUR") {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates
			WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
			WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			AND currency = $1)`, base, symbols, date)
			if err != nil {
				return nil, err
			}
		} else {
			rows, err = db.Query(`SELECT currency,
			rate / (SELECT rate FROM rates
			WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $3 LIMIT 1)
			AND currency = $1) AS rate FROM rates WHERE currency !=$1
			AND currency = ANY(string_to_array($2, ','))
			AND ratedate = (SELECT ratedate FROM rates
			WHERE ratedate <= $3 LIMIT 1)`, base, symbols, date)
			if err != nil {
				return nil, err
			}
		}
	case base != "" && symbols == "":
		rows, err = db.Query(`SELECT currency,
		rate / (SELECT rate FROM rates
		WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $2 LIMIT 1)
		AND currency = $1) AS rate FROM rates WHERE currency !=$1
		AND ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $2 LIMIT 1)
		UNION ALL SELECT 'EUR', 1 / (SELECT rate FROM rates
		WHERE ratedate = (SELECT ratedate FROM rates
		WHERE ratedate <= $2 LIMIT 1) AND currency = $1)`, base, date)
		if err != nil {
			return nil, err
		}
	case base == "" && symbols != "":
		rows, err = db.Query(`SELECT currency, rate
		FROM rates WHERE currency = ANY(string_to_array($1, ','))
		AND ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $2 LIMIT 1)`, symbols, date)
		if err != nil {
			return nil, err
		}
	default:
		rows, err = db.Query(`SELECT currency, rate FROM rates
		WHERE ratedate = (SELECT ratedate FROM rates WHERE ratedate <= $1 LIMIT 1)`,
			date)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	currencies := &Currencies{Rates: make(map[string]float32)}
	for rows.Next() {
		var (
			currency string
			rate     float32
		)
		if err := rows.Scan(&currency, &rate); err != nil {
			return nil, err
		}
		currencies.Rates[currency] = rate
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	var availableDate time.Time
	err = db.QueryRow(`SELECT ratedate FROM rates
	WHERE ratedate <= $1 LIMIT 1`, date).Scan(&availableDate)
	currencies.Date = availableDate.Format("2006-01-02")

	if base != "" {
		currencies.Base = base
	} else {
		currencies.Base = "EUR"
	}

	return currencies, nil
}
<|endoftext|>