<commit_before>// Copyright 2015 The oct Authors
//
// 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 main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"errors"
	"io"
	"io/ioutil"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"github.com/codegangsta/cli"
        "github.com/opencontainers/specs"

)

const (
	// Path to config file inside the layout
	ConfigFile = "config.json"
	// Path to rootfs directory inside the layout
	RootfsDir = "rootfs"
)

var (
	ErrNoRootFS   = errors.New("no rootfs found in layout")
	ErrNoConfig = errors.New("no config json file found in layout")
)

func validate(context *cli.Context) {
    args := context.String("config")

    if len(args) == 0 {
        args = context.String("layout")
        if len(args) == 0 {
            cli.ShowCommandHelp(context, "validate")
            return
        } else {
           err := validateLayout(args)
           if err != nil {
				fmt.Printf("%s: invalid image layout: %v\n", args, err)
			} else {
				fmt.Printf("%s: valid image layout\n", args)
			}
        }
    } else {
               validateConfigFile(args)
    }

}

func validateLayout(path string) error {
        fi, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("error accessing layout: %v", err)
	}
	if !fi.IsDir() {
		return fmt.Errorf("given path %q is not a directory", path)
	}
	var flist []string
	var imOK, rfsOK bool
	var im io.Reader
	walkLayout := func(fpath string, fi os.FileInfo, err error) error {
		rpath, err := filepath.Rel(path, fpath)
		if err != nil {
			return err
		}
		switch rpath {
		case ".":
		case ConfigFile:
			im, err = os.Open(fpath)
			if err != nil {
				return err
			}
			imOK = true
		case RootfsDir:
			if !fi.IsDir() {
				return errors.New("rootfs is not a directory")
			}
			rfsOK = true
		default:
			flist = append(flist, rpath)
		}
		return nil
	}
	if err := filepath.Walk(path, walkLayout); err != nil {
		return err
	}
	return checkLayout(imOK, im, rfsOK, flist)
}

func checkLayout(imOK bool, im io.Reader, rfsOK bool, files []string) error {
	defer func() {
		if rc, ok := im.(io.Closer); ok {
			rc.Close()
		}
	}()
	if !imOK {
		return ErrNoConfig
	}
	if !rfsOK {
		return ErrNoRootFS
	}
	_, err := ioutil.ReadAll(im)
	if err != nil {
		return fmt.Errorf("error reading the layout: %v", err)
	}

	for _, f := range files {
		if !strings.HasPrefix(f, "rootfs") {
			return fmt.Errorf("unrecognized file path in layout: %q", f)
		}
	}
	return nil
}

func validateConfigFile(path string) {
	var sp specs.Spec
	content, err := ReadFile(path)
	if err != nil {
		return
	}
	json.Unmarshal([]byte(content), &sp)
	var secret interface{} = sp
	value := reflect.ValueOf(secret)

	var err_msg []string
	ok, err_msg := validateStruct(value, reflect.TypeOf(secret).Name(), err_msg)

	if ok == false {
		fmt.Println("The configuration is incomplete, see the details: \n")
		for index := 0; index < len(err_msg); index++ {
			fmt.Println(err_msg[index])
		}
	} else {
		fmt.Println("The configuration is Good")

	}

}

func ReadFile(file_url string) (content string, err error) {
	_, err = os.Stat(file_url)
	if err != nil {
		fmt.Println("cannot find the file ", file_url)
		return content, err
	}
	file, err := os.Open(file_url)
	defer file.Close()
	if err != nil {
		fmt.Println("cannot open the file ", file_url)
		return content, err
	}
	buf := bytes.NewBufferString("")
	buf.ReadFrom(file)
	content = buf.String()

	return content, nil
}

func checkSemVer(version string) (ret bool, msg string) {
	ret = true
	str := strings.Split(version, ".")
	if len(str) != 3 {
		ret = false
	} else {
		for index := 0; index < len(str); index++ {
			i, err := strconv.Atoi(str[index])
			if err != nil {
				ret = false
				break
			} else {
				if i < 0 {
					ret = false
					break
				}
			}
		}
	}
	if ret == false {
		msg = fmt.Sprintf("%s is not a valid version format, please read 'SemVer v2.0.0'", version)
	}
	return ret, msg
}

func checkUnit(field reflect.Value, check string, parent string, err_msg []string) (bool, []string) {
	kind := field.Kind().String()
	switch kind {
	case "string":
		if check == "SemVer v2.0.0" {
			ok, msg := checkSemVer(field.String())
			if ok == false {
				err_msg = append(err_msg, fmt.Sprintf("%s : %s", parent, msg))
				return false, err_msg
			}
		}
		break
	default:
		break
	}
	return true, err_msg
}

func validateUnit(field reflect.Value, t_field reflect.StructField, parent string, err_msg []string) (bool, []string) {
	var mandatory bool
	if t_field.Tag.Get("mandatory") == "required" {
		mandatory = true
	} else {
		mandatory = false
	}

	kind := field.Kind().String()
	switch kind {
	case "string":
		if mandatory && (field.Len() == 0) {
			err_msg = append(err_msg, fmt.Sprintf("%s.%s is incomplete", parent, t_field.Name))
			return false, err_msg
		}
		break
	case "struct":
		if mandatory {
			return validateStruct(field, parent+"."+t_field.Name, err_msg)
		}
		break
	case "slice":
		if mandatory && (field.Len() == 0) {
			err_msg = append(err_msg, fmt.Sprintf("%s.%s is incomplete", parent, t_field.Name))
			return false, err_msg
		}
		valid := true
		for f_index := 0; f_index < field.Len(); f_index++ {
			if field.Index(f_index).Kind().String() == "struct" {
				var ok bool
				ok, err_msg = validateStruct(field.Index(f_index), parent+"."+t_field.Name, err_msg)
				if ok == false {
					valid = false
				}
			}
		}
		return valid, err_msg
		break
	case "int32":
		break
	default:
		break

	}

	check := t_field.Tag.Get("check")
	if len(check) > 0 {
		return checkUnit(field, check, parent+"."+t_field.Name, err_msg)
	}

	return true, err_msg
}

func validateStruct(value reflect.Value, parent string, err_msg []string) (bool, []string) {
	if value.Kind().String() != "struct" {
		fmt.Println("Program issue!")
		return true, err_msg
	}
	rtype := value.Type()
	valid := true
	for i := 0; i < value.NumField(); i++ {
		var ok bool
		field := value.Field(i)
		t_field := rtype.Field(i)
		ok, err_msg = validateUnit(field, t_field, parent, err_msg)
		if ok == false {
			valid = false
		}
	}
	if valid == false {
		err_msg = append(err_msg, fmt.Sprintf("%s is incomplete", parent))
	}
	return valid, err_msg
}
<commit_msg>Add runtime.json check<commit_after>// Copyright 2015 The oct Authors
//
// 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 main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"errors"
	"io"
	"io/ioutil"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"github.com/codegangsta/cli"
        "github.com/opencontainers/specs"

)

const (
	// Path to config file inside the layout
	ConfigFile = "config.json"
	RuntimeFile = "runtime.json"
	// Path to rootfs directory inside the layout
	RootfsDir = "rootfs"
)

var (
	ErrNoRootFS   = errors.New("no rootfs found in layout")
	ErrNoConfig = errors.New("no config json file found in layout")
	ErrNoRun = errors.New("no runtime json file found in layout")
)

func validate(context *cli.Context) {
    args := context.String("config")

    if len(args) == 0 {
        args = context.String("layout")
        if len(args) == 0 {
            cli.ShowCommandHelp(context, "validate")
            return
        } else {
           err := validateLayout(args)
           if err != nil {
				fmt.Printf("%s: invalid image layout: %v\n", args, err)
			} else {
				fmt.Printf("%s: valid image layout\n", args)
			}
        }
    } else {
               validateConfigFile(args)
    }

}

func validateLayout(path string) error {
        fi, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("error accessing layout: %v", err)
	}
	if !fi.IsDir() {
		return fmt.Errorf("given path %q is not a directory", path)
	}
	var flist []string
	var cfgOK, runOK, rfsOK bool
	var config, runtime io.Reader
	walkLayout := func(fpath string, fi os.FileInfo, err error) error {
		rpath, err := filepath.Rel(path, fpath)
		if err != nil {
			return err
		}
		switch rpath {
		case ".":
		case ConfigFile:
			config, err = os.Open(fpath)
			if err != nil {
				return err
			}
			cfgOK = true
		case RuntimeFile:
			runtime, err = os.Open(fpath)
			if err != nil {
				return err
			}
			runOK = true
		case RootfsDir:
			if !fi.IsDir() {
				return errors.New("rootfs is not a directory")
			}
			rfsOK = true
		default:
			flist = append(flist, rpath)
		}
		return nil
	}
	if err := filepath.Walk(path, walkLayout); err != nil {
		return err
	}
	return checkLayout(cfgOK, config, runOK, runtime, rfsOK, flist)
}

func checkLayout(cfgOK bool, config io.Reader, runOK bool, runtime io.Reader, rfsOK bool, files []string) error {
	defer func() {
		if rc, ok := config.(io.Closer); ok {
			rc.Close()
		}
		if rc, ok := runtime.(io.Closer); ok {
			rc.Close()
		}
	}()
	if !cfgOK {
		return ErrNoConfig
	}
	if !runOK {
		return ErrNoRun
	}
	if !rfsOK {
		return ErrNoRootFS
	}
	_, err := ioutil.ReadAll(config)
	if err != nil {
		return fmt.Errorf("error reading the layout: %v", err)
	}
	_, err = ioutil.ReadAll(runtime)
        if err != nil {
                return fmt.Errorf("error reading the layout: %v", err)
        }

	for _, f := range files {
		if !strings.HasPrefix(f, "rootfs") {
			return fmt.Errorf("unrecognized file path in layout: %q", f)
		}
	}
	return nil
}

func validateConfigFile(path string) {
	var sp specs.Spec
	content, err := ReadFile(path)
	if err != nil {
		return
	}
	json.Unmarshal([]byte(content), &sp)
	var secret interface{} = sp
	value := reflect.ValueOf(secret)

	var err_msg []string
	ok, err_msg := validateStruct(value, reflect.TypeOf(secret).Name(), err_msg)

	if ok == false {
		fmt.Println("The configuration is incomplete, see the details: \n")
		for index := 0; index < len(err_msg); index++ {
			fmt.Println(err_msg[index])
		}
	} else {
		fmt.Println("The configuration is Good")

	}

}

func ReadFile(file_url string) (content string, err error) {
	_, err = os.Stat(file_url)
	if err != nil {
		fmt.Println("cannot find the file ", file_url)
		return content, err
	}
	file, err := os.Open(file_url)
	defer file.Close()
	if err != nil {
		fmt.Println("cannot open the file ", file_url)
		return content, err
	}
	buf := bytes.NewBufferString("")
	buf.ReadFrom(file)
	content = buf.String()

	return content, nil
}

func checkSemVer(version string) (ret bool, msg string) {
	ret = true
	str := strings.Split(version, ".")
	if len(str) != 3 {
		ret = false
	} else {
		for index := 0; index < len(str); index++ {
			i, err := strconv.Atoi(str[index])
			if err != nil {
				ret = false
				break
			} else {
				if i < 0 {
					ret = false
					break
				}
			}
		}
	}
	if ret == false {
		msg = fmt.Sprintf("%s is not a valid version format, please read 'SemVer v2.0.0'", version)
	}
	return ret, msg
}

func checkUnit(field reflect.Value, check string, parent string, err_msg []string) (bool, []string) {
	kind := field.Kind().String()
	switch kind {
	case "string":
		if check == "SemVer v2.0.0" {
			ok, msg := checkSemVer(field.String())
			if ok == false {
				err_msg = append(err_msg, fmt.Sprintf("%s : %s", parent, msg))
				return false, err_msg
			}
		}
		break
	default:
		break
	}
	return true, err_msg
}

