<commit_before>package core

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/degdb/degdb/protocol"
	"github.com/spaolacci/murmur3"
)

func init() {
	dir, err := ioutil.TempDir("", "degdb-test-files")
	if err != nil {
		log.Fatal(err)
	}
	KeyFilePath = dir + "/degdb-%d.key"
	DatabaseFilePath = dir + "/degdb-%d.db"
}

func testServer(t *testing.T) *server {
	s, err := newServer(0, nil, diskAllocated)
	if err != nil {
		t.Fatal(err)
	}
	return s
}

func TestHTTP(t *testing.T) {
	t.Parallel()

	s := testServer(t)
	go s.network.Listen()
	time.Sleep(10 * time.Millisecond)
	base := fmt.Sprintf("http://localhost:%d", s.network.Port)

	localPeerJSON, err := json.Marshal(s.network.LocalPeer())
	if err != nil {
		t.Fatal(err)
	}
	hosts, err := net.LookupHost("localhost")
	if err != nil {
		t.Fatal(err)
	}

	testData := []struct {
		path string
		want []string
	}{
		{
			"/api/v1/info",
			[]string{string(localPeerJSON)},
		},
		{
			"/api/v1/myip",
			hosts,
		},
		{
			"/api/v1/peers",
			[]string{"[]"},
		},
	}

	for i, td := range testData {
		url := base + td.path
		resp, err := http.Get(url)
		if err != nil {
			t.Error(err)
		}
		body, _ := ioutil.ReadAll(resp.Body)
		bodyTrim := strings.TrimSpace(string(body))
		found := false
		for _, want := range td.want {
			wantTrim := strings.TrimSpace(want)
			if bodyTrim == wantTrim {
				found = true
			}
			break
		}
		if !found {
			t.Errorf("%d. http.Get(%+v) = %+v; not one of %+v", i, td.path, bodyTrim, td.want)
		}
	}
}

var testTriples = []*protocol.Triple{
	{
		Subj: "/m/02mjmr",
		Pred: "/type/object/name",
		Obj:  "Barack Obama",
	},
	{
		Subj: "/m/02mjmr",
		Pred: "/type/object/type",
		Obj:  "/people/person",
	},
	{
		Subj: "/m/0hume",
		Pred: "/type/object/name",
		Obj:  "Hume",
	},
	{
		Subj: "/m/0hume",
		Pred: "/type/object/type",
		Obj:  "/organization/team",
	},
}

// subjInKeyspace appends an increasing number to the prefix until it finds a
// string that will hash into the given keyspace.
func subjInKeyspace(keyspace *protocol.Keyspace, prefix string) string {
	subj := prefix
	i := 1
	for !keyspace.Includes(murmur3.Sum64([]byte(subj))) {
		subj = prefix + strconv.Itoa(i)
		i++
	}
	return subj
}

// testTriplesKeyspcae returns a set of triples in the current keyspace.
func testTriplesKeyspace(keyspace *protocol.Keyspace) []*protocol.Triple {
	subjs := make(map[string]string)
	var triples []*protocol.Triple
	for _, triple := range testTriples {
		triple := *triple
		if subj, ok := subjs[triple.Subj]; ok {
			triple.Subj = subj
		} else {
			subj = subjInKeyspace(keyspace, triple.Subj)
			subjs[triple.Subj] = subj
			triple.Subj = subj
		}
		triples = append(triples, &triple)
	}
	return triples
}

func TestInsertAndRetreiveTriples(t *testing.T) {
	t.Parallel()

	s := testServer(t)
	go s.network.Listen()

	time.Sleep(10 * time.Millisecond)
	base := fmt.Sprintf("http://localhost:%d", s.network.Port)

	testTriples := testTriplesKeyspace(s.network.LocalKeyspace())

	triples, err := json.Marshal(testTriples)
	if err != nil {
		t.Error(err)
	}

	buf := bytes.NewBuffer(triples)

	resp, err := http.Post(base+"/api/v1/insert", "application/json", buf)
	if err != nil {
		t.Error(err)
	}
	out, _ := ioutil.ReadAll(resp.Body)
	if !bytes.Contains(out, []byte(strconv.Itoa(len(testTriples)))) {
		t.Errorf("http.Post(/api/v1/insert) = %+v; missing %+v", string(out), len(testTriples))
	}

	// Takes a bit of time to write
	time.Sleep(100 * time.Millisecond)

	var signedTriples []*protocol.Triple

	resp, err = http.Get(base + "/api/v1/triples")
	if err != nil {
		t.Error(err)
	}
	err = json.NewDecoder(resp.Body).Decode(&signedTriples)
	if err != nil {
		t.Error(err)
	}
	if len(signedTriples) != len(testTriples) {
		t.Errorf("http.Get(/api/v1/insert) = %+v; not %+v", signedTriples, testTriples)
	}
}
<commit_msg>Fixed HTTP test actually<commit_after>package core

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/degdb/degdb/protocol"
	"github.com/spaolacci/murmur3"
)

func init() {
	dir, err := ioutil.TempDir("", "degdb-test-files")
	if err != nil {
		log.Fatal(err)
	}
	KeyFilePath = dir + "/degdb-%d.key"
	DatabaseFilePath = dir + "/degdb-%d.db"
}

func testServer(t *testing.T) *server {
	s, err := newServer(0, nil, diskAllocated)
	if err != nil {
		t.Fatal(err)
	}
	return s
}