func validateUnit(field reflect.Value, t_field reflect.StructField, parent string, err_msg []string) (bool, []string) {
	var mandatory bool
	if t_field.Tag.Get("mandatory") == "required" {
		mandatory = true
	} else {
		mandatory = false
	}

	kind := field.Kind().String()
	switch kind {
	case "string":
		if mandatory && (field.Len() == 0) {
			err_msg = append(err_msg, fmt.Sprintf("%s.%s is incomplete", parent, t_field.Name))
			return false, err_msg
		}
		break
	case "struct":
		if mandatory {
			return validateStruct(field, parent+"."+t_field.Name, err_msg)
		}
		break
	case "slice":
		if mandatory && (field.Len() == 0) {
			err_msg = append(err_msg, fmt.Sprintf("%s.%s is incomplete", parent, t_field.Name))
			return false, err_msg
		}
		valid := true
		for f_index := 0; f_index < field.Len(); f_index++ {
			if field.Index(f_index).Kind().String() == "struct" {
				var ok bool
				ok, err_msg = validateStruct(field.Index(f_index), parent+"."+t_field.Name, err_msg)
				if ok == false {
					valid = false
				}
			}
		}
		return valid, err_msg
		break
	case "int32":
		break
	default:
		break

	}

	check := t_field.Tag.Get("check")
	if len(check) > 0 {
		return checkUnit(field, check, parent+"."+t_field.Name, err_msg)
	}

	return true, err_msg
}