func TestHTTP(t *testing.T) {
	t.Parallel()

	s := testServer(t)
	go s.network.Listen()
	time.Sleep(10 * time.Millisecond)
	base := fmt.Sprintf("http://localhost:%d", s.network.Port)

	localPeerJSON, err := json.Marshal(s.network.LocalPeer())
	if err != nil {
		t.Fatal(err)
	}
	hosts, err := net.LookupHost("localhost")
	if err != nil {
		t.Fatal(err)
	}

	testData := []struct {
		path string
		want []string
	}{
		{
			"/api/v1/info",
			[]string{string(localPeerJSON)},
		},
		{
			"/api/v1/myip",
			hosts,
		},
		{
			"/api/v1/peers",
			[]string{"[]"},
		},
	}

	for i, td := range testData {
		url := base + td.path
		resp, err := http.Get(url)
		if err != nil {
			t.Error(err)
		}
		body, _ := ioutil.ReadAll(resp.Body)
		bodyTrim := strings.TrimSpace(string(body))
		found := false
		for _, want := range td.want {
			wantTrim := strings.TrimSpace(want)
			if bodyTrim == wantTrim {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("%d. http.Get(%+v) = %+v; not one of %+v", i, td.path, bodyTrim, td.want)
		}
	}
}

var testTriples = []*protocol.Triple{
	{
		Subj: "/m/02mjmr",
		Pred: "/type/object/name",
		Obj:  "Barack Obama",
	},
	{
		Subj: "/m/02mjmr",
		Pred: "/type/object/type",
		Obj:  "/people/person",
	},
	{
		Subj: "/m/0hume",
		Pred: "/type/object/name",
		Obj:  "Hume",
	},
	{
		Subj: "/m/0hume",
		Pred: "/type/object/type",
		Obj:  "/organization/team",
	},
}

// subjInKeyspace appends an increasing number to the prefix until it finds a
// string that will hash into the given keyspace.
func subjInKeyspace(keyspace *protocol.Keyspace, prefix string) string {
	subj := prefix
	i := 1
	for !keyspace.Includes(murmur3.Sum64([]byte(subj))) {
		subj = prefix + strconv.Itoa(i)
		i++
	}
	return subj
}

// testTriplesKeyspcae returns a set of triples in the current keyspace.
func testTriplesKeyspace(keyspace *protocol.Keyspace) []*protocol.Triple {
	subjs := make(map[string]string)
	var triples []*protocol.Triple
	for _, triple := range testTriples {
		triple := *triple
		if subj, ok := subjs[triple.Subj]; ok {
			triple.Subj = subj
		} else {
			subj = subjInKeyspace(keyspace, triple.Subj)
			subjs[triple.Subj] = subj
			triple.Subj = subj
		}
		triples = append(triples, &triple)
	}
	return triples
}

func TestInsertAndRetreiveTriples(t *testing.T) {
	t.Parallel()

	s := testServer(t)
	go s.network.Listen()

	time.Sleep(10 * time.Millisecond)
	base := fmt.Sprintf("http://localhost:%d", s.network.Port)

	testTriples := testTriplesKeyspace(s.network.LocalKeyspace())

	triples, err := json.Marshal(testTriples)
	if err != nil {
		t.Error(err)
	}

	buf := bytes.NewBuffer(triples)

	resp, err := http.Post(base+"/api/v1/insert", "application/json", buf)
	if err != nil {
		t.Error(err)
	}
	out, _ := ioutil.ReadAll(resp.Body)
	if !bytes.Contains(out, []byte(strconv.Itoa(len(testTriples)))) {
		t.Errorf("http.Post(/api/v1/insert) = %+v; missing %+v", string(out), len(testTriples))
	}

	// Takes a bit of time to write
	time.Sleep(100 * time.Millisecond)

	var signedTriples []*protocol.Triple

	resp, err = http.Get(base + "/api/v1/triples")
	if err != nil {
		t.Error(err)
	}
	err = json.NewDecoder(resp.Body).Decode(&signedTriples)
	if err != nil {
		t.Error(err)
	}
	if len(signedTriples) != len(testTriples) {
		t.Errorf("http.Get(/api/v1/insert) = %+v; not %+v", signedTriples, testTriples)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"jhhgo.us/pukaws/bookmark"

	"gopkg.in/mgo.v2/bson"

	"github.com/bitly/go-simplejson"
	"github.com/manyminds/api2go"
)

var (
	api *api2go.API
	bmk bookmark.Bookmark
)

func TestMain(m *testing.M) {
	flag.Parse()
	var bms bookmark.Storage
	if testing.Short() {
		fmt.Println("Using BookmarkMemoryStorage for testing")
		bms = bookmark.NewMemoryStorage()
	} else {
		fmt.Println("Using BookmarkMgoStorage for testing")
		uri := os.Getenv("MONGODB_URI")
		if uri == "" {
			uri = "mongodb://localhost/test"
		}
		var err error
		bms, err = bookmark.NewMgoStorage(uri)
		if err != nil {
			fmt.Println(err)
		}
	}
	api = api2go.NewAPIWithBaseURL("v0", "http://localhost:31415")
	api.AddResource(bookmark.Bookmark{}, bookmark.Resource{Storage: bms})
	bookmarks, err := bms.GetAll(bookmark.Query{})
	if err != nil {
		fmt.Println(err)
	}
	l := len(bookmarks)
	if l == 0 {
		fmt.Println("len(bookmarks) = 0 in test data")
		os.Exit(1)
	}
	rand.Seed(time.Now().UnixNano())
	bmk = bookmarks[rand.Intn(l)]
	os.Exit(m.Run())
}

func TestGetAll(t *testing.T) {
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "/v0/bookmarks", nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}
}

func TestCreate(t *testing.T) {
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/v0/bookmarks", strings.NewReader(`
		{
			"data": {
				"type": "bookmarks",
				"attributes": {
				"title": "foo"
				}
			}
		}
	`))
	if err != nil {
		t.Error(err)
	}

	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusCreated)
	}

	js, err := simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}

	data := js.Get("data")
	typ := data.Get("type").MustString()
	if typ != "bookmarks" {
		t.Errorf("type = %q; want: %q", typ, "foo")
	}

	id := data.Get("id").MustString()
	if !bson.IsObjectIdHex(id) {
		t.Errorf("id = %q is not ObjectId", id)
	}

	title := data.Get("attributes").Get("title").MustString()
	if title != "foo" {
		t.Errorf("title = %q; want: %q", title, "foo")
	}

	//Fetch again and compare
	rec = httptest.NewRecorder()
	req, err = http.NewRequest("GET", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}
	js, err = simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}
	data = js.Get("data")
	title = data.Get("attributes").Get("title").MustString()
	if title != "foo" {
		t.Errorf("title = %q; want: %q", title, "foo")
	}

}

func TestPatch(t *testing.T) {
	rec := httptest.NewRecorder()
	id := bmk.GetID()
	req, err := http.NewRequest("PATCH", "/v0/bookmarks/"+id, strings.NewReader(`
		{
			"data": {
				"type": "bookmarks",
				"attributes": {
					"title": "bar"
				}
			}
		}
		`))
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}

	js, err := simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}

	data := js.Get("data")

	nid := data.Get("id").MustString()
	if nid != id {
		t.Errorf("id = %q; want: %q", nid, id)
	}

	title := data.Get("attributes").Get("title").MustString()
	if title != "bar" {
		t.Errorf("title = %q; want: %q", title, "bar")
	}

	//Fetch again and compare
	rec = httptest.NewRecorder()
	fmt.Printf("GET /v0/bookmarks/%s\n", id)
	req, err = http.NewRequest("GET", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}
	fmt.Println("BODY", rec.Body.String())
	js, err = simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}
	data = js.Get("data")
	title = data.Get("attributes").Get("title").MustString()
	if title != "bar" {
		t.Errorf("title = %q; want: %q", title, "bar")
	}
}

func TestDelete(t *testing.T) {
	// Create bookmark to delete
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/v0/bookmarks", strings.NewReader(`
		{
			"data": {
				"type": "bookmarks"
			}
		}
	`))
	if err != nil {
		t.Error(err)
	}

	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusCreated)
	}

	js, err := simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}

	id := js.Get("data").Get("id").MustString()
	if !bson.IsObjectIdHex(id) {
		t.Errorf("id = %q is not ObjectId", id)
	}

	// Now delete bookmark with id from above
	rec = httptest.NewRecorder()
	req, err = http.NewRequest("DELETE", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNoContent {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}

	// Should be really gone
	rec = httptest.NewRecorder()
	req, err = http.NewRequest("GET", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNotFound {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusNotFound)
	}
	js, err = simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}
}

func TestTypeError(t *testing.T) {
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/v0/bookmarks", strings.NewReader(`
		{
			"data": {
				"type": "bogus"
			}
		}
		`))
	if err != nil {
		t.Error(err.Error())
	}

	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNotAcceptable {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusNotAcceptable)
	}
}

func TestDeleteNotFoundError(t *testing.T) {
	rec := httptest.NewRecorder()
	nf := bson.NewObjectId().Hex()
	req, err := http.NewRequest("DELETE", "/v0/bookmarks/"+nf, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNotFound {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusNotFound)
	}

}
<commit_msg>Don't hit database for testing.<commit_after>package main

import (
	"flag"
	"fmt"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"jhhgo.us/pukaws/bookmark"

	"gopkg.in/mgo.v2/bson"

	"github.com/bitly/go-simplejson"
	"github.com/manyminds/api2go"
)

var (
	api *api2go.API
	bmk bookmark.Bookmark
)

func TestMain(m *testing.M) {
	flag.Parse()
	bms := bookmark.NewMemoryStorage()
	api = api2go.NewAPIWithBaseURL("v0", "http://localhost:31415")
	api.AddResource(bookmark.Bookmark{}, bookmark.Resource{Storage: bms})
	bookmarks, err := bms.GetAll(bookmark.Query{})
	if err != nil {
		fmt.Println(err)
	}
	l := len(bookmarks)
	if l == 0 {
		fmt.Println("len(bookmarks) = 0 in test data")
		os.Exit(1)
	}
	rand.Seed(time.Now().UnixNano())
	bmk = bookmarks[rand.Intn(l)]
	os.Exit(m.Run())
}

func TestGetAll(t *testing.T) {
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("GET", "/v0/bookmarks", nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}
}

func TestCreate(t *testing.T) {
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/v0/bookmarks", strings.NewReader(`
		{
			"data": {
				"type": "bookmarks",
				"attributes": {
				"title": "foo"
				}
			}
		}
	`))
	if err != nil {
		t.Error(err)
	}

	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusCreated)
	}

	js, err := simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}

	data := js.Get("data")
	typ := data.Get("type").MustString()
	if typ != "bookmarks" {
		t.Errorf("type = %q; want: %q", typ, "foo")
	}

	id := data.Get("id").MustString()
	if !bson.IsObjectIdHex(id) {
		t.Errorf("id = %q is not ObjectId", id)
	}

	title := data.Get("attributes").Get("title").MustString()
	if title != "foo" {
		t.Errorf("title = %q; want: %q", title, "foo")
	}

	//Fetch again and compare
	rec = httptest.NewRecorder()
	req, err = http.NewRequest("GET", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}
	js, err = simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}
	data = js.Get("data")
	title = data.Get("attributes").Get("title").MustString()
	if title != "foo" {
		t.Errorf("title = %q; want: %q", title, "foo")
	}

}

func TestPatch(t *testing.T) {
	rec := httptest.NewRecorder()
	id := bmk.GetID()
	req, err := http.NewRequest("PATCH", "/v0/bookmarks/"+id, strings.NewReader(`
		{
			"data": {
				"type": "bookmarks",
				"attributes": {
					"title": "bar"
				}
			}
		}
		`))
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}

	js, err := simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}

	data := js.Get("data")

	nid := data.Get("id").MustString()
	if nid != id {
		t.Errorf("id = %q; want: %q", nid, id)
	}

	title := data.Get("attributes").Get("title").MustString()
	if title != "bar" {
		t.Errorf("title = %q; want: %q", title, "bar")
	}

	//Fetch again and compare
	rec = httptest.NewRecorder()
	fmt.Printf("GET /v0/bookmarks/%s\n", id)
	req, err = http.NewRequest("GET", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}
	fmt.Println("BODY", rec.Body.String())
	js, err = simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}
	data = js.Get("data")
	title = data.Get("attributes").Get("title").MustString()
	if title != "bar" {
		t.Errorf("title = %q; want: %q", title, "bar")
	}
}

func TestDelete(t *testing.T) {
	// Create bookmark to delete
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/v0/bookmarks", strings.NewReader(`
		{
			"data": {
				"type": "bookmarks"
			}
		}
	`))
	if err != nil {
		t.Error(err)
	}

	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusCreated)
	}

	js, err := simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}

	id := js.Get("data").Get("id").MustString()
	if !bson.IsObjectIdHex(id) {
		t.Errorf("id = %q is not ObjectId", id)
	}

	// Now delete bookmark with id from above
	rec = httptest.NewRecorder()
	req, err = http.NewRequest("DELETE", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNoContent {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusOK)
	}

	// Should be really gone
	rec = httptest.NewRecorder()
	req, err = http.NewRequest("GET", "/v0/bookmarks/"+id, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNotFound {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusNotFound)
	}
	js, err = simplejson.NewJson(rec.Body.Bytes())
	if err != nil {
		t.Error(err)
	}
}

func TestTypeError(t *testing.T) {
	rec := httptest.NewRecorder()
	req, err := http.NewRequest("POST", "/v0/bookmarks", strings.NewReader(`
		{
			"data": {
				"type": "bogus"
			}
		}
		`))
	if err != nil {
		t.Error(err.Error())
	}

	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNotAcceptable {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusNotAcceptable)
	}
}

func TestDeleteNotFoundError(t *testing.T) {
	rec := httptest.NewRecorder()
	nf := bson.NewObjectId().Hex()
	req, err := http.NewRequest("DELETE", "/v0/bookmarks/"+nf, nil)
	if err != nil {
		t.Error(err)
	}
	api.Handler().ServeHTTP(rec, req)
	if rec.Code != http.StatusNotFound {
		t.Errorf("http status = %d; want: %d", rec.Code, http.StatusNotFound)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/aerogo/aero"
	"github.com/animenotifier/notify.moe/arn"
	"github.com/animenotifier/notify.moe/utils/routetests"
	"github.com/stretchr/testify/assert"
)

func TestRoutes(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	// Iterate through every route
	for _, examples := range routetests.All() {
		// Iterate through every example specified for that route
		for _, example := range examples {
			testRoute(t, app, example)
		}
	}
}

func TestAnimePages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for anime := range arn.StreamAnime() {
		testRoute(t, app, anime.Link())
	}
}

func TestSoundTrackPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for soundtrack := range arn.StreamSoundTracks() {
		testRoute(t, app, soundtrack.Link())
		assert.NotNil(t, soundtrack.Creator())
	}
}

func TestAMVPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for amv := range arn.StreamAMVs() {
		testRoute(t, app, amv.Link())
		assert.NotNil(t, amv.Creator())
	}
}

func TestCompanyPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for company := range arn.StreamCompanies() {
		testRoute(t, app, company.Link())
	}
}

func TestThreadPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for thread := range arn.StreamThreads() {
		testRoute(t, app, thread.Link())
		assert.NotNil(t, thread.Creator())
	}
}

func TestPostPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for post := range arn.StreamPosts() {
		testRoute(t, app, post.Link())
		assert.NotNil(t, post.Creator())
	}
}

func TestQuotePages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for quote := range arn.StreamQuotes() {
		testRoute(t, app, quote.Link())
		assert.NotNil(t, quote.Creator())
	}
}

// func TestUserPages(t *testing.T) {
// 	t.Parallel()
// 	app := configure(aero.New())

// 	for user := range arn.StreamUsers() {
// 		testRoute(t, app, user.Link())
// 	}
// }

func testRoute(t *testing.T, app *aero.Application, route string) {
	request := httptest.NewRequest("GET", strings.ReplaceAll(route, " ", "%20"), nil)
	response := httptest.NewRecorder()
	app.ServeHTTP(response, request)
	status := response.Code

	switch status {
	case 200, 302:
		// 200 and 302 are allowed
	default:
		panic(fmt.Errorf("%s | Wrong status code | %v instead of %v", route, status, http.StatusOK))
	}
}
<commit_msg>Fixed tests<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/aerogo/aero"
	"github.com/animenotifier/notify.moe/arn"
	"github.com/animenotifier/notify.moe/utils/routetests"
	"github.com/stretchr/testify/assert"
)

func TestRoutes(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	// Iterate through every route
	for _, examples := range routetests.All() {
		// Iterate through every example specified for that route
		for _, example := range examples {
			testRoute(t, app, example)
		}
	}
}

func TestAnimePages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for anime := range arn.StreamAnime() {
		testRoute(t, app, anime.Link())
	}
}

func TestSoundTrackPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for soundtrack := range arn.StreamSoundTracks() {
		testRoute(t, app, soundtrack.Link())
		assert.NotNil(t, soundtrack.Creator())
	}
}

func TestAMVPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for amv := range arn.StreamAMVs() {
		testRoute(t, app, amv.Link())
		assert.NotNil(t, amv.Creator())
	}
}

func TestCompanyPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for company := range arn.StreamCompanies() {
		testRoute(t, app, company.Link())
	}
}

func TestThreadPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for thread := range arn.StreamThreads() {
		testRoute(t, app, thread.Link())
		assert.NotNil(t, thread.Creator())
	}
}

func TestPostPages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for post := range arn.StreamPosts() {
		testRoute(t, app, post.Link())
		assert.NotNil(t, post.Creator())
	}
}

func TestQuotePages(t *testing.T) {
	t.Parallel()
	app := configure(aero.New())
	app.BindMiddleware()

	for quote := range arn.StreamQuotes() {
		testRoute(t, app, quote.Link())
		assert.NotNil(t, quote.Creator())
	}
}

// func TestUserPages(t *testing.T) {
// 	t.Parallel()
// 	app := configure(aero.New())

// 	for user := range arn.StreamUsers() {
// 		testRoute(t, app, user.Link())
// 	}
// }

func testRoute(t *testing.T, app *aero.Application, route string) {
	request := httptest.NewRequest("GET", strings.ReplaceAll(route, " ", "%20"), nil)
	response := httptest.NewRecorder()
	app.ServeHTTP(response, request)
	status := response.Code

	switch status {
	case http.StatusOK, http.StatusTemporaryRedirect, http.StatusPermanentRedirect:
		// OK
	default:
		panic(fmt.Errorf("%s | Wrong status code | %v instead of %v", route, status, http.StatusOK))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"hash/crc64"
	"sync"
	"testing"
)

func TestConfigCheck(t *testing.T) {
	ok := Config{
		SourceBuffer: 3,
		FrameBytes:   1,
		Path:         "/dev/stdin",
		Addr:         ":http",
	}
	var c Config
	if c, c.SourceBuffer = ok, 0; c.Check() == nil {
		t.Error("SourceBuffer 0 accepted")
	}
	if c, c.SourceBuffer = ok, 2; c.Check() == nil {
		t.Error("SourceBuffer 2 accepted")
	}
	if c, c.FrameBytes = ok, 0; c.Check() == nil {
		t.Error("FrameBytes 0 accepted")
	}
	if c, c.Path = ok, ""; c.Check() == nil {
		t.Error("Path empty accepted")
	}
	if c = ok; c.Check() != nil {
		t.Error("Valid config not accepted")
	}
}

func TestContentEqual(t *testing.T) {
	source := GetSource("/dev/urandom", Config{
		SourceBuffer: 32,
		FrameBytes:   65536,
	})
	nConsumers := 10
	done := make(chan uint64)
	tab := crc64.MakeTable(crc64.ECMA)
	for i := 0; i < nConsumers; i++ {
		go func() {
			var hash uint64
			defer func() { done <- hash }()
			var frame = make(DataFrame, source.frameBytes)
			var nextFrame uint64
			for f := 0; f < 130; f++ {
				var err error
				if _, err = source.Next(&nextFrame, frame); err != nil {
					t.Fatalf("source.Next(%d, frame): %s", nextFrame, err)
				}
				hash = crc64.Update(hash, tab, frame)
			}
		}()
	}
	h0 := <-done
	for i := 1; i < nConsumers; i++ {
		hi := <-done
		if h0 == 0 {
			h0 = hi
		} else if hi != h0 {
			t.Errorf("hash mismatch: h0=%x, h%d=%x", h0, i, hi)
		}
	}
	source.Close()
}

// 32s of CD audio, 1s per frame, 1000 consumers
func BenchmarkSource1KConsumers(b *testing.B) {
	benchSource(b, 1000, Config{
		SourceBuffer: 32,
		FrameBytes:   176800,
	})
}

// 320s of CD audio, 1s per frame, 10000 consumers
func BenchmarkSource10KConsumers(b *testing.B) {
	benchSource(b, 10000, Config{
		SourceBuffer: 320,
		FrameBytes:   176800,
	})
}

// 32s of 128kbps audio, 1s per frame, 10000 consumers
func BenchmarkSource128kbps10KConsumers(b *testing.B) {
	benchSource(b, 10000, Config{
		SourceBuffer: 32,
		FrameBytes:   128 * 1000 / 8,
	})
}

func BenchmarkSourceTinyBuffer(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 3,
		FrameBytes:   1,
	})
}

func BenchmarkSourceMediumBuffer(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 64,
		FrameBytes:   64,
	})
}

func BenchmarkSourceBigFrame(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 64,
		FrameBytes:   1048576,
	})
}

func BenchmarkSourceBigBuffer(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 1048576,
		FrameBytes:   64,
	})
}

func benchSource(b *testing.B, nConsumers int, c Config) {
	source := GetSource("/dev/zero", c)
	wg := &sync.WaitGroup{}
	wg.Add(nConsumers)
	for c := 0; c < nConsumers; c++ {
		go func(c int) {
			consume(b, source, c)
			wg.Done()
		}(c)
	}
	wg.Wait()
	source.Close()
}

func consume(b *testing.B, source *Source, label interface{}) {
	var frame DataFrame
	var nextFrame uint64
	for i := uint64(0); i < 10*uint64(b.N); i++ {
		if _, err := source.Next(&nextFrame, frame); err != nil {
			b.Fatalf("source.Next(%d, frame): %s", nextFrame, err)
		}
	}
}

func ExampleStub() {
	fmt.Println("Hello")
	// Output:
	// Hello
}
<commit_msg>Fix tests.<commit_after>package main

import (
	"fmt"
	"hash/crc64"
	"sync"
	"testing"
)

func TestConfigCheck(t *testing.T) {
	ok := Config{
		SourceBuffer: 3,
		FrameBytes:   1,
		Path:         "/dev/stdin",
		Addr:         ":http",
	}
	var c Config
	if c, c.SourceBuffer = ok, 0; c.Check() == nil {
		t.Error("SourceBuffer 0 accepted")
	}
	if c, c.SourceBuffer = ok, 2; c.Check() == nil {
		t.Error("SourceBuffer 2 accepted")
	}
	if c, c.FrameBytes = ok, 0; c.Check() == nil {
		t.Error("FrameBytes 0 accepted")
	}
	if c, c.Path = ok, ""; c.Check() == nil {
		t.Error("Path empty accepted")
	}
	if c = ok; c.Check() != nil {
		t.Error("Valid config not accepted")
	}
}

func TestContentEqual(t *testing.T) {
	source := GetSource("/dev/urandom", Config{
		SourceBuffer: 32,
		FrameBytes:   65536,
	})
	nConsumers := 10
	done := make(chan uint64)
	tab := crc64.MakeTable(crc64.ECMA)
	for i := 0; i < nConsumers; i++ {
		go func() {
			var hash uint64
			defer func() { done <- hash }()
			var frame = make(DataFrame, source.frameBytes)
			var nextFrame uint64
			for f := 0; f < 130; f++ {
				var err error
				if _, err = source.Next(&nextFrame, frame); err != nil {
					t.Fatalf("source.Next(%d, frame): %s", nextFrame, err)
				}
				hash = crc64.Update(hash, tab, frame)
			}
		}()
	}
	h0 := <-done
	for i := 1; i < nConsumers; i++ {
		hi := <-done
		if h0 == 0 {
			h0 = hi
		} else if hi != h0 {
			t.Errorf("hash mismatch: h0=%x, h%d=%x", h0, i, hi)
		}
	}
	source.Close()
}

// 32s of CD audio, 1s per frame, 1000 consumers
func BenchmarkSource1KConsumers(b *testing.B) {
	benchSource(b, 1000, Config{
		SourceBuffer: 32,
		FrameBytes:   176800,
	})
}

// 320s of CD audio, 1s per frame, 10000 consumers
func BenchmarkSource10KConsumers(b *testing.B) {
	benchSource(b, 10000, Config{
		SourceBuffer: 320,
		FrameBytes:   176800,
	})
}

// 32s of 128kbps audio, 1s per frame, 10000 consumers
func BenchmarkSource128kbps10KConsumers(b *testing.B) {
	benchSource(b, 10000, Config{
		SourceBuffer: 32,
		FrameBytes:   128 * 1000 / 8,
	})
}

func BenchmarkSourceTinyBuffer(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 3,
		FrameBytes:   1,
	})
}

func BenchmarkSourceMediumBuffer(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 64,
		FrameBytes:   64,
	})
}

func BenchmarkSourceBigFrame(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 64,
		FrameBytes:   1048576,
	})
}

func BenchmarkSourceBigBuffer(b *testing.B) {
	benchSource(b, 1, Config{
		SourceBuffer: 1048576,
		FrameBytes:   64,
	})
}

func benchSource(b *testing.B, nConsumers int, c Config) {
	source := GetSource("/dev/zero", c)
	wg := &sync.WaitGroup{}
	wg.Add(nConsumers)
	for c := 0; c < nConsumers; c++ {
		go func(c int) {
			defer wg.Done()
			consume(b, source, c)
		}(c)
	}
	wg.Wait()
	source.Close()
}

func consume(b *testing.B, source *Source, label interface{}) {
	var frame DataFrame = make(DataFrame, source.frameBytes)
	var nextFrame uint64
	for i := uint64(0); i < 10*uint64(b.N); i++ {
		if _, err := source.Next(&nextFrame, frame); err != nil {
			b.Fatalf("source.Next(%d, frame): %s", nextFrame, err)
		}
	}
}

func ExampleStub() {
	fmt.Println("Hello")
	// Output:
	// Hello
}
<|endoftext|>


<commit_before>

package main

import (
	"testing"
)

func TestBmn(t *testing.T) {

	query := "flipkart.com"
	expectedCount := 23

	result,err := bmn(query)
	if err != nil {
		t.Error(err)
	}

	if expectedCount != len(result.Accounts) {
		t.Errorf("Accounts count doesn't match. Expected %s, got %s", expectedCount,
			len(result.Accounts))
	}
}

func BenchmarkGeocode(b *testing.B) {
	for i := 0; i < b.N; i++ {
		bmn("flipkart.com")
	}
}<commit_msg>updated comments<commit_after>

package main

import (
	"testing"
)

func TestBmn(t *testing.T) {

	query := "flipkart.com"
	expectedCount := 23

	result,err := bmn(query)
	if err != nil {
		t.Error(err)
	}

	if expectedCount != len(result.Accounts) {
		t.Errorf("Accounts count doesn't match. Expected %s, got %s", expectedCount,
			len(result.Accounts))
	}
}

//Benchmark Test
func BenchmarkGeocode(b *testing.B) {
	for i := 0; i < b.N; i++ {
		bmn("flipkart.com")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"testing"
)

func BenchmarkHello(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ds := Datasource{Name: fmt.Sprintf("datasource %v", i)}
		addItemToState(ds)
	}
}

// All state is not reset

func TestSingleDsIntoState(t *testing.T) {
	tmp := len(State.Vals)
	ds1 := Datasource{Name: "some name"}
	addItemToState(ds1)
	if (len(State.Vals) - tmp) != 1 {
		t.Fatal("Not able to add datasource to internal state")
	}
}

func TestDuplicateEntriesIntoState(t *testing.T) {
	tmp := len(State.Vals)
	ds1 := Datasource{Name: "TestDuplicateEntriesIntoState same name"}
	ds2 := Datasource{Name: "TestDuplicateEntriesIntoState same name"}
	addItemToState(ds1)
	addItemToState(ds2)
	if (len(State.Vals) - tmp) > 1 {
		t.Fatal("Able to add more than one data source with the same name")
	}
}

func TestDontStoreMoreThan1k(t *testing.T) {
	// test that we dont keep more than maxState items in the state
	// and that when we go above it, we keep the last values (and not
	// the oldest ones)

	// reset internal state so that we know exactly what the last value
	// should be in the internal state
	State.Vals = nil
	const testString string = "TestDontStoreMoreThanMaxState, item: "

	ds := Datasource{Name: "tmp"}
	for i := 0; i < maxState+10; i++ {
		ds.Name = fmt.Sprintf("%v %v", testString, i)
		addItemToState(ds)
	}
	if len(State.Vals) > maxState {
		t.Fatal("Able to add more than maxState items into State.Vals")
	}

	// At this point the last item should be maxState+10-1 (0 indexed, remember)
	knownName := fmt.Sprintf("%v %v", testString, maxState+10-1)
	lastItem := State.Vals[len(State.Vals)-1:]
	if lastItem[0].Name != knownName {
		t.Fatal(fmt.Sprintf("The expected last item (after adding more then maxState items) was [%v] but actually found [%v]!", knownName, lastItem[0].Name))
	}
}

func TestParsing(t *testing.T) {

	type testpair struct {
		incr int
		line string
	}

	// integer indicates with how much the count of internal state should go up
	// (could be 0, eg. not valid test input, or ds already captured)
	var testCases = []testpair{
		{1, "launchctl-carbon.stdout:24/08/2014 17:59:53 :: [creates] creating database file /opt/graphite/storage/whisper/mac-mini_local/collectd/df-Volumes-Recovery_HD/df_complex-free.wsp (archive=[(60, 525600), (600, 518400)] xff=None agg=None)"},
		{0, "astt"},
		{0, "launchctl-carbon.stdout:24/08/2014 17:59:53 :: [creates] creating database file / (archive=[(60, 525600), (600, 518400)] xff=None agg=None)"},
		{1, "13/09/2014 23:10:56 :: [creates] creating database file /opt/graphite/storage/whisper/local/random/diceroll.wsp (archive=[(60, 525600), (600, 518400)] xff=None agg=None)"},
	}

	State.Vals = nil // start fresh
	prev_count := len(State.Vals)

	for _, test := range testCases {
		parseLine(test.line)

		if len(State.Vals) != prev_count+test.incr {
			t.Fatal(fmt.Sprintf("Parsed line, should have seen %v new entries, saw %v. Line: %v", test.incr, len(State.Vals)-prev_count, test.line))
		}
		prev_count = len(State.Vals)
	}
}
<commit_msg>fail tests if Create_date is the nil-date (eg not parsed properly)<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
)

func BenchmarkHello(b *testing.B) {
	for i := 0; i < b.N; i++ {
		ds := Datasource{Name: fmt.Sprintf("datasource %v", i)}
		addItemToState(ds)
	}
}

// All state is not reset

func TestGettingAsset(t *testing.T) {
	data, _ := Asset("index.html")
	if len(data) == 0 {
		t.Fatal("Could not load static asset index.html")
	}
}

func TestHTTPServingAssets(t *testing.T) {
	handler := func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "something failed", http.StatusInternalServerError)
	}

	req, err := http.NewRequest("GET", "http://www.google.com", nil)
	if err != nil {
		t.Fatal(err)
	}

	w := httptest.NewRecorder()
	handler(w, req)

	t.Fatal(fmt.Sprintf("%+v", w))
}

func TestSingleDsIntoState(t *testing.T) {
	tmp := len(State.Vals)
	ds1 := Datasource{Name: "some name"}
	addItemToState(ds1)
	if (len(State.Vals) - tmp) != 1 {
		t.Fatal("Not able to add datasource to internal state")
	}
}

func TestDuplicateEntriesIntoState(t *testing.T) {
	tmp := len(State.Vals)
	ds1 := Datasource{Name: "TestDuplicateEntriesIntoState same name"}
	ds2 := Datasource{Name: "TestDuplicateEntriesIntoState same name"}
	addItemToState(ds1)
	addItemToState(ds2)
	if (len(State.Vals) - tmp) > 1 {
		t.Fatal("Able to add more than one data source with the same name")
	}
}

func TestDontStoreMoreThan1k(t *testing.T) {
	// test that we dont keep more than maxState items in the state
	// and that when we go above it, we keep the last values (and not
	// the oldest ones)

	// reset internal state so that we know exactly what the last value
	// should be in the internal state
	State.Vals = nil
	const testString string = "TestDontStoreMoreThanMaxState, item: "

	ds := Datasource{Name: "tmp"}
	for i := 0; i < maxState+11; i++ {
		ds.Name = fmt.Sprintf("%v %v", testString, i)
		addItemToState(ds)
	}
	if len(State.Vals) > maxState {
		t.Fatal("Able to add more than maxState items into State.Vals")
	}

	// At this point the last item should be maxState+10-1 (0 indexed, remember)
	knownName := fmt.Sprintf("%v %v", testString, maxState+10-1)
	lastItem := State.Vals[len(State.Vals)-1:]
	if lastItem[0].Name != knownName {
		t.Fatal(fmt.Sprintf("The expected last item (after adding more then maxState items) was [%v] but actually found [%v]!", knownName, lastItem[0].Name))
	}
}

func TestParsing(t *testing.T) {

	type testpair struct {
		incr int
		line string
	}

	// integer indicates with how much the count of internal state should go up
	// (could be 0, eg. not valid test input, or ds already captured)
	// Also, each line should have a non-nil Create_date
	var testCases = []testpair{
		{1, "launchctl-carbon.stdout:24/08/2014 17:59:53 :: [creates] creating database file /opt/graphite/storage/whisper/mac-mini_local/collectd/df-Volumes-Recovery_HD/df_complex-free.wsp (archive=[(60, 525600), (600, 518400)] xff=None agg=None)"},
		{0, "astt"},
		{0, "launchctl-carbon.stdout:24/08/2014 17:59:53 :: [creates] creating database file / (archive=[(60, 525600), (600, 518400)] xff=None agg=None)"},
		{1, "13/09/2014 23:10:56 :: [creates] creating database file /opt/graphite/storage/whisper/local/random/diceroll.wsp (archive=[(60, 525600), (600, 518400)] xff=None agg=None)"},
	}
	State.Vals = nil // start fresh
	prev_count := len(State.Vals)

	for _, test := range testCases {
		parseLine(test.line)

		if len(State.Vals) != prev_count+test.incr {
			t.Fatal(fmt.Sprintf("Parsed line, should have seen %v new entries, saw %v. Line: %v", test.incr, len(State.Vals)-prev_count, test.line))
		}
		prev_count = len(State.Vals)

		// Check date parseing
		last_ds := State.Vals[len(State.Vals)-1:][0]
		// Do any checking on the actual values for the data source (not complete yet)
		if last_ds.Create_date.IsZero() {
			t.Fatal(fmt.Sprintf("Data source has invalid Create_date: %+v", last_ds))
		}
	}
}
<|endoftext|>


<commit_before>// Package canvas is responsible to convert the ascii symbols to hand drawn diagrams.
// It implements the basic canvas drawing operations like moveTo, lineTo, fillText.

// The Draw method signature declared in the Drawer interface, implements the method separately on Line and Text struct.
package canvas

import (
	"math"
	"math/rand"

	"github.com/fogleman/gg"
)

// Canvas defines the canvas basic elements.
type Canvas struct {
	*gg.Context
	font      string
	lineWidth float64
}

// Drawer interface defines the Canvas drawing method.
// Struct needs to implement the Draw method.
type Drawer interface {
	Draw(*Canvas)
}

// CellSize defines symbol's cell size.
const CellSize float64 = 20

// NewCanvas is a constructor method, which instantiates a new Canvas element.
func NewCanvas(ctx *gg.Context, font string, lineWidth float64) *Canvas {
	if err := ctx.LoadFontFace(font, 20); err != nil {
		panic(err)
	}
	ctx.SetLineWidth(lineWidth)
	return &Canvas{ctx, font, lineWidth}
}

var _x0, _y0 float64

// moveTo move the pointer to (x0,y0) position
func (ctx *Canvas) moveTo(x0, y0 float64) {
	_x0 = x0
	_y0 = y0
}

// lineTo move the pointer to (x1,y1) position
func (ctx *Canvas) lineTo(x1, y1 float64) {
	ctx.shakyLine(_x0, _y0, x1, y1)
	ctx.moveTo(x1, y1)
}

// shakyLine draw a shaky line between (x0, y0) and (x1, y1).
func (ctx *Canvas) shakyLine(x0, y0, x1, y1 float64) {
	var dx, dy float64
	var k1, k2, l3, l4, x3, y3, x4, y4 float64
	dx = x1 - x0
	dy = y1 - y0

	l := math.Sqrt(dx*dx + dy*dy)

	// Pick two random points that are placed on different sides of the line that passes through.
	K := math.Sqrt(l) / 1.5
	k1 = rand.Float64()
	k2 = rand.Float64()
	l3 = rand.Float64() * K
	l4 = rand.Float64() * K

	// Pick a random point on the line between P0 and P1.
	x3 = x0 + dx*k1 + dy/l*l3
	y3 = y0 + dy*k1 - dx/l*l3

	// Pick a random point on the line between P0 and P1 but in the opposite direction.
	x4 = x0 + dx*k2 - dy/l*l4
	y4 = y0 + dy*k2 + dx/l*l4

	// Draw a bezier curve trough the four selected points.
	ctx.MoveTo(x0, y0)
	ctx.CubicTo(x3, y3, x4, y4, x1, y1)
}

// bulb draws a shaky bulb (used for line endings).
func (ctx *Canvas) bulb(x0, y0 float64) {
	fuzziness := random()*2 - 1

	for i := 0; i < 3; i++ {
		ctx.DrawArc(x0+fuzziness, y0+fuzziness, 5, 0, math.Pi*2)
		ctx.ClosePath()
		ctx.Fill()
	}
}

// arrowHead draws a shaky arrowhead at the (x1, y1) as an ending
// for the line from (x0, y0) to (x1, y1).
func (ctx *Canvas) arrowHead(x0, y0, x1, y1 float64) {
	dx := x0 - x1
	dy := y0 - y1

	alpha := math.Atan(dy / dx)

	if dy == 0 {
		if dx < 0 {
			alpha = -math.Pi
		} else {
			alpha = 0
		}
	}
	alpha3 := alpha + 0.5
	alpha4 := alpha - 0.5

	l3 := float64(20.0)
	x3 := x1 + l3*math.Cos(alpha3)
	y3 := y1 + l3*math.Sin(alpha3)

	ctx.moveTo(x3, y3)
	ctx.lineTo(x1, y1)
	ctx.Stroke()

	l4 := float64(20.0)
	x4 := x1 + l4*math.Cos(alpha4)
	y4 := y1 + l4*math.Sin(alpha4)

	ctx.moveTo(x4, y4)
	ctx.lineTo(x1, y1)
	ctx.Stroke()
}

// fillText fill out the text.
func (ctx *Canvas) fillText(text string, x0, y0 float64) {
	ctx.DrawString(text, x0, y0)
}

// Draw draws the text annotation at (x0, y0) with the given color.
func (text *Text) Draw(ctx *Canvas) {
	ctx.SetHexColor(text.color)
	ctx.fillText(text.text, X(float64(text.x0)), Y(float64(text.y0)+0.5))
}

// Draw draws a line from (x0, y0) to (x1, y1) with the given color.
func (line *Line) Draw(ctx *Canvas) {
	ctx.SetHexColor(line.color)
	ctx.SetLineWidth(ctx.lineWidth)
	ctx.moveTo(X(float64(line.x0)), Y(float64(line.y0)))
	ctx.lineTo(X(float64(line.x1)), Y(float64(line.y1)))
	ctx.Stroke()

	// Draw given type of ending on the (x1, y1).
	_ending := func(ctx *Canvas, typ string, x0, y0, x1, y1 float64) {
		switch typ {
		case "circle":
			ctx.bulb(x1, y1)
			return
		case "arrow":
			ctx.arrowHead(x0, y0, x1, y1)
			return
		}
	}

	_ending(ctx, line.start, X(float64(line.x1)), Y(float64(line.y1)), X(float64(line.x0)), Y(float64(line.y0)))
	_ending(ctx, line.end, X(float64(line.x0)), Y(float64(line.y0)), X(float64(line.x1)), Y(float64(line.y1)))
}

// X returns the symbols x position.
func X(x float64) float64 {
	return x*CellSize + (CellSize / 2)
}

// Y returns the symbol y position.
func Y(y float64) float64 {
	return y*CellSize + (CellSize / 2)
}
<commit_msg>Update canvas.go<commit_after>// Package canvas is responsible to convert the ascii symbols to hand drawn diagrams.
// It implements the basic canvas drawing operations like moveTo, lineTo, fillText.

// The Draw method signature declared in the Drawer interface implements the method separately on Line and Text struct.
package canvas

import (
	"math"
	"math/rand"

	"github.com/fogleman/gg"
)

// Canvas defines the canvas basic elements.
type Canvas struct {
	*gg.Context
	font      string
	lineWidth float64
}

// Drawer interface defines the Canvas drawing method.
// Struct needs to implement the Draw method.
type Drawer interface {
	Draw(*Canvas)
}

// CellSize defines symbol's cell size.
const CellSize float64 = 20

// NewCanvas is a constructor method, which instantiates a new Canvas element.
func NewCanvas(ctx *gg.Context, font string, lineWidth float64) *Canvas {
	if err := ctx.LoadFontFace(font, 20); err != nil {
		panic(err)
	}
	ctx.SetLineWidth(lineWidth)
	return &Canvas{ctx, font, lineWidth}
}

var _x0, _y0 float64

// moveTo move the pointer to (x0,y0) position
func (ctx *Canvas) moveTo(x0, y0 float64) {
	_x0 = x0
	_y0 = y0
}

// lineTo move the pointer to (x1,y1) position
func (ctx *Canvas) lineTo(x1, y1 float64) {
	ctx.shakyLine(_x0, _y0, x1, y1)
	ctx.moveTo(x1, y1)
}

// shakyLine draw a shaky line between (x0, y0) and (x1, y1).
func (ctx *Canvas) shakyLine(x0, y0, x1, y1 float64) {
	var dx, dy float64
	var k1, k2, l3, l4, x3, y3, x4, y4 float64
	dx = x1 - x0
	dy = y1 - y0

	l := math.Sqrt(dx*dx + dy*dy)

	// Pick two random points that are placed on different sides of the line that passes through.
	K := math.Sqrt(l) / 1.5
	k1 = rand.Float64()
	k2 = rand.Float64()
	l3 = rand.Float64() * K
	l4 = rand.Float64() * K

	// Pick a random point on the line between P0 and P1.
	x3 = x0 + dx*k1 + dy/l*l3
	y3 = y0 + dy*k1 - dx/l*l3

	// Pick a random point on the line between P0 and P1 but in the opposite direction.
	x4 = x0 + dx*k2 - dy/l*l4
	y4 = y0 + dy*k2 + dx/l*l4

	// Draw a bezier curve trough the four selected points.
	ctx.MoveTo(x0, y0)
	ctx.CubicTo(x3, y3, x4, y4, x1, y1)
}

// bulb draws a shaky bulb (used for line endings).
func (ctx *Canvas) bulb(x0, y0 float64) {
	fuzziness := random()*2 - 1

	for i := 0; i < 3; i++ {
		ctx.DrawArc(x0+fuzziness, y0+fuzziness, 5, 0, math.Pi*2)
		ctx.ClosePath()
		ctx.Fill()
	}
}

// arrowHead draws a shaky arrowhead at the (x1, y1) as an ending
// for the line from (x0, y0) to (x1, y1).
func (ctx *Canvas) arrowHead(x0, y0, x1, y1 float64) {
	dx := x0 - x1
	dy := y0 - y1

	alpha := math.Atan(dy / dx)

	if dy == 0 {
		if dx < 0 {
			alpha = -math.Pi
		} else {
			alpha = 0
		}
	}
	alpha3 := alpha + 0.5
	alpha4 := alpha - 0.5

	l3 := float64(20.0)
	x3 := x1 + l3*math.Cos(alpha3)
	y3 := y1 + l3*math.Sin(alpha3)

	ctx.moveTo(x3, y3)
	ctx.lineTo(x1, y1)
	ctx.Stroke()

	l4 := float64(20.0)
	x4 := x1 + l4*math.Cos(alpha4)
	y4 := y1 + l4*math.Sin(alpha4)

	ctx.moveTo(x4, y4)
	ctx.lineTo(x1, y1)
	ctx.Stroke()
}

// fillText fill out the text.
func (ctx *Canvas) fillText(text string, x0, y0 float64) {
	ctx.DrawString(text, x0, y0)
}

// Draw draws the text annotation at (x0, y0) with the given color.
func (text *Text) Draw(ctx *Canvas) {
	ctx.SetHexColor(text.color)
	ctx.fillText(text.text, X(float64(text.x0)), Y(float64(text.y0)+0.5))
}

// Draw draws a line from (x0, y0) to (x1, y1) with the given color.
func (line *Line) Draw(ctx *Canvas) {
	ctx.SetHexColor(line.color)
	ctx.SetLineWidth(ctx.lineWidth)
	ctx.moveTo(X(float64(line.x0)), Y(float64(line.y0)))
	ctx.lineTo(X(float64(line.x1)), Y(float64(line.y1)))
	ctx.Stroke()

	// Draw given type of ending on the (x1, y1).
	_ending := func(ctx *Canvas, typ string, x0, y0, x1, y1 float64) {
		switch typ {
		case "circle":
			ctx.bulb(x1, y1)
			return
		case "arrow":
			ctx.arrowHead(x0, y0, x1, y1)
			return
		}
	}

	_ending(ctx, line.start, X(float64(line.x1)), Y(float64(line.y1)), X(float64(line.x0)), Y(float64(line.y0)))
	_ending(ctx, line.end, X(float64(line.x0)), Y(float64(line.y0)), X(float64(line.x1)), Y(float64(line.y1)))
}

// X returns the symbols x position.
func X(x float64) float64 {
	return x*CellSize + (CellSize / 2)
}

// Y returns the symbol y position.
func Y(y float64) float64 {
	return y*CellSize + (CellSize / 2)
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	flag "github.com/spf13/pflag"
	"github.com/tendermint/confer"
)

var app *confer.Config
var appMtx sync.Mutex

func App() *confer.Config {
	appMtx.Lock()
	defer appMtx.Unlock()
	if app == nil {
		Init("")
	}
	return app
}

func SetApp(a *confer.Config) {
	appMtx.Lock()
	defer appMtx.Unlock()
	app = a
}

// NOTE: If you change this, maybe also change initDefaults()
var defaultConfig = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

Moniker = "anonymous"
Network = "tendermint_testnet4"
ListenAddr = "0.0.0.0:46656"
# First node to connect to.  Command-line overridable.
SeedNode = ""
# Pool of seeds. Best to use these, and specify one on command line
# if needed to override
SeedNodes = ["navytoad.chaintest.net:46656", "whiteferret.chaintest.net:46656", "magentagriffin.chaintest.net:46656", "greensalamander.chaintest.net:46656", "blackshadow.chaintest.net:46656", "purpleanteater.chaintest.net:46656", "pinkpenguin.chaintest.net:46656", "polkapig.chaintest.net:46656", "128.199.230.153:8080"]

[DB]
# The only other available backend is "memdb"
Backend = "leveldb"
# Dir = "~/.tendermint/data"

[Log.Stdout]
Level = "info"

[RPC.HTTP]
# For the RPC API HTTP server.  Port required.
ListenAddr = "0.0.0.0:46657"

[Alert]
# TODO: Document options

[SMTP]
# TODO: Document options
`

var DefaultGenesis = `{
    "accounts": [
        {
            "address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            "amount": 1049800000000000
        }
    ],
    "validators": [
        {
            "pub_key": [1, "9D1ACB248A713A4DC03A5546D43D12D10060E0B081B22D5731478314243C75A5"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "006C05174D39330324F6DEA0CE8CA263FC023331A107DD6C342B0BF1711B747D"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "7AEAC3C6F053893F9E7FA44AF5024DC45A7857AFA07C4166A2B210340FF3B9A3"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "178EC6008A4364508979C70CBF100BD4BCBAA12DDE6251F5F486B4FD09014F06"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "161F61AB54194473DD018FFFB253FD3D763A92BB97B9CA731CE1E89C2B761FFE"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        }
    ]
}`

// NOTE: If you change this, maybe also change defaultConfig
func initDefaults(rootDir string) {
	app.SetDefault("Moniker", "anonymous")
	app.SetDefault("Network", "tendermint_testnet0")
	app.SetDefault("ListenAddr", "0.0.0.0:46656")
	app.SetDefault("DB.Backend", "leveldb")
	app.SetDefault("DB.Dir", rootDir+"/data")
	app.SetDefault("Log.Stdout.Level", "info")
	app.SetDefault("RPC.HTTP.ListenAddr", "0.0.0.0:46657")

	app.SetDefault("GenesisFile", rootDir+"/genesis.json")
	app.SetDefault("AddrBookFile", rootDir+"/addrbook.json")
	app.SetDefault("PrivValidatorfile", rootDir+"/priv_validator.json")

	app.SetDefault("FastSync", false)
}

func Init(rootDir string) {

	// Get rootdir
	if rootDir == "" {
		rootDir = os.Getenv("TMROOT")
	}
	if rootDir == "" {
		rootDir = os.Getenv("HOME") + "/.tendermint"
	}
	configFile := path.Join(rootDir, "config.toml")
	genesisFile := path.Join(rootDir, "genesis.json")

	// Write default config file if missing.
	checkWriteFile(configFile, defaultConfig)
	checkWriteFile(genesisFile, DefaultGenesis)

	// Initialize Config
	app = confer.NewConfig()
	initDefaults(rootDir)
	paths := []string{configFile}
	if err := app.ReadPaths(paths...); err != nil {
		log.Warn("Error reading configuration", "paths", paths, "error", err)
	}

	// Confused?
	//app.Debug()
}

// Check if a file exists; if not, ensure the directory is made and write the file
func checkWriteFile(configFile, contents string) {
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		if strings.Index(configFile, "/") != -1 {
			err := os.MkdirAll(filepath.Dir(configFile), 0700)
			if err != nil {
				fmt.Printf("Could not create directory: %v", err)
				os.Exit(1)
			}
		}
		err := ioutil.WriteFile(configFile, []byte(contents), 0600)
		if err != nil {
			fmt.Printf("Could not write config file: %v", err)
			os.Exit(1)
		}
		fmt.Printf("Config file written to %v.\n", configFile)
	}
}

func ParseFlags(args []string) {
	var flags = flag.NewFlagSet("main", flag.ExitOnError)
	var printHelp = false

	// Declare flags
	flags.BoolVar(&printHelp, "help", false, "Print this help message.")
	flags.String("listen_addr", app.GetString("ListenAddr"), "Listen address. (0.0.0.0:0 means any interface, any port)")
	flags.String("seed_node", app.GetString("SeedNode"), "Address of seed nodes")
	flags.String("rpc_http_listen_addr", app.GetString("RPC.HTTP.ListenAddr"), "RPC listen address. Port required")
	flags.Bool("fast_sync", app.GetBool("FastSync"), "Fast blockchain syncing")
	flags.String("log_stdout_level", app.GetString("Log.Stdout.Level"), "Stdout log level")
	flags.Parse(args)
	if printHelp {
		flags.PrintDefaults()
		os.Exit(0)
	}

	// Merge parsed flag values onto app.
	app.BindPFlag("ListenAddr", flags.Lookup("listen_addr"))
	app.BindPFlag("SeedNode", flags.Lookup("seed_node"))
	app.BindPFlag("FastSync", flags.Lookup("fast_sync"))
	app.BindPFlag("RPC.HTTP.ListenAddr", flags.Lookup("rpc_http_listen_addr"))
	app.BindPFlag("Log.Stdout.Level", flags.Lookup("log_stdout_level"))

	// Confused?
	//app.Debug()
}
<commit_msg>add another validator<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	flag "github.com/spf13/pflag"
	"github.com/tendermint/confer"
)