func validateStruct(value reflect.Value, parent string, err_msg []string) (bool, []string) {
	if value.Kind().String() != "struct" {
		fmt.Println("Program issue!")
		return true, err_msg
	}
	rtype := value.Type()
	valid := true
	for i := 0; i < value.NumField(); i++ {
		var ok bool
		field := value.Field(i)
		t_field := rtype.Field(i)
		ok, err_msg = validateUnit(field, t_field, parent, err_msg)
		if ok == false {
			valid = false
		}
	}
	if valid == false {
		err_msg = append(err_msg, fmt.Sprintf("%s is incomplete", parent))
	}
	return valid, err_msg
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestHostIFrameGraph(t *testing.T) {
	h := &hostGraph{
		"hostid",
		"iframe",
		"loadavg5",
		"30m",
		200,
		600,
	}

	actual := h.generateGraphString("orgname")
	expected := `<iframe src="https://mackerel.io/embed/orgs/orgname/hosts/hostid?graph=loadavg5&period=30m" height="200" width="600" frameborder="0"></iframe>`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestServiceIFrameGraph(t *testing.T) {
	r := &serviceGraph{
		"hoge",
		"iframe",
		"cpu",
		"6h",
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `<iframe src="https://mackerel.io/embed/orgs/orgname/services/hoge?graph=cpu&period=6h" height="200" width="600" frameborder="0"></iframe>`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestRoleIFrameGraph(t *testing.T) {
	r := &roleGraph{
		"hoge",
		"api",
		"iframe",
		"custom.fuga.*",
		"6h",
		true,
		true,
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `<iframe src="https://mackerel.io/embed/orgs/orgname/services/hoge/api?graph=custom.fuga.%2A&period=6h&simplified=true&stacked=true" height="200" width="600" frameborder="0"></iframe>`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestExpressionIFrameGraph(t *testing.T) {
	e := &expressionGraph{
		"max(roleSlots('hoge:api','loadavg5'))",
		"iframe",
		"test",
		"6h",
		200,
		600,
	}

	actual := e.generateGraphString("orgname")
	expected := ``

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestHostImageGraph(t *testing.T) {
	h := &hostGraph{
		"hostid",
		"image",
		"loadavg5",
		"30m",
		200,
		600,
	}

	actual := h.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/hosts/hostid.png?graph=loadavg5&period=30m)](https://mackerel.io/orgs/orgname/hosts/hostid/-/graphs/loadavg5)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestServiceImageGraph(t *testing.T) {
	r := &serviceGraph{
		"hoge",
		"image",
		"custom.fuga.*",
		"6h",
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/services/hoge.png?graph=custom.fuga.%2A&period=6h)](https://mackerel.io/orgs/orgname/services/hoge/-/graphs?name=custom.fuga.%2A)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestRoleImageGraph(t *testing.T) {
	r := &roleGraph{
		"hoge",
		"api",
		"image",
		"cpu",
		"6h",
		true,
		true,
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/services/hoge/api.png?graph=cpu&period=6h&simplified=true&stacked=true)](https://mackerel.io/orgs/orgname/services/hoge/api/-/graph?name=cpu)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestExpressionImageGraph(t *testing.T) {
	e := &expressionGraph{
		"max(roleSlots('hoge:api','loadavg5'))",
		"image",
		"test",
		"6h",
		200,
		600,
	}

	actual := e.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/advanced-graph.png?period=6h&query=max%28roleSlots%28%27hoge%3Aapi%27%2C%27loadavg5%27%29%29&title=test)](https://mackerel.io/orgs/orgname/advanced-graph?query=max%28roleSlots%28%27hoge%3Aapi%27%2C%27loadavg5%27%29%29&title=test)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestGenerateMarkDown(t *testing.T) {
	defs := []*graphDef{
		{
			ServiceName: "hoge",
			RoleName:    "api",
			GraphName:   "cpu",
		},
		{
			HostID:    "abcde",
			GraphName: "cpu",
		},
		{
			Query:     "max(roleSlots('hoge:api','loadavg5'))",
			GraphName: "cpu",
		},
	}
	g := &graphFormat{
		Headline:    "headline",
		ColumnCount: 2,
		GraphDefs:   defs,
	}

	md, _ := generateGraphsMarkdownFactory(g, "iframe", 200, 400)
	actual := md.generate("orgname")
	expected := "## headline\n" +
		"|:-:|:-:|\n" +
		`|||` + "\n" +
		`||` + "\n"

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}
<commit_msg>Change test of expression graph<commit_after>package main

import (
	"testing"
)

func TestHostIFrameGraph(t *testing.T) {
	h := &hostGraph{
		"hostid",
		"iframe",
		"loadavg5",
		"30m",
		200,
		600,
	}

	actual := h.generateGraphString("orgname")
	expected := `<iframe src="https://mackerel.io/embed/orgs/orgname/hosts/hostid?graph=loadavg5&period=30m" height="200" width="600" frameborder="0"></iframe>`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestServiceIFrameGraph(t *testing.T) {
	r := &serviceGraph{
		"hoge",
		"iframe",
		"cpu",
		"6h",
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `<iframe src="https://mackerel.io/embed/orgs/orgname/services/hoge?graph=cpu&period=6h" height="200" width="600" frameborder="0"></iframe>`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestRoleIFrameGraph(t *testing.T) {
	r := &roleGraph{
		"hoge",
		"api",
		"iframe",
		"custom.fuga.*",
		"6h",
		true,
		true,
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `<iframe src="https://mackerel.io/embed/orgs/orgname/services/hoge/api?graph=custom.fuga.%2A&period=6h&simplified=true&stacked=true" height="200" width="600" frameborder="0"></iframe>`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestExpressionIFrameGraph(t *testing.T) {
	e := &expressionGraph{
		"max(roleSlots('hoge:api','loadavg5'))",
		"iframe",
		"[test graph]hoge:api & loadavg5",
		"6h",
		200,
		600,
	}

	actual := e.generateGraphString("orgname")
	expected := ``

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestHostImageGraph(t *testing.T) {
	h := &hostGraph{
		"hostid",
		"image",
		"loadavg5",
		"30m",
		200,
		600,
	}

	actual := h.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/hosts/hostid.png?graph=loadavg5&period=30m)](https://mackerel.io/orgs/orgname/hosts/hostid/-/graphs/loadavg5)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestServiceImageGraph(t *testing.T) {
	r := &serviceGraph{
		"hoge",
		"image",
		"custom.fuga.*",
		"6h",
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/services/hoge.png?graph=custom.fuga.%2A&period=6h)](https://mackerel.io/orgs/orgname/services/hoge/-/graphs?name=custom.fuga.%2A)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestRoleImageGraph(t *testing.T) {
	r := &roleGraph{
		"hoge",
		"api",
		"image",
		"cpu",
		"6h",
		true,
		true,
		200,
		600,
	}

	actual := r.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/services/hoge/api.png?graph=cpu&period=6h&simplified=true&stacked=true)](https://mackerel.io/orgs/orgname/services/hoge/api/-/graph?name=cpu)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestExpressionImageGraph(t *testing.T) {
	e := &expressionGraph{
		"max(roleSlots('hoge:api','loadavg5'))",
		"image",
		"[test graph]hoge:api & loadavg5",
		"6h",
		200,
		600,
	}

	actual := e.generateGraphString("orgname")
	expected := `[![graph](https://mackerel.io/embed/orgs/orgname/advanced-graph.png?period=6h&query=max%28roleSlots%28%27hoge%3Aapi%27%2C%27loadavg5%27%29%29&title=%5Btest+graph%5Dhoge%3Aapi+%26+loadavg5)](https://mackerel.io/orgs/orgname/advanced-graph?query=max%28roleSlots%28%27hoge%3Aapi%27%2C%27loadavg5%27%29%29&title=%5Btest+graph%5Dhoge%3Aapi+%26+loadavg5)`

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}

func TestGenerateMarkDown(t *testing.T) {
	defs := []*graphDef{
		{
			ServiceName: "hoge",
			RoleName:    "api",
			GraphName:   "cpu",
		},
		{
			HostID:    "abcde",
			GraphName: "cpu",
		},
		{
			Query:     "max(roleSlots('hoge:api','loadavg5'))",
			GraphName: "cpu",
		},
	}
	g := &graphFormat{
		Headline:    "headline",
		ColumnCount: 2,
		GraphDefs:   defs,
	}

	md, _ := generateGraphsMarkdownFactory(g, "iframe", 200, 400)
	actual := md.generate("orgname")
	expected := "## headline\n" +
		"|:-:|:-:|\n" +
		`|||` + "\n" +
		`||` + "\n"

	if actual != expected {
		t.Errorf("output should be:\n%s\nbut:\n%s", expected, actual)
	}
}
<|endoftext|>


<commit_before>package consul

import (
	"crypto/tls"
	"fmt"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/serf"
	"log"
	"math/rand"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	// clientRPCCache controls how long we keep an idle connection
	// open to a server
	clientRPCCache = 30 * time.Second

	// clientMaxStreams controsl how many idle streams we keep
	// open to a server
	clientMaxStreams = 32
)

// Interface is used to provide either a Client or Server,
// both of which can be used to perform certain common
// Consul methods
type Interface interface {
	RPC(method string, args interface{}, reply interface{}) error
	LANMembers() []serf.Member
}

// Client is Consul client which uses RPC to communicate with the
// services for service discovery, health checking, and DC forwarding.
type Client struct {
	config *Config

	// Connection pool to consul servers
	connPool *ConnPool

	// consuls tracks the locally known servers
	consuls    []*serverParts
	consulLock sync.RWMutex

	// eventCh is used to receive events from the
	// serf cluster in the datacenter
	eventCh chan serf.Event

	// lastServer is the last server we made an RPC call to,
	// this is used to re-use the last connection
	lastServer  *serverParts
	lastRPCTime time.Time

	// Logger uses the provided LogOutput
	logger *log.Logger

	// serf is the Serf cluster maintained inside the DC
	// which contains all the DC nodes
	serf *serf.Serf

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewClient is used to construct a new Consul client from the
// configuration, potentially returning an error
func NewClient(config *Config) (*Client, error) {
	// Check the protocol version
	if err := config.CheckVersion(); err != nil {
		return nil, err
	}

	// Check for a data directory!
	if config.DataDir == "" {
		return nil, fmt.Errorf("Config must provide a DataDir")
	}

	// Ensure we have a log output
	if config.LogOutput == nil {
		config.LogOutput = os.Stderr
	}

	// Create the tlsConfig
	var tlsConfig *tls.Config
	var err error
	if config.VerifyOutgoing {
		if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
			return nil, err
		}
	}

	// Create a logger
	logger := log.New(config.LogOutput, "", log.LstdFlags)

	// Create server
	c := &Client{
		config:     config,
		connPool:   NewPool(clientRPCCache, clientMaxStreams, tlsConfig),
		eventCh:    make(chan serf.Event, 256),
		logger:     logger,
		shutdownCh: make(chan struct{}),
	}

	// Start the Serf listeners to prevent a deadlock
	go c.lanEventHandler()

	// Initialize the lan Serf
	c.serf, err = c.setupSerf(config.SerfLANConfig,
		c.eventCh, serfLANSnapshot)
	if err != nil {
		c.Shutdown()
		return nil, fmt.Errorf("Failed to start lan serf: %v", err)
	}
	return c, nil
}

// setupSerf is used to setup and initialize a Serf
func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (*serf.Serf, error) {
	conf.Init()
	conf.NodeName = c.config.NodeName
	conf.Tags["role"] = "node"
	conf.Tags["dc"] = c.config.Datacenter
	conf.Tags["vsn"] = fmt.Sprintf("%d", c.config.ProtocolVersion)
	conf.Tags["vsn_min"] = fmt.Sprintf("%d", ProtocolVersionMin)
	conf.Tags["vsn_max"] = fmt.Sprintf("%d", ProtocolVersionMax)
	conf.MemberlistConfig.LogOutput = c.config.LogOutput
	conf.LogOutput = c.config.LogOutput
	conf.EventCh = ch
	conf.SnapshotPath = filepath.Join(c.config.DataDir, path)
	conf.ProtocolVersion = protocolVersionMap[c.config.ProtocolVersion]
	conf.RejoinAfterLeave = c.config.RejoinAfterLeave
	if err := ensurePath(conf.SnapshotPath, false); err != nil {
		return nil, err
	}
	return serf.Create(conf)
}

// Shutdown is used to shutdown the client
func (c *Client) Shutdown() error {
	c.logger.Printf("[INFO] consul: shutting down client")
	c.shutdownLock.Lock()
	defer c.shutdownLock.Unlock()

	if c.shutdown {
		return nil
	}

	c.shutdown = true
	close(c.shutdownCh)

	if c.serf != nil {
		c.serf.Shutdown()
	}

	// Close the connection pool
	c.connPool.Shutdown()
	return nil
}

// Leave is used to prepare for a graceful shutdown
func (c *Client) Leave() error {
	c.logger.Printf("[INFO] consul: client starting leave")

	// Leave the LAN pool
	if c.serf != nil {
		if err := c.serf.Leave(); err != nil {
			c.logger.Printf("[ERR] consul: Failed to leave LAN Serf cluster: %v", err)
		}
	}
	return nil
}

// JoinLAN is used to have Consul client join the inner-DC pool
// The target address should be another node inside the DC
// listening on the Serf LAN address
func (c *Client) JoinLAN(addrs []string) (int, error) {
	return c.serf.Join(addrs, true)
}

// LANMembers is used to return the members of the LAN cluster
func (c *Client) LANMembers() []serf.Member {
	return c.serf.Members()
}

// RemoveFailedNode is used to remove a failed node from the cluster
func (c *Client) RemoveFailedNode(node string) error {
	return c.serf.RemoveFailedNode(node)
}

// lanEventHandler is used to handle events from the lan Serf cluster
func (c *Client) lanEventHandler() {
	for {
		select {
		case e := <-c.eventCh:
			switch e.EventType() {
			case serf.EventMemberJoin:
				c.nodeJoin(e.(serf.MemberEvent))
			case serf.EventMemberLeave:
				fallthrough
			case serf.EventMemberFailed:
				c.nodeFail(e.(serf.MemberEvent))
			case serf.EventUser:
				c.localEvent(e.(serf.UserEvent))
			case serf.EventMemberUpdate: // Ignore
			case serf.EventMemberReap: // Ignore
			case serf.EventQuery: // Ignore
			default:
				c.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-c.shutdownCh:
			return
		}
	}
}

// nodeJoin is used to handle join events on the serf cluster
func (c *Client) nodeJoin(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		if parts.Datacenter != c.config.Datacenter {
			c.logger.Printf("[WARN] consul: server %s for datacenter %s has joined wrong cluster",
				m.Name, parts.Datacenter)
			continue
		}
		c.logger.Printf("[INFO] consul: adding server %s", parts)

		// Check if this server is known
		found := false
		c.consulLock.Lock()
		for idx, existing := range c.consuls {
			if existing.Name == parts.Name {
				c.consuls[idx] = parts
				found = true
				break
			}
		}

		// Add to the list if not known
		if !found {
			c.consuls = append(c.consuls, parts)
		}
		c.consulLock.Unlock()

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	}
}

// nodeFail is used to handle fail events on the serf cluster
func (c *Client) nodeFail(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		var addr net.Addr = &net.TCPAddr{IP: m.Addr, Port: parts.Port}
		c.logger.Printf("[INFO] consul: removing server for datacenter: %s, addr: %s", parts.Datacenter, addr)

		// Remove the server if known
		c.consulLock.Lock()
		n := len(c.consuls)
		for i := 0; i < n; i++ {
			if c.consuls[i].String() == addr.String() {
				c.consuls[i], c.consuls[n-1] = c.consuls[n-1], nil
				c.consuls = c.consuls[:n-1]
				break
			}
		}
		c.consulLock.Unlock()
	}
}

// localEvent is called when we receive an event on the local Serf
func (c *Client) localEvent(event serf.UserEvent) {
	// Handle only consul events
	if !strings.HasPrefix(event.Name, "consul:") {
		return
	}

	switch event.Name {
	case newLeaderEvent:
		c.logger.Printf("[INFO] consul: New leader elected: %s", event.Payload)

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	default:
		c.logger.Printf("[WARN] consul: Unhandled local event: %v", event)
	}
}

// RPC is used to forward an RPC call to a consul server, or fail if no servers
func (c *Client) RPC(method string, args interface{}, reply interface{}) error {
	// Check the last rpc time
	var server *serverParts
	if time.Now().Sub(c.lastRPCTime) < clientRPCCache {
		server = c.lastServer
		if server != nil {
			goto TRY_RPC
		}
	}

	// Bail if we can't find any servers
	c.consulLock.RLock()
	if len(c.consuls) == 0 {
		c.consulLock.RUnlock()
		return structs.ErrNoServers
	}

	// Select a random addr
	server = c.consuls[rand.Int31()%int32(len(c.consuls))]
	c.consulLock.RUnlock()

	// Forward to remote Consul
TRY_RPC:
	if err := c.connPool.RPC(server.Addr, server.Version, method, args, reply); err != nil {
		c.lastServer = nil
		c.lastRPCTime = time.Time{}
		return err
	}

	// Cache the last server
	c.lastServer = server
	c.lastRPCTime = time.Now()
	return nil
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (c *Client) Stats() map[string]map[string]string {
	toString := func(v uint64) string {
		return strconv.FormatUint(v, 10)
	}
	stats := map[string]map[string]string{
		"consul": map[string]string{
			"server":        "false",
			"known_servers": toString(uint64(len(c.consuls))),
		},
		"serf_lan": c.serf.Stats(),
		"runtime":  runtimeStats(),
	}
	return stats
}
<commit_msg>consul: Fix client server reaping<commit_after>package consul

import (
	"crypto/tls"
	"fmt"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/serf"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	// clientRPCCache controls how long we keep an idle connection
	// open to a server
	clientRPCCache = 30 * time.Second

	// clientMaxStreams controsl how many idle streams we keep
	// open to a server
	clientMaxStreams = 32
)

// Interface is used to provide either a Client or Server,
// both of which can be used to perform certain common
// Consul methods
type Interface interface {
	RPC(method string, args interface{}, reply interface{}) error
	LANMembers() []serf.Member
}

// Client is Consul client which uses RPC to communicate with the
// services for service discovery, health checking, and DC forwarding.
type Client struct {
	config *Config

	// Connection pool to consul servers
	connPool *ConnPool

	// consuls tracks the locally known servers
	consuls    []*serverParts
	consulLock sync.RWMutex

	// eventCh is used to receive events from the
	// serf cluster in the datacenter
	eventCh chan serf.Event

	// lastServer is the last server we made an RPC call to,
	// this is used to re-use the last connection
	lastServer  *serverParts
	lastRPCTime time.Time

	// Logger uses the provided LogOutput
	logger *log.Logger

	// serf is the Serf cluster maintained inside the DC
	// which contains all the DC nodes
	serf *serf.Serf

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewClient is used to construct a new Consul client from the
// configuration, potentially returning an error
func NewClient(config *Config) (*Client, error) {
	// Check the protocol version
	if err := config.CheckVersion(); err != nil {
		return nil, err
	}

	// Check for a data directory!
	if config.DataDir == "" {
		return nil, fmt.Errorf("Config must provide a DataDir")
	}

	// Ensure we have a log output
	if config.LogOutput == nil {
		config.LogOutput = os.Stderr
	}

	// Create the tlsConfig
	var tlsConfig *tls.Config
	var err error
	if config.VerifyOutgoing {
		if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
			return nil, err
		}
	}

	// Create a logger
	logger := log.New(config.LogOutput, "", log.LstdFlags)

	// Create server
	c := &Client{
		config:     config,
		connPool:   NewPool(clientRPCCache, clientMaxStreams, tlsConfig),
		eventCh:    make(chan serf.Event, 256),
		logger:     logger,
		shutdownCh: make(chan struct{}),
	}

	// Start the Serf listeners to prevent a deadlock
	go c.lanEventHandler()

	// Initialize the lan Serf
	c.serf, err = c.setupSerf(config.SerfLANConfig,
		c.eventCh, serfLANSnapshot)
	if err != nil {
		c.Shutdown()
		return nil, fmt.Errorf("Failed to start lan serf: %v", err)
	}
	return c, nil
}

// setupSerf is used to setup and initialize a Serf
func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (*serf.Serf, error) {
	conf.Init()
	conf.NodeName = c.config.NodeName
	conf.Tags["role"] = "node"
	conf.Tags["dc"] = c.config.Datacenter
	conf.Tags["vsn"] = fmt.Sprintf("%d", c.config.ProtocolVersion)
	conf.Tags["vsn_min"] = fmt.Sprintf("%d", ProtocolVersionMin)
	conf.Tags["vsn_max"] = fmt.Sprintf("%d", ProtocolVersionMax)
	conf.MemberlistConfig.LogOutput = c.config.LogOutput
	conf.LogOutput = c.config.LogOutput
	conf.EventCh = ch
	conf.SnapshotPath = filepath.Join(c.config.DataDir, path)
	conf.ProtocolVersion = protocolVersionMap[c.config.ProtocolVersion]
	conf.RejoinAfterLeave = c.config.RejoinAfterLeave
	if err := ensurePath(conf.SnapshotPath, false); err != nil {
		return nil, err
	}
	return serf.Create(conf)
}

// Shutdown is used to shutdown the client
func (c *Client) Shutdown() error {
	c.logger.Printf("[INFO] consul: shutting down client")
	c.shutdownLock.Lock()
	defer c.shutdownLock.Unlock()

	if c.shutdown {
		return nil
	}

	c.shutdown = true
	close(c.shutdownCh)

	if c.serf != nil {
		c.serf.Shutdown()
	}

	// Close the connection pool
	c.connPool.Shutdown()
	return nil
}

// Leave is used to prepare for a graceful shutdown
func (c *Client) Leave() error {
	c.logger.Printf("[INFO] consul: client starting leave")

	// Leave the LAN pool
	if c.serf != nil {
		if err := c.serf.Leave(); err != nil {
			c.logger.Printf("[ERR] consul: Failed to leave LAN Serf cluster: %v", err)
		}
	}
	return nil
}

// JoinLAN is used to have Consul client join the inner-DC pool
// The target address should be another node inside the DC
// listening on the Serf LAN address
func (c *Client) JoinLAN(addrs []string) (int, error) {
	return c.serf.Join(addrs, true)
}

// LANMembers is used to return the members of the LAN cluster
func (c *Client) LANMembers() []serf.Member {
	return c.serf.Members()
}

// RemoveFailedNode is used to remove a failed node from the cluster
func (c *Client) RemoveFailedNode(node string) error {
	return c.serf.RemoveFailedNode(node)
}

// lanEventHandler is used to handle events from the lan Serf cluster
func (c *Client) lanEventHandler() {
	for {
		select {
		case e := <-c.eventCh:
			switch e.EventType() {
			case serf.EventMemberJoin:
				c.nodeJoin(e.(serf.MemberEvent))
			case serf.EventMemberLeave:
				fallthrough
			case serf.EventMemberFailed:
				c.nodeFail(e.(serf.MemberEvent))
			case serf.EventUser:
				c.localEvent(e.(serf.UserEvent))
			case serf.EventMemberUpdate: // Ignore
			case serf.EventMemberReap: // Ignore
			case serf.EventQuery: // Ignore
			default:
				c.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-c.shutdownCh:
			return
		}
	}
}

// nodeJoin is used to handle join events on the serf cluster
func (c *Client) nodeJoin(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		if parts.Datacenter != c.config.Datacenter {
			c.logger.Printf("[WARN] consul: server %s for datacenter %s has joined wrong cluster",
				m.Name, parts.Datacenter)
			continue
		}
		c.logger.Printf("[INFO] consul: adding server %s", parts)

		// Check if this server is known
		found := false
		c.consulLock.Lock()
		for idx, existing := range c.consuls {
			if existing.Name == parts.Name {
				c.consuls[idx] = parts
				found = true
				break
			}
		}

		// Add to the list if not known
		if !found {
			c.consuls = append(c.consuls, parts)
		}
		c.consulLock.Unlock()

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	}
}

// nodeFail is used to handle fail events on the serf cluster
func (c *Client) nodeFail(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		c.logger.Printf("[INFO] consul: removing server %s", parts)

		// Remove the server if known
		c.consulLock.Lock()
		n := len(c.consuls)
		for i := 0; i < n; i++ {
			if c.consuls[i].Name == parts.Name {
				c.consuls[i], c.consuls[n-1] = c.consuls[n-1], nil
				c.consuls = c.consuls[:n-1]
				break
			}
		}
		c.consulLock.Unlock()
	}
}

// localEvent is called when we receive an event on the local Serf
func (c *Client) localEvent(event serf.UserEvent) {
	// Handle only consul events
	if !strings.HasPrefix(event.Name, "consul:") {
		return
	}

	switch event.Name {
	case newLeaderEvent:
		c.logger.Printf("[INFO] consul: New leader elected: %s", event.Payload)

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	default:
		c.logger.Printf("[WARN] consul: Unhandled local event: %v", event)
	}
}

// RPC is used to forward an RPC call to a consul server, or fail if no servers
func (c *Client) RPC(method string, args interface{}, reply interface{}) error {
	// Check the last rpc time
	var server *serverParts
	if time.Now().Sub(c.lastRPCTime) < clientRPCCache {
		server = c.lastServer
		if server != nil {
			goto TRY_RPC
		}
	}

	// Bail if we can't find any servers
	c.consulLock.RLock()
	if len(c.consuls) == 0 {
		c.consulLock.RUnlock()
		return structs.ErrNoServers
	}

	// Select a random addr
	server = c.consuls[rand.Int31()%int32(len(c.consuls))]
	c.consulLock.RUnlock()

	// Forward to remote Consul
TRY_RPC:
	if err := c.connPool.RPC(server.Addr, server.Version, method, args, reply); err != nil {
		c.lastServer = nil
		c.lastRPCTime = time.Time{}
		return err
	}

	// Cache the last server
	c.lastServer = server
	c.lastRPCTime = time.Now()
	return nil
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (c *Client) Stats() map[string]map[string]string {
	toString := func(v uint64) string {
		return strconv.FormatUint(v, 10)
	}
	stats := map[string]map[string]string{
		"consul": map[string]string{
			"server":        "false",
			"known_servers": toString(uint64(len(c.consuls))),
		},
		"serf_lan": c.serf.Stats(),
		"runtime":  runtimeStats(),
	}
	return stats
}
<|endoftext|>


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

package main

import (
	"archive/tar"
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"

	"github.com/coreos/rkt/pkg/aci"
	"github.com/coreos/rkt/pkg/keystore"
	"github.com/coreos/rkt/pkg/keystore/keystoretest"
	"github.com/coreos/rkt/rkt/config"
	"github.com/coreos/rkt/store"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/discovery"
	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
)

type httpError struct {
	code    int
	message string
}

func (e *httpError) Error() string {
	return fmt.Sprintf("%d: %s", e.code, e.message)
}

type serverHandler struct {
	body []byte
	t    *testing.T
	auth string
}

func (h *serverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch h.auth {
	case "deny":
		if _, ok := r.Header[http.CanonicalHeaderKey("Authorization")]; ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	case "none":
		// no auth to do.
	case "basic":
		payload, httpErr := getAuthPayload(r, "Basic")
		if httpErr != nil {
			w.WriteHeader(httpErr.code)
			return
		}
		creds, err := base64.StdEncoding.DecodeString(string(payload))
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		parts := strings.Split(string(creds), ":")
		if len(parts) != 2 {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		user := parts[0]
		password := parts[1]
		if user != "bar" || password != "baz" {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	case "bearer":
		payload, httpErr := getAuthPayload(r, "Bearer")
		if httpErr != nil {
			w.WriteHeader(httpErr.code)
			return
		}
		if payload != "sometoken" {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	default:
		panic("bug in test")
	}
	w.Write(h.body)
}

func getAuthPayload(r *http.Request, authType string) (string, *httpError) {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		err := &httpError{
			code:    http.StatusUnauthorized,
			message: "No auth",
		}
		return "", err
	}
	parts := strings.Split(auth, " ")
	if len(parts) != 2 {
		err := &httpError{
			code:    http.StatusBadRequest,
			message: "Malformed auth",
		}
		return "", err
	}
	if parts[0] != authType {
		err := &httpError{
			code:    http.StatusUnauthorized,
			message: "Wrong auth",
		}
		return "", err
	}
	return parts[1], nil
}

type testHeaderer struct {
	h http.Header
}

func (h *testHeaderer) Header() http.Header {
	return h.h
}

func TestNewDiscoveryApp(t *testing.T) {
	tests := []struct {
		in string

		w *discovery.App
	}{
		// not a valid AC name
		{
			"bad AC name",
			nil,
		},
		// simple case - default arch, os should be substituted
		{
			"foo.com/bar",
			&discovery.App{
				Name: "foo.com/bar",
				Labels: map[types.ACName]string{
					"arch": defaultArch,
					"os":   defaultOS,
				},
			},
		},
		// overriding arch, os should work
		{
			"www.abc.xyz/my/app,os=freebsd,arch=i386",
			&discovery.App{
				Name: "www.abc.xyz/my/app",
				Labels: map[types.ACName]string{
					"arch": "i386",
					"os":   "freebsd",
				},
			},
		},
		// setting version should work
		{
			"yes.com/no:v1.2.3",
			&discovery.App{
				Name: "yes.com/no",
				Labels: map[types.ACName]string{
					"version": "v1.2.3",
					"arch":    defaultArch,
					"os":      defaultOS,
				},
			},
		},
		// arbitrary user-supplied labels
		{
			"example.com/foo/haha,val=one",
			&discovery.App{
				Name: "example.com/foo/haha",
				Labels: map[types.ACName]string{
					"val":  "one",
					"arch": defaultArch,
					"os":   defaultOS,
				},
			},
		},
		// combinations
		{
			"one.two/appname:three,os=four,foo=five,arch=six",
			&discovery.App{
				Name: "one.two/appname",
				Labels: map[types.ACName]string{
					"version": "three",
					"os":      "four",
					"foo":     "five",
					"arch":    "six",
				},
			},
		},
	}
	for i, tt := range tests {
		g := newDiscoveryApp(tt.in)
		if !reflect.DeepEqual(g, tt.w) {
			t.Errorf("#%d: got %v, want %v", i, g, tt.w)
		}
	}
}

func TestDownloading(t *testing.T) {
	dir, err := ioutil.TempDir("", "download-image")
	if err != nil {
		t.Fatalf("error creating tempdir: %v", err)
	}
	defer os.RemoveAll(dir)

	imj := `{
			"acKind": "ImageManifest",
			"acVersion": "0.5.3",
			"name": "example.com/test01"
		}`

	entries := []*aci.ACIEntry{
		// An empty file
		{
			Contents: "hello",
			Header: &tar.Header{
				Name: "rootfs/file01.txt",
				Size: 5,
			},
		},
	}

	aci, err := aci.NewACI(dir, imj, entries)
	if err != nil {
		t.Fatalf("error creating test tar: %v", err)
	}

	// Rewind the ACI
	if _, err := aci.Seek(0, 0); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	body, err := ioutil.ReadAll(aci)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	noauthServer := &serverHandler{
		body: body,
		t:    t,
		auth: "none",
	}
	basicServer := &serverHandler{
		body: body,
		t:    t,
		auth: "basic",
	}
	oauthServer := &serverHandler{
		body: body,
		t:    t,
		auth: "bearer",
	}
	denyServer := &serverHandler{
		body: body,
		t:    t,
		auth: "deny",
	}
	noAuthTS := httptest.NewTLSServer(noauthServer)
	defer noAuthTS.Close()
	basicTS := httptest.NewTLSServer(basicServer)
	defer basicTS.Close()
	oauthTS := httptest.NewTLSServer(oauthServer)
	defer oauthTS.Close()
	denyAuthTS := httptest.NewServer(denyServer)
	noAuth := http.Header{}
	// YmFyOmJheg== is base64(bar:baz)
	basicAuth := http.Header{"Authorization": {"Basic YmFyOmJheg=="}}
	bearerAuth := http.Header{"Authorization": {"Bearer sometoken"}}
	urlToName := map[string]string{
		noAuthTS.URL:   "no auth",
		basicTS.URL:    "basic",
		oauthTS.URL:    "oauth",
		denyAuthTS.URL: "deny auth",
	}
	tests := []struct {
		ACIURL   string
		SigURL   string
		body     []byte
		hit      bool
		options  http.Header
		authFail bool
	}{
		{noAuthTS.URL, "", body, false, noAuth, false},
		{noAuthTS.URL, "", body, true, noAuth, false},
		{noAuthTS.URL, "", body, true, bearerAuth, false},
		{noAuthTS.URL, "", body, true, basicAuth, false},

		{basicTS.URL, "", body, false, noAuth, true},
		{basicTS.URL, "", body, false, bearerAuth, true},
		{basicTS.URL, "", body, false, basicAuth, false},

		{oauthTS.URL, "", body, false, noAuth, true},
		{oauthTS.URL, "", body, false, basicAuth, true},
		{oauthTS.URL, "", body, false, bearerAuth, false},

		{denyAuthTS.URL, "", body, false, basicAuth, false},
		{denyAuthTS.URL, "", body, true, bearerAuth, false},
		{denyAuthTS.URL, "", body, true, noAuth, false},
	}

	ds, err := store.NewStore(dir)
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	for _, tt := range tests {
		_, ok, err := ds.GetRemote(tt.ACIURL)
		if err != nil {
			t.Fatalf("unexpected err: %v", err)
		}
		if tt.hit == false && ok {
			t.Fatalf("expected miss got a hit")
		}
		if tt.hit == true && !ok {
			t.Fatalf("expected a hit got a miss")
		}
		parsed, err := url.Parse(tt.ACIURL)
		if err != nil {
			panic(fmt.Sprintf("Invalid url from test server: %s", tt.ACIURL))
		}
		headers := map[string]config.Headerer{
			parsed.Host: &testHeaderer{tt.options},
		}
		ft := &fetcher{
			imageActionData: imageActionData{
				ds:                 ds,
				headers:            headers,
				insecureSkipVerify: true,
			},
		}
		_, aciFile, err := ft.fetch(tt.ACIURL, tt.SigURL, nil)
		if err == nil {
			defer os.Remove(aciFile.Name())
		}
		if err != nil && !tt.authFail {
			t.Fatalf("expected download to succeed, it failed: %v (server: %q, headers: `%v`)", err, urlToName[tt.ACIURL], tt.options)
		}
		if err == nil && tt.authFail {
			t.Fatalf("expected download to fail, it succeeded (server: %q, headers: `%v`)", urlToName[tt.ACIURL], tt.options)
		}
		if err != nil {
			continue
		}

		key, err := ds.WriteACI(aciFile, false)
		if err != nil {
			t.Fatalf("unexpected err: %v", err)
		}
		rem := store.NewRemote(tt.ACIURL, tt.SigURL)
		rem.BlobKey = key
		err = ds.WriteRemote(rem)
		if err != nil {
			t.Fatalf("unexpected err: %v", err)
		}
	}

	ds.Dump(false)
}

func TestFetchImage(t *testing.T) {
	dir, err := ioutil.TempDir("", "fetch-image")
	if err != nil {
		t.Fatalf("error creating tempdir: %v", err)
	}
	defer os.RemoveAll(dir)
	ds, err := store.NewStore(dir)
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	defer ds.Dump(false)

	ks, ksPath, err := keystore.NewTestKeystore()
	if err != nil {
		t.Errorf("unexpected error %v", err)
	}
	defer os.RemoveAll(ksPath)

	key := keystoretest.KeyMap["example.com/app"]
	if _, err := ks.StoreTrustedKeyPrefix("example.com/app", bytes.NewBufferString(key.ArmoredPublicKey)); err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	a, err := aci.NewBasicACI(dir, "example.com/app")
	defer a.Close()
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	// Rewind the ACI
	if _, err := a.Seek(0, 0); err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	asc, err := aci.NewDetachedSignature(key.ArmoredPrivateKey, a)
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	// Rewind the ACI.
	if _, err := a.Seek(0, 0); err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch filepath.Ext(r.URL.Path) {
		case ".aci":
			io.Copy(w, a)
			return
		case ".asc":
			io.Copy(w, asc)
			return
		default:
			t.Fatalf("unknown extension %v", r.URL.Path)
		}
	}))
	defer ts.Close()
	ft := &fetcher{
		imageActionData: imageActionData{
			ds: ds,
			ks: ks,
		},
	}
	_, err = ft.fetchImage(fmt.Sprintf("%s/app.aci", ts.URL), "", true)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestSigURLFromImgURL(t *testing.T) {
	tests := []struct {
		in, out string
	}{
		{
			"http://localhost/aci-latest-linux-amd64.aci",
			"http://localhost/aci-latest-linux-amd64.aci.asc",
		},
	}
	for i, tt := range tests {
		out := ascURLFromImgURL(tt.in)
		if out != tt.out {
			t.Errorf("#%d: got %v, want %v", i, out, tt.out)
		}
	}
}
<commit_msg>rkt: Simplify TestDownloading<commit_after>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"archive/tar"
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"

	"github.com/coreos/rkt/pkg/aci"
	"github.com/coreos/rkt/pkg/keystore"
	"github.com/coreos/rkt/pkg/keystore/keystoretest"
	"github.com/coreos/rkt/rkt/config"
	"github.com/coreos/rkt/store"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/discovery"
	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
)

type httpError struct {
	code    int
	message string
}

func (e *httpError) Error() string {
	return fmt.Sprintf("%d: %s", e.code, e.message)
}

type serverHandler struct {
	body []byte
	t    *testing.T
	auth string
}

func (h *serverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch h.auth {
	case "deny":
		if _, ok := r.Header[http.CanonicalHeaderKey("Authorization")]; ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	case "none":
		// no auth to do.
	case "basic":
		payload, httpErr := getAuthPayload(r, "Basic")
		if httpErr != nil {
			w.WriteHeader(httpErr.code)
			return
		}
		creds, err := base64.StdEncoding.DecodeString(string(payload))
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		parts := strings.Split(string(creds), ":")
		if len(parts) != 2 {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		user := parts[0]
		password := parts[1]
		if user != "bar" || password != "baz" {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	case "bearer":
		payload, httpErr := getAuthPayload(r, "Bearer")
		if httpErr != nil {
			w.WriteHeader(httpErr.code)
			return
		}
		if payload != "sometoken" {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	default:
		panic("bug in test")
	}
	w.Write(h.body)
}

func getAuthPayload(r *http.Request, authType string) (string, *httpError) {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		err := &httpError{
			code:    http.StatusUnauthorized,
			message: "No auth",
		}
		return "", err
	}
	parts := strings.Split(auth, " ")
	if len(parts) != 2 {
		err := &httpError{
			code:    http.StatusBadRequest,
			message: "Malformed auth",
		}
		return "", err
	}
	if parts[0] != authType {
		err := &httpError{
			code:    http.StatusUnauthorized,
			message: "Wrong auth",
		}
		return "", err
	}
	return parts[1], nil
}

type testHeaderer struct {
	h http.Header
}

func (h *testHeaderer) Header() http.Header {
	return h.h
}

func TestNewDiscoveryApp(t *testing.T) {
	tests := []struct {
		in string

		w *discovery.App
	}{
		// not a valid AC name
		{
			"bad AC name",
			nil,
		},
		// simple case - default arch, os should be substituted
		{
			"foo.com/bar",
			&discovery.App{
				Name: "foo.com/bar",
				Labels: map[types.ACName]string{
					"arch": defaultArch,
					"os":   defaultOS,
				},
			},
		},
		// overriding arch, os should work
		{
			"www.abc.xyz/my/app,os=freebsd,arch=i386",
			&discovery.App{
				Name: "www.abc.xyz/my/app",
				Labels: map[types.ACName]string{
					"arch": "i386",
					"os":   "freebsd",
				},
			},
		},
		// setting version should work
		{
			"yes.com/no:v1.2.3",
			&discovery.App{
				Name: "yes.com/no",
				Labels: map[types.ACName]string{
					"version": "v1.2.3",
					"arch":    defaultArch,
					"os":      defaultOS,
				},
			},
		},
		// arbitrary user-supplied labels
		{
			"example.com/foo/haha,val=one",
			&discovery.App{
				Name: "example.com/foo/haha",
				Labels: map[types.ACName]string{
					"val":  "one",
					"arch": defaultArch,
					"os":   defaultOS,
				},
			},
		},
		// combinations
		{
			"one.two/appname:three,os=four,foo=five,arch=six",
			&discovery.App{
				Name: "one.two/appname",
				Labels: map[types.ACName]string{
					"version": "three",
					"os":      "four",
					"foo":     "five",
					"arch":    "six",
				},
			},
		},
	}
	for i, tt := range tests {
		g := newDiscoveryApp(tt.in)
		if !reflect.DeepEqual(g, tt.w) {
			t.Errorf("#%d: got %v, want %v", i, g, tt.w)
		}
	}
}

func TestDownloading(t *testing.T) {
	dir, err := ioutil.TempDir("", "download-image")
	if err != nil {
		t.Fatalf("error creating tempdir: %v", err)
	}
	defer os.RemoveAll(dir)

	imj := `{
			"acKind": "ImageManifest",
			"acVersion": "0.5.3",
			"name": "example.com/test01"
		}`

	entries := []*aci.ACIEntry{
		// An empty file
		{
			Contents: "hello",
			Header: &tar.Header{
				Name: "rootfs/file01.txt",
				Size: 5,
			},
		},
	}

	aci, err := aci.NewACI(dir, imj, entries)
	if err != nil {
		t.Fatalf("error creating test tar: %v", err)
	}

	// Rewind the ACI
	if _, err := aci.Seek(0, 0); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	body, err := ioutil.ReadAll(aci)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	noauthServer := &serverHandler{
		body: body,
		t:    t,
		auth: "none",
	}
	basicServer := &serverHandler{
		body: body,
		t:    t,
		auth: "basic",
	}
	oauthServer := &serverHandler{
		body: body,
		t:    t,
		auth: "bearer",
	}
	denyServer := &serverHandler{
		body: body,
		t:    t,
		auth: "deny",
	}
	noAuthTS := httptest.NewTLSServer(noauthServer)
	defer noAuthTS.Close()
	basicTS := httptest.NewTLSServer(basicServer)
	defer basicTS.Close()
	oauthTS := httptest.NewTLSServer(oauthServer)
	defer oauthTS.Close()
	denyAuthTS := httptest.NewServer(denyServer)
	noAuth := http.Header{}
	// YmFyOmJheg== is base64(bar:baz)
	basicAuth := http.Header{"Authorization": {"Basic YmFyOmJheg=="}}
	bearerAuth := http.Header{"Authorization": {"Bearer sometoken"}}
	urlToName := map[string]string{
		noAuthTS.URL:   "no auth",
		basicTS.URL:    "basic",
		oauthTS.URL:    "oauth",
		denyAuthTS.URL: "deny auth",
	}
	tests := []struct {
		ACIURL   string
		hit      bool
		options  http.Header
		authFail bool
	}{
		{noAuthTS.URL, false, noAuth, false},
		{noAuthTS.URL, true, noAuth, false},
		{noAuthTS.URL, true, bearerAuth, false},
		{noAuthTS.URL, true, basicAuth, false},

		{basicTS.URL, false, noAuth, true},
		{basicTS.URL, false, bearerAuth, true},
		{basicTS.URL, false, basicAuth, false},

		{oauthTS.URL, false, noAuth, true},
		{oauthTS.URL, false, basicAuth, true},
		{oauthTS.URL, false, bearerAuth, false},

		{denyAuthTS.URL, false, basicAuth, false},
		{denyAuthTS.URL, true, bearerAuth, false},
		{denyAuthTS.URL, true, noAuth, false},
	}

	ds, err := store.NewStore(dir)
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	for _, tt := range tests {
		_, ok, err := ds.GetRemote(tt.ACIURL)
		if err != nil {
			t.Fatalf("unexpected err: %v", err)
		}
		if tt.hit == false && ok {
			t.Fatalf("expected miss got a hit")
		}
		if tt.hit == true && !ok {
			t.Fatalf("expected a hit got a miss")
		}
		parsed, err := url.Parse(tt.ACIURL)
		if err != nil {
			panic(fmt.Sprintf("Invalid url from test server: %s", tt.ACIURL))
		}
		headers := map[string]config.Headerer{
			parsed.Host: &testHeaderer{tt.options},
		}
		ft := &fetcher{
			imageActionData: imageActionData{
				ds:                 ds,
				headers:            headers,
				insecureSkipVerify: true,
			},
		}
		_, aciFile, err := ft.fetch(tt.ACIURL, "", nil)
		if err == nil {
			defer os.Remove(aciFile.Name())
		}
		if err != nil && !tt.authFail {
			t.Fatalf("expected download to succeed, it failed: %v (server: %q, headers: `%v`)", err, urlToName[tt.ACIURL], tt.options)
		}
		if err == nil && tt.authFail {
			t.Fatalf("expected download to fail, it succeeded (server: %q, headers: `%v`)", urlToName[tt.ACIURL], tt.options)
		}
		if err != nil {
			continue
		}

		key, err := ds.WriteACI(aciFile, false)
		if err != nil {
			t.Fatalf("unexpected err: %v", err)
		}
		rem := store.NewRemote(tt.ACIURL, "")
		rem.BlobKey = key
		err = ds.WriteRemote(rem)
		if err != nil {
			t.Fatalf("unexpected err: %v", err)
		}
	}

	ds.Dump(false)
}

func TestFetchImage(t *testing.T) {
	dir, err := ioutil.TempDir("", "fetch-image")
	if err != nil {
		t.Fatalf("error creating tempdir: %v", err)
	}
	defer os.RemoveAll(dir)
	ds, err := store.NewStore(dir)
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	defer ds.Dump(false)

	ks, ksPath, err := keystore.NewTestKeystore()
	if err != nil {
		t.Errorf("unexpected error %v", err)
	}
	defer os.RemoveAll(ksPath)

	key := keystoretest.KeyMap["example.com/app"]
	if _, err := ks.StoreTrustedKeyPrefix("example.com/app", bytes.NewBufferString(key.ArmoredPublicKey)); err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	a, err := aci.NewBasicACI(dir, "example.com/app")
	defer a.Close()
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	// Rewind the ACI
	if _, err := a.Seek(0, 0); err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	asc, err := aci.NewDetachedSignature(key.ArmoredPrivateKey, a)
	if err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	// Rewind the ACI.
	if _, err := a.Seek(0, 0); err != nil {
		t.Fatalf("unexpected error %v", err)
	}

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch filepath.Ext(r.URL.Path) {
		case ".aci":
			io.Copy(w, a)
			return
		case ".asc":
			io.Copy(w, asc)
			return
		default:
			t.Fatalf("unknown extension %v", r.URL.Path)
		}
	}))
	defer ts.Close()
	ft := &fetcher{
		imageActionData: imageActionData{
			ds: ds,
			ks: ks,
		},
	}
	_, err = ft.fetchImage(fmt.Sprintf("%s/app.aci", ts.URL), "", true)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestSigURLFromImgURL(t *testing.T) {
	tests := []struct {
		in, out string
	}{
		{
			"http://localhost/aci-latest-linux-amd64.aci",
			"http://localhost/aci-latest-linux-amd64.aci.asc",
		},
	}
	for i, tt := range tests {
		out := ascURLFromImgURL(tt.in)
		if out != tt.out {
			t.Errorf("#%d: got %v, want %v", i, out, tt.out)
		}
	}
}
<|endoftext|>


<commit_before>package db

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/bosssauce/ponzu/system/admin/user"

	"github.com/boltdb/bolt"
	"github.com/nilslice/jwt"
	"github.com/nilslice/rand"
)

// ErrUserExists is used for the db to report to admin user of existing user
var ErrUserExists = errors.New("Error. User exists.")

// ErrNoUserExists is used for the db to report to admin user of non-existing user
var ErrNoUserExists = errors.New("Error. No user exists.")

// SetUser sets key:value pairs in the db for user settings
func SetUser(usr *user.User) (int, error) {
	err := store.Update(func(tx *bolt.Tx) error {
		email := []byte(usr.Email)
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get(email)
		if exists != nil {
			return ErrUserExists
		}

		// get NextSequence int64 and set it as the User.ID
		id, err := users.NextSequence()
		if err != nil {
			return err
		}
		usr.ID = int(id)

		// marshal User to json and put into bucket
		j, err := json.Marshal(usr)
		if err != nil {
			return err
		}

		err = users.Put(email, j)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return 0, err
	}

	return usr.ID, nil
}

// UpdateUser sets key:value pairs in the db for existing user settings
func UpdateUser(usr, updatedUsr *user.User) error {
	err := store.Update(func(tx *bolt.Tx) error {
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get([]byte(usr.Email))
		if exists == nil {
			return ErrNoUserExists
		}

		// marshal User to json and put into bucket
		j, err := json.Marshal(updatedUsr)
		if err != nil {
			return err
		}

		err = users.Put([]byte(updatedUsr.Email), j)
		if err != nil {
			return err
		}

		// if email address was changed, delete the old record of former
		// user with original email address
		if usr.Email != updatedUsr.Email {
			err = users.Delete([]byte(usr.Email))
			if err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// DeleteUser deletes a user from the db by email
func DeleteUser(email string) error {
	err := store.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.Delete([]byte(email))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// User gets the user by email from the db
func User(email string) ([]byte, error) {
	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		usr := b.Get([]byte(email))

		_, err := val.Write(usr)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	if val.Bytes() == nil {
		return nil, ErrNoUserExists
	}

	return val.Bytes(), nil
}

// UserAll returns all users from the db
func UserAll() ([][]byte, error) {
	var users [][]byte
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.ForEach(func(k, v []byte) error {
			users = append(users, v)
			return nil
		})
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return users, nil
}

// CurrentUser extracts the user from the request data and returns the current user from the db
func CurrentUser(req *http.Request) ([]byte, error) {
	if !user.IsValid(req) {
		return nil, fmt.Errorf("Error. Invalid User.")
	}

	token, err := req.Cookie("_token")
	if err != nil {
		return nil, err
	}

	claims := jwt.GetClaims(token.Value)
	email, ok := claims["user"]
	if !ok {
		return nil, fmt.Errorf("Error. No user data found in request token.")
	}

	usr, err := User(email.(string))
	if err != nil {
		return nil, err
	}

	return usr, nil
}

// SetRecoveryKey generates and saves a random secret key to verify an email
// address submitted in order to recover/reset an account password
func SetRecoveryKey(email string) (string, error) {
	key := fmt.Sprintf("%d", rand.Int63())

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte("_recoveryKeys"))
		if err != nil {
			return err
		}

		err = b.Put([]byte(email), []byte(key))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key, nil
}

// RecoveryKey gets a previously set recovery key to verify an email address
// submitted in order to recover/reset an account password
func RecoveryKey(email string) (string, error) {
	key := &bytes.Buffer{}

	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_recoveryKeys"))
		if b == nil {
			return errors.New("No database found for checking keys.")
		}

		_, err := key.Write(b.Get([]byte("email")))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key.String(), nil
}
<commit_msg>fixed lookup by email<commit_after>package db

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/bosssauce/ponzu/system/admin/user"

	"github.com/boltdb/bolt"
	"github.com/nilslice/jwt"
	"github.com/nilslice/rand"
)

// ErrUserExists is used for the db to report to admin user of existing user
var ErrUserExists = errors.New("Error. User exists.")

// ErrNoUserExists is used for the db to report to admin user of non-existing user
var ErrNoUserExists = errors.New("Error. No user exists.")

// SetUser sets key:value pairs in the db for user settings
func SetUser(usr *user.User) (int, error) {
	err := store.Update(func(tx *bolt.Tx) error {
		email := []byte(usr.Email)
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get(email)
		if exists != nil {
			return ErrUserExists
		}

		// get NextSequence int64 and set it as the User.ID
		id, err := users.NextSequence()
		if err != nil {
			return err
		}
		usr.ID = int(id)

		// marshal User to json and put into bucket
		j, err := json.Marshal(usr)
		if err != nil {
			return err
		}

		err = users.Put(email, j)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return 0, err
	}

	return usr.ID, nil
}

// UpdateUser sets key:value pairs in the db for existing user settings
func UpdateUser(usr, updatedUsr *user.User) error {
	err := store.Update(func(tx *bolt.Tx) error {
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get([]byte(usr.Email))
		if exists == nil {
			return ErrNoUserExists
		}

		// marshal User to json and put into bucket
		j, err := json.Marshal(updatedUsr)
		if err != nil {
			return err
		}

		err = users.Put([]byte(updatedUsr.Email), j)
		if err != nil {
			return err
		}

		// if email address was changed, delete the old record of former
		// user with original email address
		if usr.Email != updatedUsr.Email {
			err = users.Delete([]byte(usr.Email))
			if err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// DeleteUser deletes a user from the db by email
func DeleteUser(email string) error {
	err := store.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.Delete([]byte(email))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// User gets the user by email from the db
func User(email string) ([]byte, error) {
	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		usr := b.Get([]byte(email))

		_, err := val.Write(usr)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	if val.Bytes() == nil {
		return nil, ErrNoUserExists
	}

	return val.Bytes(), nil
}

// UserAll returns all users from the db
func UserAll() ([][]byte, error) {
	var users [][]byte
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.ForEach(func(k, v []byte) error {
			users = append(users, v)
			return nil
		})
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return users, nil
}

// CurrentUser extracts the user from the request data and returns the current user from the db
func CurrentUser(req *http.Request) ([]byte, error) {
	if !user.IsValid(req) {
		return nil, fmt.Errorf("Error. Invalid User.")
	}

	token, err := req.Cookie("_token")
	if err != nil {
		return nil, err
	}

	claims := jwt.GetClaims(token.Value)
	email, ok := claims["user"]
	if !ok {
		return nil, fmt.Errorf("Error. No user data found in request token.")
	}

	usr, err := User(email.(string))
	if err != nil {
		return nil, err
	}

	return usr, nil
}

// SetRecoveryKey generates and saves a random secret key to verify an email
// address submitted in order to recover/reset an account password
func SetRecoveryKey(email string) (string, error) {
	key := fmt.Sprintf("%d", rand.Int63())

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte("_recoveryKeys"))
		if err != nil {
			return err
		}

		err = b.Put([]byte(email), []byte(key))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key, nil
}

// RecoveryKey gets a previously set recovery key to verify an email address
// submitted in order to recover/reset an account password
func RecoveryKey(email string) (string, error) {
	key := &bytes.Buffer{}

	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_recoveryKeys"))
		if b == nil {
			return errors.New("No database found for checking keys.")
		}

		_, err := key.Write(b.Get([]byte(email)))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key.String(), nil
}
<|endoftext|>


<commit_before>package taku

import (
	"testing"

	"github.com/k0kubun/pp"
)

func TestDoCalcPoint(t *testing.T) {
	taku := Taku{}
	c := taku.DoCalcPoint(1)
	pp.Println(c.String())
}
<commit_msg>test: 依存ライブラリを消去<commit_after>package taku

import (
	"testing"

	"fmt"
)

func TestDoCalcPoint(t *testing.T) {
	taku := Taku{}
	c := taku.DoCalcPoint(1)
	fmt.Println(c.String())
}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2016 Ivan Dejanovic

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package cfg

import (
	"bufio"
	"fmt"
	"mlpl/types"
	"os"
	"strings"
)

const (
	minus       = "-"
	doubleMinus = "--"
	empty       = ""
	usage       = "Usage: mlpl <codefilename> [configurationfilename]"
)

func getDefaultReserved() []types.ReservedWord {
	reserved := make([]types.ReservedWord, 0, 8)

	reserved = append(reserved, types.ReservedWord{types.IF, "if"})
	reserved = append(reserved, types.ReservedWord{types.THEN, "then"})
	reserved = append(reserved, types.ReservedWord{types.ELSE, "else"})
	reserved = append(reserved, types.ReservedWord{types.END, "end"})
	reserved = append(reserved, types.ReservedWord{types.REPEAT, "repeat"})
	reserved = append(reserved, types.ReservedWord{types.UNTIL, "until"})
	reserved = append(reserved, types.ReservedWord{types.READ, "read"})
	reserved = append(reserved, types.ReservedWord{types.WRITE, "write"})

	return reserved
}

func getConfigReservedWords(configFile string) []types.ReservedWord {
	reserved := make([]types.ReservedWord, 0, 8)
	var localization []string
	const length = 8

	config, err := os.Open(configFile)
	if err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(config)
	for scanner.Scan() {
		localization = append(localization, scanner.Text())
	}

	defer config.Close()

	if len(localization) != length {
		fmt.Println("Configuration file must contain localizations for eight key word.")
	}

	reserved = append(reserved, types.ReservedWord{types.IF, localization[0]})
	reserved = append(reserved, types.ReservedWord{types.THEN, localization[1]})
	reserved = append(reserved, types.ReservedWord{types.ELSE, localization[2]})
	reserved = append(reserved, types.ReservedWord{types.END, localization[3]})
	reserved = append(reserved, types.ReservedWord{types.REPEAT, localization[4]})
	reserved = append(reserved, types.ReservedWord{types.UNTIL, localization[5]})
	reserved = append(reserved, types.ReservedWord{types.READ, localization[6]})
	reserved = append(reserved, types.ReservedWord{types.WRITE, localization[7]})

	return reserved
}

func HandleArgs() (bool, string, []types.ReservedWord) {
	var abort bool = true
	var codeFile string
	var reserved []types.ReservedWord

	args := os.Args[1:]
	argc := len(args)

	for index := 0; index < argc; index++ {
		var flag string = empty
		var flagArg string = args[index]

		if strings.HasPrefix(flagArg, doubleMinus) {
			flag = strings.TrimPrefix(flagArg, doubleMinus)
		} else if strings.HasPrefix(flagArg, minus) {
			flag = strings.TrimPrefix(flagArg, minus)
		}

		if flag != empty {
			switch flag {
			case "h", "help":
				fmt.Println()
				fmt.Println(usage)
				fmt.Println()
				fmt.Println("Options:")
				fmt.Println("  -h, --help       Prints help")
				fmt.Println("  -v, --version    Prints version")
			case "v", "version":
				fmt.Println("MLPL interpreter version 0.2.0")
			default:
				fmt.Println("Invalid usage. For correct usage examples please try: mlpl -h")
			}
			return abort, codeFile, reserved
		}
	}

	if argc < 1 || argc > 2 {
		fmt.Println(usage)
		return abort, codeFile, reserved
	}

	if argc == 2 {
		reserved = getConfigReservedWords(args[1])
	} else {
		reserved = getDefaultReserved()
	}

	//If we get this far we have good data to process
	abort = false
	codeFile = args[0]

	return abort, codeFile, reserved
}
<commit_msg>Updated version.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2016 Ivan Dejanovic

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package cfg

import (
	"bufio"
	"fmt"
	"mlpl/types"
	"os"
	"strings"
)

const (
	minus       = "-"
	doubleMinus = "--"
	empty       = ""
	usage       = "Usage: mlpl <codefilename> [configurationfilename]"
)

func getDefaultReserved() []types.ReservedWord {
	reserved := make([]types.ReservedWord, 0, 8)

	reserved = append(reserved, types.ReservedWord{types.IF, "if"})
	reserved = append(reserved, types.ReservedWord{types.THEN, "then"})
	reserved = append(reserved, types.ReservedWord{types.ELSE, "else"})
	reserved = append(reserved, types.ReservedWord{types.END, "end"})
	reserved = append(reserved, types.ReservedWord{types.REPEAT, "repeat"})
	reserved = append(reserved, types.ReservedWord{types.UNTIL, "until"})
	reserved = append(reserved, types.ReservedWord{types.READ, "read"})
	reserved = append(reserved, types.ReservedWord{types.WRITE, "write"})

	return reserved
}

func getConfigReservedWords(configFile string) []types.ReservedWord {
	reserved := make([]types.ReservedWord, 0, 8)
	var localization []string
	const length = 8

	config, err := os.Open(configFile)
	if err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(config)
	for scanner.Scan() {
		localization = append(localization, scanner.Text())
	}

	defer config.Close()

	if len(localization) != length {
		fmt.Println("Configuration file must contain localizations for eight key word.")
	}

	reserved = append(reserved, types.ReservedWord{types.IF, localization[0]})
	reserved = append(reserved, types.ReservedWord{types.THEN, localization[1]})
	reserved = append(reserved, types.ReservedWord{types.ELSE, localization[2]})
	reserved = append(reserved, types.ReservedWord{types.END, localization[3]})
	reserved = append(reserved, types.ReservedWord{types.REPEAT, localization[4]})
	reserved = append(reserved, types.ReservedWord{types.UNTIL, localization[5]})
	reserved = append(reserved, types.ReservedWord{types.READ, localization[6]})
	reserved = append(reserved, types.ReservedWord{types.WRITE, localization[7]})

	return reserved
}

func HandleArgs() (bool, string, []types.ReservedWord) {
	var abort bool = true
	var codeFile string
	var reserved []types.ReservedWord

	args := os.Args[1:]
	argc := len(args)

	for index := 0; index < argc; index++ {
		var flag string = empty
		var flagArg string = args[index]

		if strings.HasPrefix(flagArg, doubleMinus) {
			flag = strings.TrimPrefix(flagArg, doubleMinus)
		} else if strings.HasPrefix(flagArg, minus) {
			flag = strings.TrimPrefix(flagArg, minus)
		}

		if flag != empty {
			switch flag {
			case "h", "help":
				fmt.Println()
				fmt.Println(usage)
				fmt.Println()
				fmt.Println("Options:")
				fmt.Println("  -h, --help       Prints help")
				fmt.Println("  -v, --version    Prints version")
			case "v", "version":
				fmt.Println("MLPL interpreter version 0.2.1")
			default:
				fmt.Println("Invalid usage. For correct usage examples please try: mlpl -h")
			}
			return abort, codeFile, reserved
		}
	}

	if argc < 1 || argc > 2 {
		fmt.Println(usage)
		return abort, codeFile, reserved
	}

	if argc == 2 {
		reserved = getConfigReservedWords(args[1])
	} else {
		reserved = getDefaultReserved()
	}

	//If we get this far we have good data to process
	abort = false
	codeFile = args[0]

	return abort, codeFile, reserved
}
<|endoftext|>


<commit_before>package template

import (
	"bytes"
	"gnd.la/loaders"
	"gnd.la/template/assets"
	"testing"
)

type templateTest struct {
	tmpl   string
	data   interface{}
	result string
}

type testType struct {
}

func (t *testType) Foo() string {
	return "bar"
}

func (t *testType) Bar(s string) string {
	return "bared-" + s
}

var (
	ftests = []*templateTest{
		{"{{ $one := 1 }}{{ $two := 2 }}{{ $three := 3 }}{{ $one }}+{{ $two }}+{{ $three }}={{ add $one $two $three }}", nil, "1+2+3=6"},
		{"{{ add 2 3 }}", nil, "5"},
		{"{{ to_lower .foo }}", map[string]string{"foo": "BAR"}, "bar"},
		{"{{ to_upper .foo }}", map[string]string{"foo": "bar"}, "BAR"},
		{"{{ join .chars .sep }}", map[string]interface{}{"chars": []string{"a", "b", "c"}, "sep": ","}, "a,b,c"},
		{"{{ to_html .s }}", map[string]string{"s": "<foo\nbar"}, "<foo
bar"},
		{"{{ mult 2 1.1 }}", nil, "2.2"},
		{"{{ imult 2 1.1 }}", nil, "2"},
		{"{{ concat \"foo\" \"bar\" }}", nil, "foobar"},
		{"{{ concat (concat \"foo\" \"bar\") \"baz\" }}", nil, "foobarbaz"},
		{"{{ if divisible 5 2 }}1{{ else }}0{{ end }}", nil, "0"},
		{"{{ if divisible 4 2 }}1{{ else }}0{{ end }}", nil, "1"},
	}
	compilerTests = []*templateTest{
		{"{{ \"output\" | printf \"%s\" }}", nil, "output"},
		{"{{ call .foo }}", map[string]interface{}{"foo": func() string { return "bar" }}, "bar"},
		{"{{ .Foo }}", struct{ Foo string }{"bar"}, "bar"},
		{"{{ .Foo }}", &testType{}, "bar"},
		{"{{ .Bar \"this\" }}", &testType{}, "bared-this"},
		{"{{ .t.Bar .foo }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foo"},
		{"{{ .t.Bar (concat .foo \"bar\") }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foobar"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", map[string]string{"A": "yes"}, "yes"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", nil, "no"},
		{"{{ with .A }}{{ . }}{{ end }}", nil, ""},
		{"{{ range . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ . }}{{ end }}{{ . }}", []int{1, 2, 3}, "123[1 2 3]"},
		{"{{ range $idx, $el := . }}{{ $idx }}{{ $el }}{{ end }}", []int{1, 2, 3}, "011223"},
		{"{{ range $el := . }}{{ $el }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ else }}nope{{ end }}", nil, "nope"},
		{"{{ range $k, $v := . }}{{ $k }}={{ $v }}{{ end }}", map[string]int{"b": 2, "c": 3, "a": 1}, "a=1b=2c=3"},
	}
	compilerErrorTests = []*templateTest{
		{"{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:1:9: can't range over int"},
		{"{{ . }}\n{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:2:9: can't range over int"},
		{"{{ . }}\n{{ range .foo }}{{ else }}nope{{ end }}\n{{ range .bar }}{{ . }}{{ end }} ", map[string]interface{}{"foo": []int{}, "bar": ""}, "template.html:3:9: can't range over string"},
	}
)

func parseText(tb testing.TB, text string) *Template {
	loader := loaders.MapLoader(map[string][]byte{"template.html": []byte(text)})
	tmpl, err := Parse(loader, nil, "template.html")
	if err != nil {
		tb.Errorf("error parsing %q: %s", text, err)
	}
	return tmpl
}

func parseTestTemplate(tb testing.TB, name string) *Template {
	loader := loaders.FSLoader("_testdata")
	tmpl := New(loader, assets.NewManager(loader, ""))
	tmpl.Funcs(FuncMap{"t": func(s string) string { return s }})
	if err := tmpl.Parse(name); err != nil {
		tb.Errorf("error parsing %q: %s", name, err)
		return nil
	}
	return tmpl
}

func TestFunctions(t *testing.T) {
	for _, v := range ftests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompiler(t *testing.T) {
	var tests []*templateTest
	tests = append(tests, ftests...)
	tests = append(tests, compilerTests...)
	for _, v := range tests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		pr, err := NewProgram(tmpl)
		if err != nil {
			t.Errorf("error compiling %q: %s", v.tmpl, err)
			continue
		}
		var buf bytes.Buffer
		if err := pr.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompilerErrors(t *testing.T) {
	for _, v := range compilerErrorTests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		pr, err := NewProgram(tmpl)
		if err != nil {
			t.Errorf("error compiling %q: %s", v.tmpl, err)
			continue
		}
		var buf bytes.Buffer
		err = pr.Execute(&buf, v.data)
		if err == nil {
			t.Errorf("expecting an error when executing %q, got nil", v.tmpl)
			continue
		}
		if err.Error() != v.result {
			t.Logf("template is %q", v.tmpl)
			t.Errorf("expecting error %q, got %q", v.result, err.Error())
		}
	}
}

func TestBigTemplate(t *testing.T) {
	const name = "1.html"
	tmpl := parseTestTemplate(t, name)
	if tmpl != nil {
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, nil); err != nil {
			t.Errorf("error executing template %s: %s", name, err)
		}
	}
}

func TestCompileBigTemplate(t *testing.T) {
	const name = "1.html"
	tmpl := parseTestTemplate(t, name)
	if tmpl != nil {
		pr, err := NewProgram(tmpl)
		if err != nil {
			t.Fatalf("can't compile template %q: %s", name, err)
		}
		var buf1 bytes.Buffer
		if err := tmpl.Execute(&buf1, nil); err != nil {
			t.Errorf("error executing template %s: %s", name, err)
		}
		var buf2 bytes.Buffer
		if err := pr.Execute(&buf2, nil); err != nil {
			t.Errorf("error executing program %s: %s", name, err)
		}
		if buf1.String() != buf2.String() {
			s1 := buf1.String()
			s2 := buf2.String()
			t.Logf("len(s1) = %d, len(s2) = %d", len(s1), len(s2))
			for ii := 0; ii < len(s1) && ii < len(s2); ii++ {
				if s1[ii] != s2[ii] {
					t.Logf("char %d: s1 %s s2 %s\n", ii, string(s1[ii]), string(s2[ii]))
					break
				}
			}
			t.Errorf("program output differs - interpreted: \n\n%v\n\n - compiled: \n\n%v\n\n", s1, s2)
		}
	}
}

func benchmarkTests() []*templateTest {
	var tests []*templateTest
	tests = append(tests, ftests...)
	tests = append(tests, compilerTests...)
	return tests
}

func BenchmarkExecute(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	templates := make([]*Template, len(tests))
	for ii, v := range tests {
		tmpl := parseText(b, v.tmpl)
		if tmpl == nil {
			b.Fatalf("can't parse %q", v.tmpl)
		}
		templates[ii] = tmpl
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range templates {
			v.Execute(&buf, tests[ii].data)
			buf.Reset()
		}
	}
}

func BenchmarkExecuteProgram(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	programs := make([]*Program, len(tests))
	for ii, v := range tests {
		tmpl := parseText(b, v.tmpl)
		if tmpl == nil {
			b.Fatalf("can't parse %q", v.tmpl)
		}
		pr, err := NewProgram(tmpl)
		if err != nil {
			b.Fatalf("can't compile %", v.tmpl)
		}
		programs[ii] = pr
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range programs {
			v.Execute(&buf, tests[ii].data)
			buf.Reset()
		}
	}
}

func benchmarkBig(b *testing.B, pr bool) {
	b.ReportAllocs()
	const name = "1.html"
	tmpl := parseTestTemplate(b, name)
	if tmpl == nil {
		return
	}
	var buf bytes.Buffer
	if pr {
		pr, err := NewProgram(tmpl)
		if err != nil {
			b.Fatalf("can't compile template %q: %s", name, err)
		}
		b.ResetTimer()
		for ii := 0; ii < b.N; ii++ {
			pr.Execute(&buf, nil)
		}
	} else {
		// Execute once to add the escaping
		tmpl.Execute(&buf, nil)
		b.ResetTimer()
		for ii := 0; ii < b.N; ii++ {
			tmpl.Execute(&buf, nil)
		}
	}
}

func BenchmarkBig(b *testing.B) {
	benchmarkBig(b, false)
}

func BenchmarkBigProgram(b *testing.B) {
	benchmarkBig(b, true)
}
<commit_msg>Add a couple of test to make sure dot is preseved in ranges<commit_after>package template

import (
	"bytes"
	"gnd.la/loaders"
	"gnd.la/template/assets"
	"testing"
)

type templateTest struct {
	tmpl   string
	data   interface{}
	result string
}

type testType struct {
}

func (t *testType) Foo() string {
	return "bar"
}

func (t *testType) Bar(s string) string {
	return "bared-" + s
}

var (
	ftests = []*templateTest{
		{"{{ $one := 1 }}{{ $two := 2 }}{{ $three := 3 }}{{ $one }}+{{ $two }}+{{ $three }}={{ add $one $two $three }}", nil, "1+2+3=6"},
		{"{{ add 2 3 }}", nil, "5"},
		{"{{ to_lower .foo }}", map[string]string{"foo": "BAR"}, "bar"},
		{"{{ to_upper .foo }}", map[string]string{"foo": "bar"}, "BAR"},
		{"{{ join .chars .sep }}", map[string]interface{}{"chars": []string{"a", "b", "c"}, "sep": ","}, "a,b,c"},
		{"{{ to_html .s }}", map[string]string{"s": "<foo\nbar"}, "<foo
bar"},
		{"{{ mult 2 1.1 }}", nil, "2.2"},
		{"{{ imult 2 1.1 }}", nil, "2"},
		{"{{ concat \"foo\" \"bar\" }}", nil, "foobar"},
		{"{{ concat (concat \"foo\" \"bar\") \"baz\" }}", nil, "foobarbaz"},
		{"{{ if divisible 5 2 }}1{{ else }}0{{ end }}", nil, "0"},
		{"{{ if divisible 4 2 }}1{{ else }}0{{ end }}", nil, "1"},
	}
	compilerTests = []*templateTest{
		{"{{ \"output\" | printf \"%s\" }}", nil, "output"},
		{"{{ call .foo }}", map[string]interface{}{"foo": func() string { return "bar" }}, "bar"},
		{"{{ .Foo }}", struct{ Foo string }{"bar"}, "bar"},
		{"{{ .Foo }}", &testType{}, "bar"},
		{"{{ .Bar \"this\" }}", &testType{}, "bared-this"},
		{"{{ .t.Bar .foo }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foo"},
		{"{{ .t.Bar (concat .foo \"bar\") }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foobar"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", map[string]string{"A": "yes"}, "yes"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", nil, "no"},
		{"{{ with .A }}{{ . }}{{ end }}", nil, ""},
		{"{{ range . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ . }}{{ end }}{{ . }}", []int{1, 2, 3}, "123[1 2 3]"},
		{"{{ range $idx, $el := . }}{{ $idx }}{{ $el }}{{ end }}", []int{1, 2, 3}, "011223"},
		{"{{ range $el := . }}{{ $el }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range $el := . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range $idx, $el := . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ else }}nope{{ end }}", nil, "nope"},
		{"{{ range $k, $v := . }}{{ $k }}={{ $v }}{{ end }}", map[string]int{"b": 2, "c": 3, "a": 1}, "a=1b=2c=3"},
	}
	compilerErrorTests = []*templateTest{
		{"{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:1:9: can't range over int"},
		{"{{ . }}\n{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:2:9: can't range over int"},
		{"{{ . }}\n{{ range .foo }}{{ else }}nope{{ end }}\n{{ range .bar }}{{ . }}{{ end }} ", map[string]interface{}{"foo": []int{}, "bar": ""}, "template.html:3:9: can't range over string"},
	}
)

func parseText(tb testing.TB, text string) *Template {
	loader := loaders.MapLoader(map[string][]byte{"template.html": []byte(text)})
	tmpl, err := Parse(loader, nil, "template.html")
	if err != nil {
		tb.Errorf("error parsing %q: %s", text, err)
	}
	return tmpl
}

func parseTestTemplate(tb testing.TB, name string) *Template {
	loader := loaders.FSLoader("_testdata")
	tmpl := New(loader, assets.NewManager(loader, ""))
	tmpl.Funcs(FuncMap{"t": func(s string) string { return s }})
	if err := tmpl.Parse(name); err != nil {
		tb.Errorf("error parsing %q: %s", name, err)
		return nil
	}
	return tmpl
}

func TestFunctions(t *testing.T) {
	for _, v := range ftests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompiler(t *testing.T) {
	var tests []*templateTest
	tests = append(tests, ftests...)
	tests = append(tests, compilerTests...)
	for _, v := range tests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		pr, err := NewProgram(tmpl)
		if err != nil {
			t.Errorf("error compiling %q: %s", v.tmpl, err)
			continue
		}
		var buf bytes.Buffer
		if err := pr.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompilerErrors(t *testing.T) {
	for _, v := range compilerErrorTests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		pr, err := NewProgram(tmpl)
		if err != nil {
			t.Errorf("error compiling %q: %s", v.tmpl, err)
			continue
		}
		var buf bytes.Buffer
		err = pr.Execute(&buf, v.data)
		if err == nil {
			t.Errorf("expecting an error when executing %q, got nil", v.tmpl)
			continue
		}
		if err.Error() != v.result {
			t.Logf("template is %q", v.tmpl)
			t.Errorf("expecting error %q, got %q", v.result, err.Error())
		}
	}
}

func TestBigTemplate(t *testing.T) {
	const name = "1.html"
	tmpl := parseTestTemplate(t, name)
	if tmpl != nil {
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, nil); err != nil {
			t.Errorf("error executing template %s: %s", name, err)
		}
	}
}

func TestCompileBigTemplate(t *testing.T) {
	const name = "1.html"
	tmpl := parseTestTemplate(t, name)
	if tmpl != nil {
		pr, err := NewProgram(tmpl)
		if err != nil {
			t.Fatalf("can't compile template %q: %s", name, err)
		}
		var buf1 bytes.Buffer
		if err := tmpl.Execute(&buf1, nil); err != nil {
			t.Errorf("error executing template %s: %s", name, err)
		}
		var buf2 bytes.Buffer
		if err := pr.Execute(&buf2, nil); err != nil {
			t.Errorf("error executing program %s: %s", name, err)
		}
		if buf1.String() != buf2.String() {
			s1 := buf1.String()
			s2 := buf2.String()
			t.Logf("len(s1) = %d, len(s2) = %d", len(s1), len(s2))
			for ii := 0; ii < len(s1) && ii < len(s2); ii++ {
				if s1[ii] != s2[ii] {
					t.Logf("char %d: s1 %s s2 %s\n", ii, string(s1[ii]), string(s2[ii]))
					break
				}
			}
			t.Errorf("program output differs - interpreted: \n\n%v\n\n - compiled: \n\n%v\n\n", s1, s2)
		}
	}
}

func benchmarkTests() []*templateTest {
	var tests []*templateTest
	tests = append(tests, ftests...)
	tests = append(tests, compilerTests...)
	return tests
}

func BenchmarkExecute(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	templates := make([]*Template, len(tests))
	for ii, v := range tests {
		tmpl := parseText(b, v.tmpl)
		if tmpl == nil {
			b.Fatalf("can't parse %q", v.tmpl)
		}
		templates[ii] = tmpl
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range templates {
			v.Execute(&buf, tests[ii].data)
			buf.Reset()
		}
	}
}

func BenchmarkExecuteProgram(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	programs := make([]*Program, len(tests))
	for ii, v := range tests {
		tmpl := parseText(b, v.tmpl)
		if tmpl == nil {
			b.Fatalf("can't parse %q", v.tmpl)
		}
		pr, err := NewProgram(tmpl)
		if err != nil {
			b.Fatalf("can't compile %", v.tmpl)
		}
		programs[ii] = pr
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range programs {
			v.Execute(&buf, tests[ii].data)
			buf.Reset()
		}
	}
}

func benchmarkBig(b *testing.B, pr bool) {
	b.ReportAllocs()
	const name = "1.html"
	tmpl := parseTestTemplate(b, name)
	if tmpl == nil {
		return
	}
	var buf bytes.Buffer
	if pr {
		pr, err := NewProgram(tmpl)
		if err != nil {
			b.Fatalf("can't compile template %q: %s", name, err)
		}
		b.ResetTimer()
		for ii := 0; ii < b.N; ii++ {
			pr.Execute(&buf, nil)
		}
	} else {
		// Execute once to add the escaping
		tmpl.Execute(&buf, nil)
		b.ResetTimer()
		for ii := 0; ii < b.N; ii++ {
			tmpl.Execute(&buf, nil)
		}
	}
}

func BenchmarkBig(b *testing.B) {
	benchmarkBig(b, false)
}

func BenchmarkBigProgram(b *testing.B) {
	benchmarkBig(b, true)
}
<|endoftext|>