var app *confer.Config
var appMtx sync.Mutex

func App() *confer.Config {
	appMtx.Lock()
	defer appMtx.Unlock()
	if app == nil {
		Init("")
	}
	return app
}

func SetApp(a *confer.Config) {
	appMtx.Lock()
	defer appMtx.Unlock()
	app = a
}

// NOTE: If you change this, maybe also change initDefaults()
var defaultConfig = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

Moniker = "anonymous"
Network = "tendermint_testnet4"
ListenAddr = "0.0.0.0:46656"
# First node to connect to.  Command-line overridable.
SeedNode = ""
# Pool of seeds. Best to use these, and specify one on command line
# if needed to override
SeedNodes = ["navytoad.chaintest.net:46656", "whiteferret.chaintest.net:46656", "magentagriffin.chaintest.net:46656", "greensalamander.chaintest.net:46656", "blackshadow.chaintest.net:46656", "purpleanteater.chaintest.net:46656", "pinkpenguin.chaintest.net:46656", "polkapig.chaintest.net:46656", "128.199.230.153:8080"]

[DB]
# The only other available backend is "memdb"
Backend = "leveldb"
# Dir = "~/.tendermint/data"

[Log.Stdout]
Level = "info"

[RPC.HTTP]
# For the RPC API HTTP server.  Port required.
ListenAddr = "0.0.0.0:46657"

[Alert]
# TODO: Document options

[SMTP]
# TODO: Document options
`

var DefaultGenesis = `{
    "accounts": [
        {
            "address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            "amount": 1049800000000000
        },
        {
            "address": "9e54c9eca9a3fd5d4496696818da17a9e17f69da",
            "amount": 1049800000000000
        }
    ],
    "validators": [
        {
            "pub_key": [1, "9D1ACB248A713A4DC03A5546D43D12D10060E0B081B22D5731478314243C75A5"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "e56663353d01c58a1d4cdb4d14b70c2e3335be1ebb6c3f697af7882c03837962"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "9e54c9eca9a3fd5d4496696818da17a9e17f69da",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "006C05174D39330324F6DEA0CE8CA263FC023331A107DD6C342B0BF1711B747D"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "7AEAC3C6F053893F9E7FA44AF5024DC45A7857AFA07C4166A2B210340FF3B9A3"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "178EC6008A4364508979C70CBF100BD4BCBAA12DDE6251F5F486B4FD09014F06"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        },
        {
            "pub_key": [1, "161F61AB54194473DD018FFFB253FD3D763A92BB97B9CA731CE1E89C2B761FFE"],
            "amount": 100000000000,
            "unbond_to": [
            	{
            		"address": "93E243AC8A01F723DE353A4FA1ED911529CCB6E5",
            		"amount":  100000000000
            	}
            ]
        }
    ]
}`

// NOTE: If you change this, maybe also change defaultConfig
func initDefaults(rootDir string) {
	app.SetDefault("Moniker", "anonymous")
	app.SetDefault("Network", "tendermint_testnet0")
	app.SetDefault("ListenAddr", "0.0.0.0:46656")
	app.SetDefault("DB.Backend", "leveldb")
	app.SetDefault("DB.Dir", rootDir+"/data")
	app.SetDefault("Log.Stdout.Level", "info")
	app.SetDefault("RPC.HTTP.ListenAddr", "0.0.0.0:46657")

	app.SetDefault("GenesisFile", rootDir+"/genesis.json")
	app.SetDefault("AddrBookFile", rootDir+"/addrbook.json")
	app.SetDefault("PrivValidatorfile", rootDir+"/priv_validator.json")

	app.SetDefault("FastSync", false)
}

func Init(rootDir string) {

	// Get rootdir
	if rootDir == "" {
		rootDir = os.Getenv("TMROOT")
	}
	if rootDir == "" {
		rootDir = os.Getenv("HOME") + "/.tendermint"
	}
	configFile := path.Join(rootDir, "config.toml")
	genesisFile := path.Join(rootDir, "genesis.json")

	// Write default config file if missing.
	checkWriteFile(configFile, defaultConfig)
	checkWriteFile(genesisFile, DefaultGenesis)

	// Initialize Config
	app = confer.NewConfig()
	initDefaults(rootDir)
	paths := []string{configFile}
	if err := app.ReadPaths(paths...); err != nil {
		log.Warn("Error reading configuration", "paths", paths, "error", err)
	}

	// Confused?
	//app.Debug()
}

// Check if a file exists; if not, ensure the directory is made and write the file
func checkWriteFile(configFile, contents string) {
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		if strings.Index(configFile, "/") != -1 {
			err := os.MkdirAll(filepath.Dir(configFile), 0700)
			if err != nil {
				fmt.Printf("Could not create directory: %v", err)
				os.Exit(1)
			}
		}
		err := ioutil.WriteFile(configFile, []byte(contents), 0600)
		if err != nil {
			fmt.Printf("Could not write config file: %v", err)
			os.Exit(1)
		}
		fmt.Printf("Config file written to %v.\n", configFile)
	}
}

func ParseFlags(args []string) {
	var flags = flag.NewFlagSet("main", flag.ExitOnError)
	var printHelp = false

	// Declare flags
	flags.BoolVar(&printHelp, "help", false, "Print this help message.")
	flags.String("listen_addr", app.GetString("ListenAddr"), "Listen address. (0.0.0.0:0 means any interface, any port)")
	flags.String("seed_node", app.GetString("SeedNode"), "Address of seed nodes")
	flags.String("rpc_http_listen_addr", app.GetString("RPC.HTTP.ListenAddr"), "RPC listen address. Port required")
	flags.Bool("fast_sync", app.GetBool("FastSync"), "Fast blockchain syncing")
	flags.String("log_stdout_level", app.GetString("Log.Stdout.Level"), "Stdout log level")
	flags.Parse(args)
	if printHelp {
		flags.PrintDefaults()
		os.Exit(0)
	}

	// Merge parsed flag values onto app.
	app.BindPFlag("ListenAddr", flags.Lookup("listen_addr"))
	app.BindPFlag("SeedNode", flags.Lookup("seed_node"))
	app.BindPFlag("FastSync", flags.Lookup("fast_sync"))
	app.BindPFlag("RPC.HTTP.ListenAddr", flags.Lookup("rpc_http_listen_addr"))
	app.BindPFlag("Log.Stdout.Level", flags.Lookup("log_stdout_level"))

	// Confused?
	//app.Debug()
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/juju/loggo"
	"gopkg.in/alecthomas/kingpin.v1"
)

var (
	config map[string]interface{}
	log    = loggo.GetLogger("config") // avoid the wrapper as it uses this module and will cause a loop
)

func init() {
	MustRefresh()

	if Bool(false, "dumpConfig") {
		spew.Dump(GetAll(false))
	}
}

func GetAll(flatten bool) map[string]interface{} {
	if flatten {
		return config
	}
	return unflatten(config)
}

var serial string
var sphereVersion string

func Serial() string {

	if serial == "" {

		if HasString("serial") {
			serial = String("serial")
		} else {

			cmd := exec.Command("sphere-serial", os.Args[1:]...)

			var out bytes.Buffer
			cmd.Stdout = &out

			err := cmd.Run()
			if err == nil {
				serial = out.String()
			} else {

				if runtime.GOOS == "darwin" {
					serial = darwinSerial()
				} else {
					log.Errorf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
					panic(err)
				}
			}

		}
	}

	return serial
}

func darwinSerial() string {
	cmd := exec.Command("/bin/sh", "-c", "system_profiler SPHardwareDataType | sed -n 's/.*Serial Number (system).*: /OSX/p'")
	bytes, err := cmd.Output()
	if err != nil {
		log.Errorf("Failed to get darwin serial: %s ", err)
		panic("No darwin serial")
	}

	return string(bytes[0 : len(bytes)-1])
}

func SphereVersion() string {
	if sphereVersion == "" {
		if HasString("sphere-version") {

			sphereVersion = String("sphere-version")

		} else {

			cmd := exec.Command("sphere-version", os.Args[1:]...)

			var out bytes.Buffer
			cmd.Stdout = &out

			err := cmd.Run()
			if err != nil {
				log.Errorf("Failed to get sphere version (sphere-version must be in the PATH) error:%s", err)
				panic(err)
			}
			sphereVersion = out.String()
		}

	}
	return sphereVersion
}

func IsPaired() bool {
	return /*HasString("sphereNetworkKey") && */ HasString("token") && HasString("userId")
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// Duration returns the string property at the path, as a time.Duration
func Duration(def time.Duration, path ...string) time.Duration {
	s := String(hey, path...)
	if s == hey {
		return def
	}
	d, err := time.ParseDuration(s)
	if err != nil {
		log.Infof("Failed to parse duration '%s': %s", s, err)
		return def
	}
	return d
}

// MustDuration returns the string property at the path, as a time.Duration
func MustDuration(path ...string) time.Duration {
	s := MustString(path...)
	d, err := time.ParseDuration(s)
	if err != nil {
		log.Infof("Failed to parse duration '%s': %s", s, err)
	}
	return d
}

// MustStringArray returns the string array property at the path
func MustStringArray(path ...string) []string {
	a := mustGet(path...).([]interface{})
	b := make([]string, len(a))
	for i := range a {
		b[i] = a[i].(string)
	}
	return b
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Float returns the float property at the path, with a default
func Float(def float64, path ...string) float64 {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(float64)
}

func MustFloat(path ...string) float64 {
	return mustGet(path...).(float64)
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Errorf("expected value for %v but found nothing", path)
		panic(fmt.Errorf("expected value for %v but found nothing", path))
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		} else {
			if ok, boolValue := value.(bool); ok {
				if boolValue {
					environments = append(environments, name)
				}
			}
		}
	}

	log.Infof("Environments: %s", strings.Join(environments, ", "))

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	if _, err := os.Stat(installDir); err != nil {
		// check for installation in snappy, apply different default path
		snappAppPath := os.Getenv("SNAPP_APP_PATH")
		if snappAppPath != "" {
			installDir = snappAppPath
		}
	}

	flat["installDirectory"] = installDir

	if _, err := os.Stat(installDir); err != nil {
		log.Errorf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
		panic(err)
	}

	dataPath := "/data"

	// in snappy, we default to using the snappy data path
	snappDataPath := os.Getenv("SNAPP_APP_DATA_PATH")
	if snappDataPath != "" {
		dataPath = snappDataPath
	}

	// User overrides (json)
	addFile(dataPath+"/config.json", flat)

	// Add site preference overrides
	addFile(dataPath+"/etc/opt/ninja/site-preferences.json", flat)

	// credentials file
	addFile(dataPath+"/etc/opt/ninja/credentials.json", flat)

	// mesh file
	addFile(dataPath+"/etc/opt/ninja/mesh.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "config", environments[i]+".json"), flat)
	}

	// common credentials config
	addFile(filepath.Join(installDir, "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				if next.Value == "false" {
					value = false
				} else if next.Value == "true" {
					value = true
				} else {
					value = next.Value
				}
			} else {
				value = true
				// It's an environment indicator... like --cloud-production
			}

			config[name] = value
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	//spew.Dump(path, content)

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}

func IsSlave() bool {
	masterNodeId := String("", "masterNodeId")
	serial := Serial()
	return masterNodeId != "" && serial != masterNodeId
}

func IsMaster() bool {
	masterNodeId := String("", "masterNodeId")
	serial := Serial()
	return masterNodeId != "" && serial == masterNodeId
}
<commit_msg>Trim leading and trailing space from the version string.<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/juju/loggo"
	"gopkg.in/alecthomas/kingpin.v1"
)

var (
	config map[string]interface{}
	log    = loggo.GetLogger("config") // avoid the wrapper as it uses this module and will cause a loop
)

func init() {
	MustRefresh()

	if Bool(false, "dumpConfig") {
		spew.Dump(GetAll(false))
	}
}

func GetAll(flatten bool) map[string]interface{} {
	if flatten {
		return config
	}
	return unflatten(config)
}

var serial string
var sphereVersion string

func Serial() string {

	if serial == "" {

		if HasString("serial") {
			serial = String("serial")
		} else {

			cmd := exec.Command("sphere-serial", os.Args[1:]...)

			var out bytes.Buffer
			cmd.Stdout = &out

			err := cmd.Run()
			if err == nil {
				serial = out.String()
			} else {

				if runtime.GOOS == "darwin" {
					serial = darwinSerial()
				} else {
					log.Errorf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
					panic(err)
				}
			}

		}
	}

	return serial
}

func darwinSerial() string {
	cmd := exec.Command("/bin/sh", "-c", "system_profiler SPHardwareDataType | sed -n 's/.*Serial Number (system).*: /OSX/p'")
	bytes, err := cmd.Output()
	if err != nil {
		log.Errorf("Failed to get darwin serial: %s ", err)
		panic("No darwin serial")
	}

	return string(bytes[0 : len(bytes)-1])
}

func SphereVersion() string {
	if sphereVersion == "" {
		if HasString("sphere-version") {

			sphereVersion = String("sphere-version")

		} else {

			cmd := exec.Command("sphere-version", os.Args[1:]...)

			var out bytes.Buffer
			cmd.Stdout = &out

			err := cmd.Run()
			if err != nil {
				log.Errorf("Failed to get sphere version (sphere-version must be in the PATH) error:%s", err)
				panic(err)
			}
			sphereVersion = strings.TrimSpace(out.String())
		}

	}
	return sphereVersion
}

func IsPaired() bool {
	return /*HasString("sphereNetworkKey") && */ HasString("token") && HasString("userId")
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// Duration returns the string property at the path, as a time.Duration
func Duration(def time.Duration, path ...string) time.Duration {
	s := String(hey, path...)
	if s == hey {
		return def
	}
	d, err := time.ParseDuration(s)
	if err != nil {
		log.Infof("Failed to parse duration '%s': %s", s, err)
		return def
	}
	return d
}

// MustDuration returns the string property at the path, as a time.Duration
func MustDuration(path ...string) time.Duration {
	s := MustString(path...)
	d, err := time.ParseDuration(s)
	if err != nil {
		log.Infof("Failed to parse duration '%s': %s", s, err)
	}
	return d
}

// MustStringArray returns the string array property at the path
func MustStringArray(path ...string) []string {
	a := mustGet(path...).([]interface{})
	b := make([]string, len(a))
	for i := range a {
		b[i] = a[i].(string)
	}
	return b
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Float returns the float property at the path, with a default
func Float(def float64, path ...string) float64 {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(float64)
}

func MustFloat(path ...string) float64 {
	return mustGet(path...).(float64)
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Errorf("expected value for %v but found nothing", path)
		panic(fmt.Errorf("expected value for %v but found nothing", path))
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		} else {
			if ok, boolValue := value.(bool); ok {
				if boolValue {
					environments = append(environments, name)
				}
			}
		}
	}

	log.Infof("Environments: %s", strings.Join(environments, ", "))

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	if _, err := os.Stat(installDir); err != nil {
		// check for installation in snappy, apply different default path
		snappAppPath := os.Getenv("SNAPP_APP_PATH")
		if snappAppPath != "" {
			installDir = snappAppPath
		}
	}

	flat["installDirectory"] = installDir

	if _, err := os.Stat(installDir); err != nil {
		log.Errorf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
		panic(err)
	}

	dataPath := "/data"

	// in snappy, we default to using the snappy data path
	snappDataPath := os.Getenv("SNAPP_APP_DATA_PATH")
	if snappDataPath != "" {
		dataPath = snappDataPath
	}

	// User overrides (json)
	addFile(dataPath+"/config.json", flat)

	// Add site preference overrides
	addFile(dataPath+"/etc/opt/ninja/site-preferences.json", flat)

	// credentials file
	addFile(dataPath+"/etc/opt/ninja/credentials.json", flat)

	// mesh file
	addFile(dataPath+"/etc/opt/ninja/mesh.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "config", environments[i]+".json"), flat)
	}

	// common credentials config
	addFile(filepath.Join(installDir, "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				if next.Value == "false" {
					value = false
				} else if next.Value == "true" {
					value = true
				} else {
					value = next.Value
				}
			} else {
				value = true
				// It's an environment indicator... like --cloud-production
			}

			config[name] = value
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	//spew.Dump(path, content)

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}

func IsSlave() bool {
	masterNodeId := String("", "masterNodeId")
	serial := Serial()
	return masterNodeId != "" && serial != masterNodeId
}

func IsMaster() bool {
	masterNodeId := String("", "masterNodeId")
	serial := Serial()
	return masterNodeId != "" && serial == masterNodeId
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"os"
	"path"
	"time"

	"github.com/huacnlee/gobackup/logger"
	"github.com/spf13/viper"
)

var (
	// Exist Is config file exist
	Exist bool
	// Models configs
	Models []ModelConfig
	// HomeDir of user
	HomeDir = os.Getenv("HOME")
)

// ModelConfig for special case
type ModelConfig struct {
	Name         string
	TempPath     string
	DumpPath     string
	CompressWith SubConfig
	EncryptWith  SubConfig
	StoreWith    SubConfig
	Archive      *viper.Viper
	Databases    []SubConfig
	Storages     []SubConfig
	Viper        *viper.Viper
}

// SubConfig sub config info
type SubConfig struct {
	Name  string
	Type  string
	Viper *viper.Viper
}

// loadConfig from:
// - ./gobackup.yml
// - ~/.gobackup/gobackup.yml
// - /etc/gobackup/gobackup.yml
func Init(configFile string) {
	viper.SetConfigType("yaml")

	// set config file directly
	if len(configFile) > 0 {
		viper.SetConfigFile(configFile)
	} else {
		viper.SetConfigName("gobackup")

		// ./gobackup.yml
		viper.AddConfigPath(".")
		// ~/.gobackup/gobackup.yml
		viper.AddConfigPath("$HOME/.gobackup") // call multiple times to add many search paths
		// /etc/gobackup/gobackup.yml
		viper.AddConfigPath("/etc/gobackup/") // path to look for the config file in
	}

	err := viper.ReadInConfig()
	if err != nil {
		logger.Error("Load gobackup config faild", err)
		return
	}

	Exist = true
	Models = []ModelConfig{}
	for key := range viper.GetStringMap("models") {
		Models = append(Models, loadModel(key))
	}
}

func loadModel(key string) (model ModelConfig) {
	model.Name = key
	model.TempPath = path.Join(os.TempDir(), "gobackup", fmt.Sprintf("%d", time.Now().UnixNano()))
	model.DumpPath = path.Join(model.TempPath, key)
	model.Viper = viper.Sub("models." + key)

	model.CompressWith = SubConfig{
		Type:  model.Viper.GetString("compress_with.type"),
		Viper: model.Viper.Sub("compress_with"),
	}

	model.EncryptWith = SubConfig{
		Type:  model.Viper.GetString("encrypt_with.type"),
		Viper: model.Viper.Sub("encrypt_with"),
	}

	model.StoreWith = SubConfig{
		Type:  model.Viper.GetString("store_with.type"),
		Viper: model.Viper.Sub("store_with"),
	}

	model.Archive = model.Viper.Sub("archive")

	loadDatabasesConfig(&model)
	loadStoragesConfig(&model)

	return
}

func loadDatabasesConfig(model *ModelConfig) {
	subViper := model.Viper.Sub("databases")
	for key := range model.Viper.GetStringMap("databases") {
		dbViper := subViper.Sub(key)
		model.Databases = append(model.Databases, SubConfig{
			Name:  key,
			Type:  dbViper.GetString("type"),
			Viper: dbViper,
		})
	}
}

func loadStoragesConfig(model *ModelConfig) {
	subViper := model.Viper.Sub("storages")
	for key := range model.Viper.GetStringMap("storages") {
		dbViper := subViper.Sub(key)
		model.Storages = append(model.Storages, SubConfig{
			Name:  key,
			Type:  dbViper.GetString("type"),
			Viper: dbViper,
		})
	}
}

// GetModelByName get model by name
func GetModelByName(name string) (model *ModelConfig) {
	for _, m := range Models {
		if m.Name == name {
			model = &m
			return
		}
	}
	return
}

// GetDatabaseByName get database config by name
func (model *ModelConfig) GetDatabaseByName(name string) (subConfig *SubConfig) {
	for _, m := range model.Databases {
		if m.Name == name {
			subConfig = &m
			return
		}
	}
	return
}
<commit_msg>[added] added config key 'workdir' to specify a working base directory<commit_after>package config

import (
	"fmt"
	"os"
	"path"
	"time"

	"github.com/huacnlee/gobackup/logger"
	"github.com/spf13/viper"
)

var (
	// Exist Is config file exist
	Exist bool
	// Models configs
	Models []ModelConfig
	// HomeDir of user
	HomeDir = os.Getenv("HOME")
)

// ModelConfig for special case
type ModelConfig struct {
	Name         string
	TempPath     string
	DumpPath     string
	CompressWith SubConfig
	EncryptWith  SubConfig
	StoreWith    SubConfig
	Archive      *viper.Viper
	Databases    []SubConfig
	Storages     []SubConfig
	Viper        *viper.Viper
}

// SubConfig sub config info
type SubConfig struct {
	Name  string
	Type  string
	Viper *viper.Viper
}

// loadConfig from:
// - ./gobackup.yml
// - ~/.gobackup/gobackup.yml
// - /etc/gobackup/gobackup.yml
func Init(configFile string) {
	viper.SetConfigType("yaml")

	// set config file directly
	if len(configFile) > 0 {
		viper.SetConfigFile(configFile)
	} else {
		viper.SetConfigName("gobackup")

		// ./gobackup.yml
		viper.AddConfigPath(".")
		// ~/.gobackup/gobackup.yml
		viper.AddConfigPath("$HOME/.gobackup") // call multiple times to add many search paths
		// /etc/gobackup/gobackup.yml
		viper.AddConfigPath("/etc/gobackup/") // path to look for the config file in
	}

	err := viper.ReadInConfig()
	if err != nil {
		logger.Error("Load gobackup config faild", err)
		return
	}

	viper.SetDefault("workdir", path.Join(os.TempDir(), "gobackup"))

	Exist = true
	Models = []ModelConfig{}
	for key := range viper.GetStringMap("models") {
		Models = append(Models, loadModel(key))
	}
}

func loadModel(key string) (model ModelConfig) {
	model.Name = key
	model.TempPath = path.Join(viper.GetString("workdir"), fmt.Sprintf("%d", time.Now().UnixNano()))
	model.DumpPath = path.Join(model.TempPath, key)
	model.Viper = viper.Sub("models." + key)

	model.CompressWith = SubConfig{
		Type:  model.Viper.GetString("compress_with.type"),
		Viper: model.Viper.Sub("compress_with"),
	}

	model.EncryptWith = SubConfig{
		Type:  model.Viper.GetString("encrypt_with.type"),
		Viper: model.Viper.Sub("encrypt_with"),
	}

	model.StoreWith = SubConfig{
		Type:  model.Viper.GetString("store_with.type"),
		Viper: model.Viper.Sub("store_with"),
	}

	model.Archive = model.Viper.Sub("archive")

	loadDatabasesConfig(&model)
	loadStoragesConfig(&model)

	return
}

func loadDatabasesConfig(model *ModelConfig) {
	subViper := model.Viper.Sub("databases")
	for key := range model.Viper.GetStringMap("databases") {
		dbViper := subViper.Sub(key)
		model.Databases = append(model.Databases, SubConfig{
			Name:  key,
			Type:  dbViper.GetString("type"),
			Viper: dbViper,
		})
	}
}

func loadStoragesConfig(model *ModelConfig) {
	subViper := model.Viper.Sub("storages")
	for key := range model.Viper.GetStringMap("storages") {
		dbViper := subViper.Sub(key)
		model.Storages = append(model.Storages, SubConfig{
			Name:  key,
			Type:  dbViper.GetString("type"),
			Viper: dbViper,
		})
	}
}

// GetModelByName get model by name
func GetModelByName(name string) (model *ModelConfig) {
	for _, m := range Models {
		if m.Name == name {
			model = &m
			return
		}
	}
	return
}

// GetDatabaseByName get database config by name
func (model *ModelConfig) GetDatabaseByName(name string) (subConfig *SubConfig) {
	for _, m := range model.Databases {
		if m.Name == name {
			subConfig = &m
			return
		}
	}
	return
}
<|endoftext|>