<commit_before>package middleware

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
)

type (
	// CSRFConfig defines the config for CSRF middleware.
	CSRFConfig struct {
		// Key to create CSRF token.
		Secret []byte `json:"secret"`

		// TokenLookup is a string in the form of "<source>:<key>" that is used
		// to extract token from the request.
		// Optional. Default value "header:X-CSRF-Token".
		// Possible values:
		// - "header:<name>"
		// - "form:<name>"
		// - "query:<name>"
		TokenLookup string `json:"token_lookup"`

		// Context key to store generated CSRF token into context.
		// Optional. Default value "csrf".
		ContextKey string `json:"context_key"`

		// Name of the CSRF cookie. This cookie will store CSRF token.
		// Optional. Default value "csrf".
		CookieName string `json:"cookie_name"`

		// Domain of the CSRF cookie.
		// Optional. Default value none.
		CookieDomain string `json:"cookie_domain"`

		// Path of the CSRF cookie.
		// Optional. Default value none.
		CookiePath string `json:"cookie_path"`

		// Expiration time of the CSRF cookie.
		// Optional. Default value 24H.
		CookieExpires time.Time `json:"cookie_expires"`

		// Indicates if CSRF cookie is secure.
		CookieSecure bool `json:"cookie_secure"`
		// Optional. Default value false.

		// Indicates if CSRF cookie is HTTP only.
		// Optional. Default value false.
		CookieHTTPOnly bool `json:"cookie_http_only"`
	}

	// csrfTokenExtractor defines a function that takes `echo.Context` and returns
	// either a token or an error.
	csrfTokenExtractor func(echo.Context) (string, error)
)

var (
	// DefaultCSRFConfig is the default CSRF middleware config.
	DefaultCSRFConfig = CSRFConfig{
		TokenLookup:   "header:" + echo.HeaderXCSRFToken,
		ContextKey:    "csrf",
		CookieName:    "csrf",
		CookieExpires: time.Now().Add(24 * time.Hour),
	}
)

// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
func CSRF(secret []byte) echo.MiddlewareFunc {
	c := DefaultCSRFConfig
	c.Secret = secret
	return CSRFWithConfig(c)
}

// CSRFWithConfig returns a CSRF middleware from config.
// See `CSRF()`.
func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Secret == nil {
		panic("csrf secret must be provided")
	}
	if config.TokenLookup == "" {
		config.TokenLookup = DefaultCSRFConfig.TokenLookup
	}
	if config.ContextKey == "" {
		config.ContextKey = DefaultCSRFConfig.ContextKey
	}
	if config.CookieName == "" {
		config.CookieName = DefaultCSRFConfig.CookieName
	}
	if config.CookieExpires.IsZero() {
		config.CookieExpires = DefaultCSRFConfig.CookieExpires
	}

	// Initialize
	parts := strings.Split(config.TokenLookup, ":")
	extractor := csrfTokenFromHeader(parts[1])
	switch parts[0] {
	case "form":
		extractor = csrfTokenFromForm(parts[1])
	case "query":
		extractor = csrfTokenFromQuery(parts[1])
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			req := c.Request()

			// Set CSRF token
			salt, err := generateSalt(8)
			if err != nil {
				return err
			}
			token := generateCSRFToken(config.Secret, salt)
			c.Set(config.ContextKey, token)
			cookie := new(echo.Cookie)
			cookie.SetName(config.CookieName)
			cookie.SetValue(token)
			if config.CookiePath != "" {
				cookie.SetPath(config.CookiePath)
			}
			if config.CookieDomain != "" {
				cookie.SetDomain(config.CookieDomain)
			}
			cookie.SetExpires(config.CookieExpires)
			cookie.SetSecure(config.CookieSecure)
			cookie.SetHTTPOnly(config.CookieHTTPOnly)
			c.SetCookie(cookie)

			switch req.Method() {
			case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE:
			default:
				token, err := extractor(c)
				if err != nil {
					return err
				}
				ok, err := validateCSRFToken(token, config.Secret)
				if err != nil {
					return err
				}
				if !ok {
					return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
				}
			}
			return next(c)
		}
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided request header.
func csrfTokenFromHeader(header string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		return c.Request().Header().Get(header), nil
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided form parameter.
func csrfTokenFromForm(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.FormValue(param)
		if token == "" {
			return "", errors.New("empty csrf token in form param")
		}
		return token, nil
	}
}

// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the
// provided query parameter.
func csrfTokenFromQuery(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.QueryParam(param)
		if token == "" {
			return "", errors.New("empty csrf token in query param")
		}
		return token, nil
	}
}

func generateCSRFToken(secret, salt []byte) string {
	h := hmac.New(sha1.New, secret)
	h.Write(salt)
	return fmt.Sprintf("%s:%s", hex.EncodeToString(h.Sum(nil)), hex.EncodeToString(salt))
}

func validateCSRFToken(token string, secret []byte) (bool, error) {
	sep := strings.Index(token, ":")
	if sep < 0 {
		return false, nil
	}
	salt, err := hex.DecodeString(token[sep+1:])
	if err != nil {
		return false, err
	}
	return token == generateCSRFToken(secret, salt), nil
}

func generateSalt(len uint8) (salt []byte, err error) {
	salt = make([]byte, len)
	_, err = rand.Read(salt)
	return
}
<commit_msg>Fixed #584<commit_after>package middleware

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
)

type (
	// CSRFConfig defines the config for CSRF middleware.
	CSRFConfig struct {
		// Key to create CSRF token.
		Secret []byte `json:"secret"`

		// TokenLookup is a string in the form of "<source>:<key>" that is used
		// to extract token from the request.
		// Optional. Default value "header:X-CSRF-Token".
		// Possible values:
		// - "header:<name>"
		// - "form:<name>"
		// - "query:<name>"
		TokenLookup string `json:"token_lookup"`

		// Context key to store generated CSRF token into context.
		// Optional. Default value "csrf".
		ContextKey string `json:"context_key"`

		// Name of the CSRF cookie. This cookie will store CSRF token.
		// Optional. Default value "csrf".
		CookieName string `json:"cookie_name"`

		// Domain of the CSRF cookie.
		// Optional. Default value none.
		CookieDomain string `json:"cookie_domain"`

		// Path of the CSRF cookie.
		// Optional. Default value none.
		CookiePath string `json:"cookie_path"`

		// Max age (in seconds) of the CSRF cookie.
		// Optional. Default value 86400 (24hr).
		CookieMaxAge int `json:"cookie_max_age"`

		// Indicates if CSRF cookie is secure.
		CookieSecure bool `json:"cookie_secure"`
		// Optional. Default value false.

		// Indicates if CSRF cookie is HTTP only.
		// Optional. Default value false.
		CookieHTTPOnly bool `json:"cookie_http_only"`
	}

	// csrfTokenExtractor defines a function that takes `echo.Context` and returns
	// either a token or an error.
	csrfTokenExtractor func(echo.Context) (string, error)
)

var (
	// DefaultCSRFConfig is the default CSRF middleware config.
	DefaultCSRFConfig = CSRFConfig{
		TokenLookup:  "header:" + echo.HeaderXCSRFToken,
		ContextKey:   "csrf",
		CookieName:   "csrf",
		CookieMaxAge: 86400,
	}
)

// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
func CSRF(secret []byte) echo.MiddlewareFunc {
	c := DefaultCSRFConfig
	c.Secret = secret
	return CSRFWithConfig(c)
}

// CSRFWithConfig returns a CSRF middleware from config.
// See `CSRF()`.
func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Secret == nil {
		panic("csrf secret must be provided")
	}
	if config.TokenLookup == "" {
		config.TokenLookup = DefaultCSRFConfig.TokenLookup
	}
	if config.ContextKey == "" {
		config.ContextKey = DefaultCSRFConfig.ContextKey
	}
	if config.CookieName == "" {
		config.CookieName = DefaultCSRFConfig.CookieName
	}
	if config.CookieMaxAge == 0 {
		config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
	}

	// Initialize
	parts := strings.Split(config.TokenLookup, ":")
	extractor := csrfTokenFromHeader(parts[1])
	switch parts[0] {
	case "form":
		extractor = csrfTokenFromForm(parts[1])
	case "query":
		extractor = csrfTokenFromQuery(parts[1])
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			req := c.Request()

			// Set CSRF token
			salt, err := generateSalt(8)
			if err != nil {
				return err
			}
			token := generateCSRFToken(config.Secret, salt)
			c.Set(config.ContextKey, token)
			cookie := new(echo.Cookie)
			cookie.SetName(config.CookieName)
			cookie.SetValue(token)
			if config.CookiePath != "" {
				cookie.SetPath(config.CookiePath)
			}
			if config.CookieDomain != "" {
				cookie.SetDomain(config.CookieDomain)
			}
			cookie.SetExpires(time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second))
			cookie.SetSecure(config.CookieSecure)
			cookie.SetHTTPOnly(config.CookieHTTPOnly)
			c.SetCookie(cookie)

			switch req.Method() {
			case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE:
			default:
				token, err := extractor(c)
				if err != nil {
					return err
				}
				ok, err := validateCSRFToken(token, config.Secret)
				if err != nil {
					return err
				}
				if !ok {
					return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
				}
			}
			return next(c)
		}
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided request header.
func csrfTokenFromHeader(header string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		return c.Request().Header().Get(header), nil
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided form parameter.
func csrfTokenFromForm(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.FormValue(param)
		if token == "" {
			return "", errors.New("empty csrf token in form param")
		}
		return token, nil
	}
}

// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the
// provided query parameter.
func csrfTokenFromQuery(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.QueryParam(param)
		if token == "" {
			return "", errors.New("empty csrf token in query param")
		}
		return token, nil
	}
}

func generateCSRFToken(secret, salt []byte) string {
	h := hmac.New(sha1.New, secret)
	h.Write(salt)
	return fmt.Sprintf("%s:%s", hex.EncodeToString(h.Sum(nil)), hex.EncodeToString(salt))
}

func validateCSRFToken(token string, secret []byte) (bool, error) {
	sep := strings.Index(token, ":")
	if sep < 0 {
		return false, nil
	}
	salt, err := hex.DecodeString(token[sep+1:])
	if err != nil {
		return false, err
	}
	return token == generateCSRFToken(secret, salt), nil
}

func generateSalt(len uint8) (salt []byte, err error) {
	salt = make([]byte, len)
	_, err = rand.Read(salt)
	return
}
<|endoftext|>


<commit_before>package controllers

import (
	"reflect"

	"github.com/hoop33/tbdotd/app/models"
	"github.com/revel/revel"
)

type App struct {
	*revel.Controller
}

var vendors = []models.Vendor{
	{
		Name:    "Apress",
		HomeUrl: "http://www.apress.com/",
		DealUrl: "http://www.apress.com/index.php/dailydeals/index/rss",
	},
}

func (c App) Index() revel.Result {
	results := make(chan models.Deal)

	for _, vendor := range vendors {
		go func() {
			var deal models.Deal
			method := reflect.ValueOf(&vendor).MethodByName(vendor.Name)
			if method.IsValid() {
				values := method.Call([]reflect.Value{})
				deal = values[0].Interface().(models.Deal)
			} else {
				deal = models.Deal{
					Vendor:   &vendor,
					Title:    "Not Found",
					ImageUrl: "",
					Url:      "#",
				}
			}
			results <- deal
		}()
	}

	deals := []models.Deal{}
	for _, _ = range vendors {
		deals = append(deals, <-results)
	}

	return c.Render(deals)
}
<commit_msg>Fix: Pass vendor to go func<commit_after>package controllers

import (
	"reflect"
	"strings"

	"github.com/hoop33/tbdotd/app/models"
	"github.com/revel/revel"
)

type App struct {
	*revel.Controller
}

var vendors = []models.Vendor{
	{
		Name:    "Apress",
		HomeUrl: "http://www.apress.com/",
		DealUrl: "http://www.apress.com/index.php/dailydeals/index/rss",
	},
}

func (c App) Index() revel.Result {
	results := make(chan models.Deal)

	for _, vendor := range vendors {
		go func(vendor models.Vendor) {
			var deal models.Deal
			method := reflect.ValueOf(&vendor).MethodByName(strings.Replace(vendor.Name, "'", "", -1))
			if method.IsValid() {
				values := method.Call([]reflect.Value{})
				deal = values[0].Interface().(models.Deal)
			} else {
				deal = models.Deal{
					Vendor:   &vendor,
					Title:    "Not Found",
					ImageUrl: "",
					Url:      vendor.HomeUrl,
				}
			}
			results <- deal
		}(vendor)
	}

	// TODO should we use a WaitGroup here instead?
	deals := []models.Deal{}
	for _, _ = range vendors {
		deals = append(deals, <-results)
	}

	return c.Render(deals)
}
<|endoftext|>


<commit_before>package migrations

import (
	"crypto/md5"
	"database/sql"
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"time"
)

type Con interface {
	QueryRow(query string, args ...interface{}) *sql.Row
	Query(query string, args ...interface{}) (*sql.Rows, error)
	Prepare(query string) (*sql.Stmt, error)
	Exec(query string, args ...interface{}) (sql.Result, error)
}

const createMigrationsSql = `
  CREATE TABLE migrations (idx INTEGER PRIMARY KEY NOT NULL, md5 UUID NOT NULL, statement VARCHAR NOT NULL, created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL)
`

func New(migrations ...interface{}) *Migrations {
	return &Migrations{steps: migrations}
}

type logger interface {
	Printf(string, ...interface{})
}

type Migrations struct {
	Logger logger
	steps  []interface{}
}

func (list Migrations) Execute(db *sql.DB) error {
	tx, e := db.Begin()
	if e != nil {
		return e
	}

	e = list.ExecuteTx(tx)

	if e != nil {
		tx.Rollback()
		return e
	}
	return tx.Commit()
}

func (list Migrations) ExecuteTx(tx *sql.Tx) error {
	started := time.Now()
	if _, err := list.setup(tx); err != nil {
		return err
	}

	migrations, err := list.migrations()
	if err != nil {
		return err
	}
	for _, m := range migrations {
		if err := m.Execute(tx); err != nil {
			return err
		}
	}
	if list.Logger != nil {
		list.Logger.Printf("migrated in %.06f", time.Since(started).Seconds())
	}
	return nil
}

func (list Migrations) migrations() (out []*Migration, err error) {
	for idx, i := range list.steps {
		if m, err := newMigration(idx+1, i); err != nil {
			return nil, err
		} else {
			m.Logger = list.Logger
			out = append(out, m)
		}
	}
	return out, nil
}

func newMigration(idx int, statement interface{}) (*Migration, error) {
	m := &Migration{Idx: idx}

	switch casted := statement.(type) {
	case string:
		m.Statement = casted
	case fmt.Stringer:
		m.Statement = casted.String()
	case func(Con) error:
		m.Statement = runtime.FuncForPC(reflect.ValueOf(statement).Pointer()).Name()
		m.Func = casted
	default:
		return nil, fmt.Errorf("type %T not supported", casted)
	}
	return m, nil
}

type Migration struct {
	Idx       int
	Statement string
	Func      func(Con) error
	Logger    logger
}

func (list Migrations) setup(tx *sql.Tx) (sql.Result, error) {
	row := tx.QueryRow("SELECT COUNT(1) FROM pg_tables WHERE schemaname = $1 AND tablename = $2", "public", "migrations")
	var cnt int
	e := row.Scan(&cnt)
	if e != nil {
		return nil, e
	}

	if cnt == 0 {
		return tx.Exec(createMigrationsSql)
	}
	return nil, nil
}

func (m *Migration) log(t string, dur time.Duration) {
	if m.Logger != nil {
		out := []string{}
		lines := strings.Split(strings.TrimSpace(m.Statement), "\n")
		for _, l := range lines {
			out = append(out, strings.TrimSpace(l))
		}
		msg := fmt.Sprintf("%s: migration %d %q %q", t, m.Idx, m.checksum(), strings.Join(strings.Fields(strings.Join(out, " ")), " "))
		if dur != 0 {
			msg += fmt.Sprintf(" [%.06f]", dur.Seconds())
		}
		m.Logger.Printf(msg)
	}
}

func (m *Migration) checksum() string {
	return fmt.Sprintf("%x", md5.Sum([]byte(m.Statement)))
}

func (m *Migration) Execute(tx *sql.Tx) error {
	rows, err := tx.Query("SELECT md5, statement FROM migrations where idx = $1", m.Idx)
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		var cs, statement string
		if err := rows.Scan(&cs, &statement); err != nil {
			return err
		}
		cs = strings.Replace(cs, "-", "", -1)
		if statement == m.Statement {
			m.log("SKIP", 0)
			return nil
		} else {
			return fmt.Errorf("MIGRATION MISMATCH:\n<<<<<<< code migration %d\n%q\n=======\n%q\n>>>>>>> db migration\n", m.Idx, m.Statement, statement)
		}
	}
	started := time.Now()
	if m.Func != nil {
		if err := m.Func(tx); err != nil {
			return err
		}
	} else {
		if _, err := tx.Exec(m.Statement); err != nil {
			return err
		}
	}
	_, err = tx.Exec("INSERT INTO migrations (idx, md5, statement, created_at) VALUES ($1, $2, $3, $4)", m.Idx, m.checksum(), m.Statement, time.Now().UTC().Format(time.RFC3339Nano))
	if err != nil {
		return err
	}
	m.log("EXEC", time.Since(started))
	return err
}
<commit_msg>use interface type for Tx<commit_after>package migrations

import (
	"crypto/md5"
	"database/sql"
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"time"
)

type Con interface {
	QueryRow(query string, args ...interface{}) *sql.Row
	Query(query string, args ...interface{}) (*sql.Rows, error)
	Prepare(query string) (*sql.Stmt, error)
	Exec(query string, args ...interface{}) (sql.Result, error)
}

const createMigrationsSql = `
  CREATE TABLE migrations (idx INTEGER PRIMARY KEY NOT NULL, md5 UUID NOT NULL, statement VARCHAR NOT NULL, created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL)
`

func New(migrations ...interface{}) *Migrations {
	return &Migrations{steps: migrations}
}

type logger interface {
	Printf(string, ...interface{})
}

type Migrations struct {
	Logger logger
	steps  []interface{}
}

func (list Migrations) Execute(db *sql.DB) error {
	tx, e := db.Begin()
	if e != nil {
		return e
	}

	e = list.ExecuteTx(tx)

	if e != nil {
		tx.Rollback()
		return e
	}
	return tx.Commit()
}

type Tx interface {
	Con
	Commit() error
	Rollback() error
}

func (list Migrations) ExecuteTx(tx Tx) error {
	started := time.Now()
	if _, err := list.setup(tx); err != nil {
		return err
	}

	migrations, err := list.migrations()
	if err != nil {
		return err
	}
	for _, m := range migrations {
		if err := m.Execute(tx); err != nil {
			return err
		}
	}
	if list.Logger != nil {
		list.Logger.Printf("migrated in %.06f", time.Since(started).Seconds())
	}
	return nil
}

func (list Migrations) migrations() (out []*Migration, err error) {
	for idx, i := range list.steps {
		if m, err := newMigration(idx+1, i); err != nil {
			return nil, err
		} else {
			m.Logger = list.Logger
			out = append(out, m)
		}
	}
	return out, nil
}

func newMigration(idx int, statement interface{}) (*Migration, error) {
	m := &Migration{Idx: idx}

	switch casted := statement.(type) {
	case string:
		m.Statement = casted
	case fmt.Stringer:
		m.Statement = casted.String()
	case func(Con) error:
		m.Statement = runtime.FuncForPC(reflect.ValueOf(statement).Pointer()).Name()
		m.Func = casted
	default:
		return nil, fmt.Errorf("type %T not supported", casted)
	}
	return m, nil
}

type Migration struct {
	Idx       int
	Statement string
	Func      func(Con) error
	Logger    logger
}

func (list Migrations) setup(tx Tx) (sql.Result, error) {
	row := tx.QueryRow("SELECT COUNT(1) FROM pg_tables WHERE schemaname = $1 AND tablename = $2", "public", "migrations")
	var cnt int
	e := row.Scan(&cnt)
	if e != nil {
		return nil, e
	}

	if cnt == 0 {
		return tx.Exec(createMigrationsSql)
	}
	return nil, nil
}

func (m *Migration) log(t string, dur time.Duration) {
	if m.Logger != nil {
		out := []string{}
		lines := strings.Split(strings.TrimSpace(m.Statement), "\n")
		for _, l := range lines {
			out = append(out, strings.TrimSpace(l))
		}
		msg := fmt.Sprintf("%s: migration %d %q %q", t, m.Idx, m.checksum(), strings.Join(strings.Fields(strings.Join(out, " ")), " "))
		if dur != 0 {
			msg += fmt.Sprintf(" [%.06f]", dur.Seconds())
		}
		m.Logger.Printf(msg)
	}
}

func (m *Migration) checksum() string {
	return fmt.Sprintf("%x", md5.Sum([]byte(m.Statement)))
}

func (m *Migration) Execute(tx Tx) error {
	rows, err := tx.Query("SELECT md5, statement FROM migrations where idx = $1", m.Idx)
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		var cs, statement string
		if err := rows.Scan(&cs, &statement); err != nil {
			return err
		}
		cs = strings.Replace(cs, "-", "", -1)
		if statement == m.Statement {
			m.log("SKIP", 0)
			return nil
		} else {
			return fmt.Errorf("MIGRATION MISMATCH:\n<<<<<<< code migration %d\n%q\n=======\n%q\n>>>>>>> db migration\n", m.Idx, m.Statement, statement)
		}
	}
	started := time.Now()
	if m.Func != nil {
		if err := m.Func(tx); err != nil {
			return err
		}
	} else {
		if _, err := tx.Exec(m.Statement); err != nil {
			return err
		}
	}
	_, err = tx.Exec("INSERT INTO migrations (idx, md5, statement, created_at) VALUES ($1, $2, $3, $4)", m.Idx, m.checksum(), m.Statement, time.Now().UTC().Format(time.RFC3339Nano))
	if err != nil {
		return err
	}
	m.log("EXEC", time.Since(started))
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
)

func main() {
	flatsite := &FlatSite{}
	flatsite.OutputDir = getConf("OUTPUT_DIR", "www")
	flatsite.InputDir = getConf("INPUT_DIR", "tmpl")

	// walk filesystem, load all templates
	flatsite.Templates = template.New("templates")
	flatsite.Templates.Funcs(funcs)
	filepath.Walk(flatsite.InputDir, func(name string, info os.FileInfo, err error) error {
		isHidden := []rune(path.Base(name))[0] == '.'
		if info.IsDir() {
			if isHidden {
				return filepath.SkipDir
			} else {
				return nil
			}
		} else {
			if isHidden {
				return nil
			} else {
				if content, err := ioutil.ReadFile(name); err == nil {
					name, _ = filepath.Rel(flatsite.InputDir, name)
					flatsite.Templates.New(name).Parse(string(content))
				} else {
					fmt.Fprintf(os.Stderr, "%s \"%s\": %s\n", "failed to read template file", name, err)
				}
			}
		}
		return nil
	})

	// generate and output templates
	fmt.Printf("Generating public templates:\n")
	for _, v := range flatsite.Templates.Templates() {
		pth := NewPath(v.Name())
		if pth.Chunks[0] != "output" {
			continue
		}
		fmt.Fprintf(os.Stdout, "\t%s : %#v\n", v.Name(), v)
		page := NewMap()
		page.Set("path", Path{Chunks: pth.Chunks[1:]})
		if err := flatsite.generateFile(v, page); err != nil {
			fmt.Fprintf(os.Stderr, "\t\t%s \"%s\": %s\n", "failed to generate from template", v.Name(), err)
		}
	}
}

func getConf(key string, defalt string) string {
	env := os.Getenv(key)
	if env == "" {
		return defalt
	} else {
		return env
	}
}

type FlatSite struct {
	OutputDir string
	InputDir  string
	Templates *template.Template
}

func NewPath(pth string) Path {
	return Path{
		Chunks: strings.Split(pth, string(os.PathSeparator)),
	}
}

type Path struct {
	Chunks []string
}

func (pth Path) String() string {
	return strings.Join(pth.Chunks, "/")
}

func (pth Path) Paths() []Path {
	n := len(pth.Chunks)
	v := make([]Path, n)
	for i := 0; i < n; i++ {
		v[i] = Path{Chunks: pth.Chunks[0 : i+1]}
	}
	return v
}

func (pth Path) LastChunk() string {
	return pth.Chunks[len(pth.Chunks)-1]
}

type Map map[string]interface{}

func NewMap() Map {
	m := make(map[string]interface{})
	m[""] = ""
	return m
}

func (m Map) Set(key string, value interface{}) string {
	if _, ok := m[""]; len(m) == 1 && ok {
		delete(m, "")
	}
	m[key] = value
	return ""
}

func (m Map) Get(key string) interface{} {
	return m[key]
}

var funcs = template.FuncMap{
	"eq": func(a interface{}, b interface{}) bool {
		return a == b
	},
	"NewMap": func() Map {
		return NewMap()
	},
	"Set": func(m Map, key string, value interface{}) interface{} {
		m.Set(key, value)
		return value
	},
	"Nul": func(_ ...interface{}) string {
		return ""
	},
}

func (flatsite *FlatSite) generateFile(tmpl *template.Template, page Map) error {
	outputPath := filepath.Join(page.Get("path").(Path).Chunks...)
	outputPathFull := filepath.Join(flatsite.OutputDir, outputPath)
	os.MkdirAll(path.Dir(outputPathFull), 0755)
	w, err := os.Create(outputPathFull)
	if err != nil {
		return fmt.Errorf("error creating static file %s: %s", outputPath, err)
	}
	defer w.Close()

	return tmpl.Execute(w, &page)
}
<commit_msg>quote formatting is good.<commit_after>package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
)

func main() {
	flatsite := &FlatSite{}
	flatsite.OutputDir = getConf("OUTPUT_DIR", "www")
	flatsite.InputDir = getConf("INPUT_DIR", "tmpl")

	// walk filesystem, load all templates
	flatsite.Templates = template.New("templates")
	flatsite.Templates.Funcs(funcs)
	filepath.Walk(flatsite.InputDir, func(name string, info os.FileInfo, err error) error {
		isHidden := []rune(path.Base(name))[0] == '.'
		if info.IsDir() {
			if isHidden {
				return filepath.SkipDir
			} else {
				return nil
			}
		} else {
			if isHidden {
				return nil
			} else {
				if content, err := ioutil.ReadFile(name); err == nil {
					name, _ = filepath.Rel(flatsite.InputDir, name)
					flatsite.Templates.New(name).Parse(string(content))
				} else {
					fmt.Fprintf(os.Stderr, "%s \"%s\": %s\n", "failed to read template file", name, err)
				}
			}
		}
		return nil
	})

	// generate and output templates
	fmt.Printf("Generating public templates:\n")
	for _, v := range flatsite.Templates.Templates() {
		pth := NewPath(v.Name())
		if pth.Chunks[0] != "output" {
			continue
		}
		fmt.Fprintf(os.Stdout, "\t%s : %#v\n", v.Name(), v)
		page := NewMap()
		page.Set("path", Path{Chunks: pth.Chunks[1:]})
		if err := flatsite.generateFile(v, page); err != nil {
			fmt.Fprintf(os.Stderr, "\t\t%s \"%s\": %s\n", "failed to generate from template", v.Name(), err)
		}
	}
}

func getConf(key string, defalt string) string {
	env := os.Getenv(key)
	if env == "" {
		return defalt
	} else {
		return env
	}
}

type FlatSite struct {
	OutputDir string
	InputDir  string
	Templates *template.Template
}

func NewPath(pth string) Path {
	return Path{
		Chunks: strings.Split(pth, string(os.PathSeparator)),
	}
}

type Path struct {
	Chunks []string
}

func (pth Path) String() string {
	return strings.Join(pth.Chunks, "/")
}

func (pth Path) Paths() []Path {
	n := len(pth.Chunks)
	v := make([]Path, n)
	for i := 0; i < n; i++ {
		v[i] = Path{Chunks: pth.Chunks[0 : i+1]}
	}
	return v
}

func (pth Path) LastChunk() string {
	return pth.Chunks[len(pth.Chunks)-1]
}

type Map map[string]interface{}

func NewMap() Map {
	m := make(map[string]interface{})
	m[""] = ""
	return m
}

func (m Map) Set(key string, value interface{}) string {
	if _, ok := m[""]; len(m) == 1 && ok {
		delete(m, "")
	}
	m[key] = value
	return ""
}

func (m Map) Get(key string) interface{} {
	return m[key]
}

var funcs = template.FuncMap{
	"eq": func(a interface{}, b interface{}) bool {
		return a == b
	},
	"NewMap": func() Map {
		return NewMap()
	},
	"Set": func(m Map, key string, value interface{}) interface{} {
		m.Set(key, value)
		return value
	},
	"Nul": func(_ ...interface{}) string {
		return ""
	},
}

func (flatsite *FlatSite) generateFile(tmpl *template.Template, page Map) error {
	outputPath := filepath.Join(page.Get("path").(Path).Chunks...)
	outputPathFull := filepath.Join(flatsite.OutputDir, outputPath)
	os.MkdirAll(path.Dir(outputPathFull), 0755)
	w, err := os.Create(outputPathFull)
	if err != nil {
		return fmt.Errorf("error creating static file %q: %s", outputPath, err)
	}
	defer w.Close()

	return tmpl.Execute(w, &page)
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import (
	"context"
)

// ArangoSearchView provides access to the information of a view.
// Views are only available in ArangoDB 3.4 and higher.
type ArangoSearchView interface {
	// Include generic View functions
	View

	// Properties fetches extended information about the view.
	Properties(ctx context.Context) (ArangoSearchViewProperties, error)

	// SetProperties changes properties of the view.
	SetProperties(ctx context.Context, options ArangoSearchViewProperties) error
}

// ArangoSearchViewProperties contains properties an an ArangoSearch view.
type ArangoSearchViewProperties struct {
	// CleanupIntervalStep specifies the minimum number of commits to wait between
	// removing unused files in the data directory.
	// Defaults to 10.
	// Use 0 to disable waiting.
	// For the case where the consolidation policies merge segments often
	// (i.e. a lot of commit+consolidate), a lower value will cause a lot of
	// disk space to be wasted.
	// For the case where the consolidation policies rarely merge segments
	// (i.e. few inserts/deletes), a higher value will impact performance
	// without any added benefits.
	CleanupIntervalStep *int64 `json:"cleanupIntervalStep,omitempty"`
	// ConsolidationInterval specifies the minimum number of milliseconds that must be waited
	// between committing index data changes and making them visible to queries.
	// Defaults to 60000.
	// Use 0 to disable.
	// For the case where there are a lot of inserts/updates, a lower value,
	// until commit, will cause the index not to account for them and memory usage
	// would continue to grow.
	// For the case where there are a few inserts/updates, a higher value will
	// impact performance and waste disk space for each commit call without
	// any added benefits.
	ConsolidationInterval *int64 `json:"consolidationIntervalMsec,omitempty"`
	// ConsolidationPolicy specifies thresholds for consolidation.
	ConsolidationPolicy *ArangoSearchConsolidationPolicy `json:"consolidationPolicy,omitempty"`

	// WriteBufferIdel specifies the maximum number of writers (segments) cached in the pool.
	// 0 value turns off caching, default value is 64.
	WriteBufferIdel *int64 `json:"writebufferIdle,omitempty"`

	// WriteBufferActive specifies the maximum number of concurrent active writers (segments) performs (a transaction).
	// Other writers (segments) are wait till current active writers (segments) finish.
	// 0 value turns off this limit and used by default.
	WriteBufferActive *int64 `json:"writebufferActive,omitempty"`

	// WriteBufferSizeMax specifies maximum memory byte size per writer (segment) before a writer (segment) flush is triggered.
	// 0 value turns off this limit fon any writer (buffer) and will be flushed only after a period defined for special thread during ArangoDB server startup.
	// 0 value should be used with carefully due to high potential memory consumption.
	WriteBufferSizeMax *int64 `json:"writebufferSizeMax,omitempty"`

	// Links contains the properties for how individual collections
	// are indexed in thie view.
	// The key of the map are collection names.
	Links ArangoSearchLinks `json:"links,omitempty"`
}

// ArangoSearchConsolidationPolicyType strings for consolidation types
type ArangoSearchConsolidationPolicyType string

const (
	// ArangoSearchConsolidationPolicyTypeTier consolidate based on segment byte size and live document count as dictated by the customization attributes.
	ArangoSearchConsolidationPolicyTypeTier ArangoSearchConsolidationPolicyType = "tier"
	// ArangoSearchConsolidationPolicyTypeBytesAccum consolidate if and only if ({threshold} range [0.0, 1.0])
	// {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes,
	// i.e. the sum of all candidate segment's byte size is less than the total segment byte size multiplied by the {threshold}.
	ArangoSearchConsolidationPolicyTypeBytesAccum ArangoSearchConsolidationPolicyType = "bytes_accum"
)

// ArangoSearchConsolidationPolicy holds threshold values specifying when to
// consolidate view data.
// Semantics of the values depend on where they are used.
type ArangoSearchConsolidationPolicy struct {
	// Type returns the type of the ConsolidationPolicy. This interface can then be casted to the corresponding ArangoSearchConsolidationPolicy* struct.
	Type ArangoSearchConsolidationPolicyType

	ArangoSearchConsolidationPolicyBytesAccum
	ArangoSearchConsolidationPolicyTier
}

// ArangoSearchConsolidationPolicyBytesAccum contains fields used for ArangoSearchConsolidationPolicyTypeBytesAccum
type ArangoSearchConsolidationPolicyBytesAccum struct {
	// Threshold, see ArangoSearchConsolidationTypeBytesAccum
	Threshold *float64 `json:"threshold,omitempty"`
}

// ArangoSearchConsolidationPolicyTier contains fields used for ArangoSearchConsolidationPolicyTypeTier
type ArangoSearchConsolidationPolicyTier struct {
	// MinSegments specifies the minimum number of segments that will be evaluated as candidates for consolidation.
	MinSegments *int64 `json:"minSegments,omitempty"`
	// MaxSegments specifies the maximum number of segments that will be evaluated as candidates for consolidation.
	MaxSegments *int64 `json:"maxSegments,omitempty"`
	// SegmentsBytesMax specifies the maxinum allowed size of all consolidated segments in bytes.
	SegmentsBytesMax *int64 `json:"segmentsBytesMax,omitempty"`
	// SegmentsBytesFloor defines the value (in bytes) to treat all smaller segments as equal for consolidation selection.
	SegmentsBytesFloor *int64 `json:"segmentsBytesFloor,omitempty"`
	// Lookahead specifies the number of additionally searched tiers except initially chosen candidated based on min_segments,
	// max_segments, segments_bytes_max, segments_bytes_floor with respect to defined values.
	// Default value falls to integer_traits<size_t>::const_max (in C++ source code).
	Lookahead *int64 `json:"lookahead,omitempty"`
}

// ArangoSearchLinks is a strongly typed map containing links between a
// collection and a view.
// The keys in the map are collection names.
type ArangoSearchLinks map[string]ArangoSearchElementProperties

// ArangoSearchFields is a strongly typed map containing properties per field.
// The keys in the map are field names.
type ArangoSearchFields map[string]ArangoSearchElementProperties

// ArangoSearchElementProperties contains properties that specify how an element
// is indexed in an ArangoSearch view.
// Note that this structure is recursive. Settings not specified (nil)
// at a given level will inherit their setting from a lower level.
type ArangoSearchElementProperties struct {
	// The list of analyzers to be used for indexing of string values. Defaults to ["identify"].
	Analyzers []string `json:"analyzers,omitempty"`
	// If set to true, all fields of this element will be indexed. Defaults to false.
	IncludeAllFields *bool `json:"includeAllFields,omitempty"`
	// If set to true, values in a listed are treated as separate values. Defaults to false.
	TrackListPositions *bool `json:"trackListPositions,omitempty"`
	// This values specifies how the view should track values.
	StoreValues ArangoSearchStoreValues `json:"storeValues,omitempty"`
	// Fields contains the properties for individual fields of the element.
	// The key of the map are field names.
	Fields ArangoSearchFields `json:"fields,omitempty"`
}

// ArangoSearchStoreValues is the type of the StoreValues option of an ArangoSearch element.
type ArangoSearchStoreValues string

const (
	// ArangoSearchStoreValuesNone specifies that a view should not store values.
	ArangoSearchStoreValuesNone ArangoSearchStoreValues = "none"
	// ArangoSearchStoreValuesID specifies that a view should only store
	// information about value presence, to allow use of the EXISTS() function.
	ArangoSearchStoreValuesID ArangoSearchStoreValues = "id"
)
<commit_msg>Added missing annotation for json. (#166)<commit_after>//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import (
	"context"
)

// ArangoSearchView provides access to the information of a view.
// Views are only available in ArangoDB 3.4 and higher.
type ArangoSearchView interface {
	// Include generic View functions
	View

	// Properties fetches extended information about the view.
	Properties(ctx context.Context) (ArangoSearchViewProperties, error)

	// SetProperties changes properties of the view.
	SetProperties(ctx context.Context, options ArangoSearchViewProperties) error
}

// ArangoSearchViewProperties contains properties an an ArangoSearch view.
type ArangoSearchViewProperties struct {
	// CleanupIntervalStep specifies the minimum number of commits to wait between
	// removing unused files in the data directory.
	// Defaults to 10.
	// Use 0 to disable waiting.
	// For the case where the consolidation policies merge segments often
	// (i.e. a lot of commit+consolidate), a lower value will cause a lot of
	// disk space to be wasted.
	// For the case where the consolidation policies rarely merge segments
	// (i.e. few inserts/deletes), a higher value will impact performance
	// without any added benefits.
	CleanupIntervalStep *int64 `json:"cleanupIntervalStep,omitempty"`
	// ConsolidationInterval specifies the minimum number of milliseconds that must be waited
	// between committing index data changes and making them visible to queries.
	// Defaults to 60000.
	// Use 0 to disable.
	// For the case where there are a lot of inserts/updates, a lower value,
	// until commit, will cause the index not to account for them and memory usage
	// would continue to grow.
	// For the case where there are a few inserts/updates, a higher value will
	// impact performance and waste disk space for each commit call without
	// any added benefits.
	ConsolidationInterval *int64 `json:"consolidationIntervalMsec,omitempty"`
	// ConsolidationPolicy specifies thresholds for consolidation.
	ConsolidationPolicy *ArangoSearchConsolidationPolicy `json:"consolidationPolicy,omitempty"`

	// WriteBufferIdel specifies the maximum number of writers (segments) cached in the pool.
	// 0 value turns off caching, default value is 64.
	WriteBufferIdel *int64 `json:"writebufferIdle,omitempty"`

	// WriteBufferActive specifies the maximum number of concurrent active writers (segments) performs (a transaction).
	// Other writers (segments) are wait till current active writers (segments) finish.
	// 0 value turns off this limit and used by default.
	WriteBufferActive *int64 `json:"writebufferActive,omitempty"`

	// WriteBufferSizeMax specifies maximum memory byte size per writer (segment) before a writer (segment) flush is triggered.
	// 0 value turns off this limit fon any writer (buffer) and will be flushed only after a period defined for special thread during ArangoDB server startup.
	// 0 value should be used with carefully due to high potential memory consumption.
	WriteBufferSizeMax *int64 `json:"writebufferSizeMax,omitempty"`

	// Links contains the properties for how individual collections
	// are indexed in thie view.
	// The key of the map are collection names.
	Links ArangoSearchLinks `json:"links,omitempty"`
}

// ArangoSearchConsolidationPolicyType strings for consolidation types
type ArangoSearchConsolidationPolicyType string

const (
	// ArangoSearchConsolidationPolicyTypeTier consolidate based on segment byte size and live document count as dictated by the customization attributes.
	ArangoSearchConsolidationPolicyTypeTier ArangoSearchConsolidationPolicyType = "tier"
	// ArangoSearchConsolidationPolicyTypeBytesAccum consolidate if and only if ({threshold} range [0.0, 1.0])
	// {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes,
	// i.e. the sum of all candidate segment's byte size is less than the total segment byte size multiplied by the {threshold}.
	ArangoSearchConsolidationPolicyTypeBytesAccum ArangoSearchConsolidationPolicyType = "bytes_accum"
)

// ArangoSearchConsolidationPolicy holds threshold values specifying when to
// consolidate view data.
// Semantics of the values depend on where they are used.
type ArangoSearchConsolidationPolicy struct {
	// Type returns the type of the ConsolidationPolicy. This interface can then be casted to the corresponding ArangoSearchConsolidationPolicy* struct.
	Type ArangoSearchConsolidationPolicyType `json:"type,omitempty"`

	ArangoSearchConsolidationPolicyBytesAccum
	ArangoSearchConsolidationPolicyTier
}

// ArangoSearchConsolidationPolicyBytesAccum contains fields used for ArangoSearchConsolidationPolicyTypeBytesAccum
type ArangoSearchConsolidationPolicyBytesAccum struct {
	// Threshold, see ArangoSearchConsolidationTypeBytesAccum
	Threshold *float64 `json:"threshold,omitempty"`
}

// ArangoSearchConsolidationPolicyTier contains fields used for ArangoSearchConsolidationPolicyTypeTier
type ArangoSearchConsolidationPolicyTier struct {
	// MinSegments specifies the minimum number of segments that will be evaluated as candidates for consolidation.
	MinSegments *int64 `json:"minSegments,omitempty"`
	// MaxSegments specifies the maximum number of segments that will be evaluated as candidates for consolidation.
	MaxSegments *int64 `json:"maxSegments,omitempty"`
	// SegmentsBytesMax specifies the maxinum allowed size of all consolidated segments in bytes.
	SegmentsBytesMax *int64 `json:"segmentsBytesMax,omitempty"`
	// SegmentsBytesFloor defines the value (in bytes) to treat all smaller segments as equal for consolidation selection.
	SegmentsBytesFloor *int64 `json:"segmentsBytesFloor,omitempty"`
	// Lookahead specifies the number of additionally searched tiers except initially chosen candidated based on min_segments,
	// max_segments, segments_bytes_max, segments_bytes_floor with respect to defined values.
	// Default value falls to integer_traits<size_t>::const_max (in C++ source code).
	Lookahead *int64 `json:"lookahead,omitempty"`
}

// ArangoSearchLinks is a strongly typed map containing links between a
// collection and a view.
// The keys in the map are collection names.
type ArangoSearchLinks map[string]ArangoSearchElementProperties

// ArangoSearchFields is a strongly typed map containing properties per field.
// The keys in the map are field names.
type ArangoSearchFields map[string]ArangoSearchElementProperties

// ArangoSearchElementProperties contains properties that specify how an element
// is indexed in an ArangoSearch view.
// Note that this structure is recursive. Settings not specified (nil)
// at a given level will inherit their setting from a lower level.
type ArangoSearchElementProperties struct {
	// The list of analyzers to be used for indexing of string values. Defaults to ["identify"].
	Analyzers []string `json:"analyzers,omitempty"`
	// If set to true, all fields of this element will be indexed. Defaults to false.
	IncludeAllFields *bool `json:"includeAllFields,omitempty"`
	// If set to true, values in a listed are treated as separate values. Defaults to false.
	TrackListPositions *bool `json:"trackListPositions,omitempty"`
	// This values specifies how the view should track values.
	StoreValues ArangoSearchStoreValues `json:"storeValues,omitempty"`
	// Fields contains the properties for individual fields of the element.
	// The key of the map are field names.
	Fields ArangoSearchFields `json:"fields,omitempty"`
}

// ArangoSearchStoreValues is the type of the StoreValues option of an ArangoSearch element.
type ArangoSearchStoreValues string

const (
	// ArangoSearchStoreValuesNone specifies that a view should not store values.
	ArangoSearchStoreValuesNone ArangoSearchStoreValues = "none"
	// ArangoSearchStoreValuesID specifies that a view should only store
	// information about value presence, to allow use of the EXISTS() function.
	ArangoSearchStoreValuesID ArangoSearchStoreValues = "id"
)
<|endoftext|>


<commit_before>// Copyright 2016 The go-qemu 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 qemu

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

	"github.com/digitalocean/go-qemu/qmp"
)

const defaultTestTimeout = 5 * time.Second

func TestNew(t *testing.T) {
	m := &mockMonitor{}

	_, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}
}

func TestNewError(t *testing.T) {
	m := &mockMonitor{alwaysFail: true}

	_, err := NewDomain(m, "foo")
	if err == nil {
		t.Errorf("expected monitor failure")
	}
}

func TestBlockDevice(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	device := "drive-virtio-disk0"
	bd, err := d.BlockDevice(device)
	if err != nil {
		t.Error(err)
	}

	if bd.Device != device {
		t.Errorf("expected device %q, got %q", device, bd.Device)
	}
}

func TestBlockDeviceNotFound(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	device := "foo"
	_, err = d.BlockDevice(device)
	if err == nil {
		t.Errorf("expected block device %q to not exist", device)
	}

	if err != ErrBlockDeviceNotFound {
		t.Errorf("expected ErrBlockDeviceNotFound")
	}
}

func TestBlockDeviceMonitorFailure(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	if _, err = d.BlockDevice("foo"); err == nil {
		t.Error("expected monitor failure")
	}
}

func TestBlockJobs(t *testing.T) {
	m := &mockMonitor{activeJobs: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	jobs, err := d.BlockJobs()
	if err != nil {
		t.Error(err)
	}

	if len(jobs) != 1 {
		t.Error("expected running backup job")
	}

	expected := "ok"
	if jobs[0].IOStatus != expected {
		t.Errorf("expected i/o status %q, got %q", expected, jobs[0].IOStatus)
	}

	expected = "drive-virtio-disk0"
	if jobs[0].Device != expected {
		t.Errorf("expected device %q, got %q", expected, jobs[0].Device)
	}
}

func TestBlockStats(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	stats, err := d.BlockStats()
	if err != nil {
		t.Error(err)
	}

	if len(stats) != 4 {
		t.Error("expected 4 block stats")
	}

	expected := "ide0-hd0"
	if stats[0].Device != expected {
		t.Errorf("expected device %q, got %q", expected, stats[0].Device)
	}

	expectedBytes := uint64(9786368)
	if stats[0].WriteBytes != expectedBytes {
		t.Errorf("expected %d write bytes, got %d", expectedBytes, stats[0].WriteBytes)
	}
}

func TestBlockJobsMonitorFail(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	_, err = d.BlockJobs()
	if err == nil {
		t.Errorf("expected monitor failure")
	}
}

func TestBlockJobsInvalidJSON(t *testing.T) {
	m := &mockMonitor{invalidJSON: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, err = d.BlockJobs()
	if err == nil {
		t.Errorf("expected invalid json to cause failure")
	}
}

func TestClose(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if err := d.Close(); err != nil {
		t.Error(err)
	}

	if _, ok := <-d.done; ok {
		t.Error("domain should be closed")
	}

	if !m.disconnected {
		t.Error("monitor should be disconnected")
	}
}

func TestCommands(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	cmds, err := d.Commands()
	if err != nil {
		t.Error(err)
	}

	expected := 135
	actual := len(cmds)
	if actual != expected {
		t.Errorf("expected number of supported commands to be %d, got %d", expected, actual)
	}

	found := false
	search := "query-block"
	for _, c := range cmds {
		if c == search {
			found = true
			break
		}
	}

	if !found {
		t.Errorf("expected command %q to be returned", search)
	}
}

func TestCommandsMonitorFailure(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	if _, err := d.Commands(); err == nil {
		t.Error("expected monitor failure")
	}
}

func TestCommandsInvalidJSON(t *testing.T) {
	m := &mockMonitor{invalidJSON: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if _, err := d.Commands(); err == nil {
		t.Error("expected invalid json to cause failure")
	}
}

func TestDomainScreenDump(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	// Use a fixed file name generation function
	name := filepath.Join(os.TempDir(), "test-screendump")
	d.tempFileName = func(_ string, _ string) string {
		return name
	}

	want := []byte("hello world")
	if err := ioutil.WriteFile(name, want, 0666); err != nil {
		t.Error(err)
	}

	rc, err := d.ScreenDump()
	if err != nil {
		t.Error(err)
	}

	got, err := ioutil.ReadAll(rc)
	if err != nil {
		t.Error(err)
	}

	if !bytes.Equal(want, got) {
		t.Errorf("unexpected bytes:\n- want: %v\n-  got: %v", want, got)
	}

	if err := rc.Close(); err != nil {
		t.Error(err)
	}

	if _, err := os.Stat(name); !os.IsNotExist(err) {
		t.Errorf("file should no longer exist, but got: %v", err)
	}
}

func TestPCIDevices(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	devices, err := d.PCIDevices()
	if err != nil {
		t.Error(err)
	}

	if len(devices) != 2 {
		t.Error("expected two PCI devices")
	}

	expected := 0
	if devices[0].Bus != expected {
		t.Errorf("expected device bus %d, got %q", expected, devices[0].Bus)
	}

	expectedDesc := "Intel Ethernet controller"
	if devices[1].ClassInfo.Desc != expectedDesc {
		t.Errorf("expected device %q, got %q", expectedDesc, devices[1].ClassInfo.Desc)
	}
}

func TestStatusRunning(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	status, err := d.Status()
	if err != nil {
		t.Error(err)
	}

	if status != StatusRunning {
		t.Error("expected domain to be running")
	}
}

func TestStatusShutdown(t *testing.T) {
	m := &mockMonitor{poweredOff: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	status, err := d.Status()
	if err != nil {
		t.Error(err)
	}

	if status != StatusShutdown {
		t.Error("expected domain to be powered off")
	}
}

func TestStatusFail(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	_, err = d.Status()
	if err == nil {
		t.Errorf("expected monitor failure")
	}
}

func TestStatusInvalidJSON(t *testing.T) {
	m := &mockMonitor{invalidJSON: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, err = d.Status()
	if err == nil {
		t.Errorf("expected invalid json to cause failure")
	}
}

func TestRunInvalidCommand(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, err = d.Run(qmp.Cmd{})
	if err == nil {
		t.Error("expected invalid command to fail")
	}
}

func TestSupported(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	cmd := "query-block"
	supported, err := d.Supported(cmd)
	if err != nil {
		t.Error(err)
	}

	if !supported {
		t.Errorf("expected command %q to be supported", cmd)
	}
}

func TestSupportedFalse(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	cmd := "query-foo"
	supported, err := d.Supported(cmd)
	if err != nil {
		t.Error(err)
	}

	if supported {
		t.Errorf("expected command %q to be unsupported", cmd)
	}
}

func TestSupportedMonitorFailure(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	if _, err := d.Supported("foo"); err == nil {
		t.Error("expected monitor failure")
	}
}

func TestSystemPowerdown(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if err := d.SystemPowerdown(); err != nil {
		t.Errorf("error powering down domain: %v", err)
	}
}

func TestSystemReset(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if err := d.SystemReset(); err != nil {
		t.Errorf("error resetting domain: %v", err)
	}
}

func TestEvents(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	events, done, err := d.Events()
	if err != nil {
		t.Error(err)
	}

	select {
	case <-events:
		done <- struct{}{}
	case <-time.After(time.Second * 2):
		t.Error("expected event")
	}
}

func TestEventsUnsupported(t *testing.T) {
	m := &mockMonitor{}
	m.eventsUnsupported = true

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, _, err = d.Events()
	if err != qmp.ErrEventsNotSupported {
		t.Errorf("expected qmp.ErrEventsNotSupported, got %s", err.Error())
	}
}
<commit_msg>qemu: example of new style of testing<commit_after>// Copyright 2016 The go-qemu 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 qemu

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/digitalocean/go-qemu/qmp"
)

const defaultTestTimeout = 5 * time.Second

func TestNew(t *testing.T) {
	m := &mockMonitor{}

	_, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}
}

func TestNewError(t *testing.T) {
	m := &mockMonitor{alwaysFail: true}

	_, err := NewDomain(m, "foo")
	if err == nil {
		t.Errorf("expected monitor failure")
	}
}

func TestBlockDevice(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	device := "drive-virtio-disk0"
	bd, err := d.BlockDevice(device)
	if err != nil {
		t.Error(err)
	}

	if bd.Device != device {
		t.Errorf("expected device %q, got %q", device, bd.Device)
	}
}

func TestBlockDeviceNotFound(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	device := "foo"
	_, err = d.BlockDevice(device)
	if err == nil {
		t.Errorf("expected block device %q to not exist", device)
	}

	if err != ErrBlockDeviceNotFound {
		t.Errorf("expected ErrBlockDeviceNotFound")
	}
}

func TestBlockDeviceMonitorFailure(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	if _, err = d.BlockDevice("foo"); err == nil {
		t.Error("expected monitor failure")
	}
}

func TestBlockJobs(t *testing.T) {
	m := &mockMonitor{activeJobs: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	jobs, err := d.BlockJobs()
	if err != nil {
		t.Error(err)
	}

	if len(jobs) != 1 {
		t.Error("expected running backup job")
	}

	expected := "ok"
	if jobs[0].IOStatus != expected {
		t.Errorf("expected i/o status %q, got %q", expected, jobs[0].IOStatus)
	}

	expected = "drive-virtio-disk0"
	if jobs[0].Device != expected {
		t.Errorf("expected device %q, got %q", expected, jobs[0].Device)
	}
}

func TestBlockStats(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	stats, err := d.BlockStats()
	if err != nil {
		t.Error(err)
	}

	if len(stats) != 4 {
		t.Error("expected 4 block stats")
	}

	expected := "ide0-hd0"
	if stats[0].Device != expected {
		t.Errorf("expected device %q, got %q", expected, stats[0].Device)
	}

	expectedBytes := uint64(9786368)
	if stats[0].WriteBytes != expectedBytes {
		t.Errorf("expected %d write bytes, got %d", expectedBytes, stats[0].WriteBytes)
	}
}

func TestBlockJobsMonitorFail(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	_, err = d.BlockJobs()
	if err == nil {
		t.Errorf("expected monitor failure")
	}
}

func TestBlockJobsInvalidJSON(t *testing.T) {
	m := &mockMonitor{invalidJSON: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, err = d.BlockJobs()
	if err == nil {
		t.Errorf("expected invalid json to cause failure")
	}
}

func TestClose(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if err := d.Close(); err != nil {
		t.Error(err)
	}

	if _, ok := <-d.done; ok {
		t.Error("domain should be closed")
	}

	if !m.disconnected {
		t.Error("monitor should be disconnected")
	}
}

func TestCommands(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	cmds, err := d.Commands()
	if err != nil {
		t.Error(err)
	}

	expected := 135
	actual := len(cmds)
	if actual != expected {
		t.Errorf("expected number of supported commands to be %d, got %d", expected, actual)
	}

	found := false
	search := "query-block"
	for _, c := range cmds {
		if c == search {
			found = true
			break
		}
	}

	if !found {
		t.Errorf("expected command %q to be returned", search)
	}
}

func TestCommandsMonitorFailure(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	if _, err := d.Commands(); err == nil {
		t.Error("expected monitor failure")
	}
}

func TestCommandsInvalidJSON(t *testing.T) {
	m := &mockMonitor{invalidJSON: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if _, err := d.Commands(); err == nil {
		t.Error("expected invalid json to cause failure")
	}
}

func TestDomainScreenDump(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	// Use a fixed file name generation function
	name := filepath.Join(os.TempDir(), "test-screendump")
	d.tempFileName = func(_ string, _ string) string {
		return name
	}

	want := []byte("hello world")
	if err := ioutil.WriteFile(name, want, 0666); err != nil {
		t.Error(err)
	}

	rc, err := d.ScreenDump()
	if err != nil {
		t.Error(err)
	}

	got, err := ioutil.ReadAll(rc)
	if err != nil {
		t.Error(err)
	}

	if !bytes.Equal(want, got) {
		t.Errorf("unexpected bytes:\n- want: %v\n-  got: %v", want, got)
	}

	if err := rc.Close(); err != nil {
		t.Error(err)
	}

	if _, err := os.Stat(name); !os.IsNotExist(err) {
		t.Errorf("file should no longer exist, but got: %v", err)
	}
}

func TestPCIDevices(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	devices, err := d.PCIDevices()
	if err != nil {
		t.Error(err)
	}

	if len(devices) != 2 {
		t.Error("expected two PCI devices")
	}

	expected := 0
	if devices[0].Bus != expected {
		t.Errorf("expected device bus %d, got %q", expected, devices[0].Bus)
	}

	expectedDesc := "Intel Ethernet controller"
	if devices[1].ClassInfo.Desc != expectedDesc {
		t.Errorf("expected device %q, got %q", expectedDesc, devices[1].ClassInfo.Desc)
	}
}

func TestStatusRunning(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	status, err := d.Status()
	if err != nil {
		t.Error(err)
	}

	if status != StatusRunning {
		t.Error("expected domain to be running")
	}
}

func TestStatusShutdown(t *testing.T) {
	m := &mockMonitor{poweredOff: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	status, err := d.Status()
	if err != nil {
		t.Error(err)
	}

	if status != StatusShutdown {
		t.Error("expected domain to be powered off")
	}
}

func TestStatusFail(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	_, err = d.Status()
	if err == nil {
		t.Errorf("expected monitor failure")
	}
}

func TestStatusInvalidJSON(t *testing.T) {
	m := &mockMonitor{invalidJSON: true}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, err = d.Status()
	if err == nil {
		t.Errorf("expected invalid json to cause failure")
	}
}

func TestRunInvalidCommand(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, err = d.Run(qmp.Cmd{})
	if err == nil {
		t.Error("expected invalid command to fail")
	}
}

func TestSupported(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	cmd := "query-block"
	supported, err := d.Supported(cmd)
	if err != nil {
		t.Error(err)
	}

	if !supported {
		t.Errorf("expected command %q to be supported", cmd)
	}
}

func TestSupportedFalse(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	cmd := "query-foo"
	supported, err := d.Supported(cmd)
	if err != nil {
		t.Error(err)
	}

	if supported {
		t.Errorf("expected command %q to be unsupported", cmd)
	}
}

func TestSupportedMonitorFailure(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	m.alwaysFail = true
	if _, err := d.Supported("foo"); err == nil {
		t.Error("expected monitor failure")
	}
}

func TestSystemPowerdown(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	if err := d.SystemPowerdown(); err != nil {
		t.Errorf("error powering down domain: %v", err)
	}
}

type success struct {
	Return struct{} `json:"return"`
}

func TestSystemReset(t *testing.T) {
	d, done := testDomain(t, func(cmd qmp.Cmd) interface{} {
		if want, got := "system_reset", cmd.Execute; want != got {
			t.Fatalf("unexpected QMP command:\n- want: %q\n-  got: %q",
				want, got)
		}

		return success{}
	})
	defer done()

	if err := d.SystemReset(); err != nil {
		t.Errorf("error resetting domain: %v", err)
	}
}

func TestEvents(t *testing.T) {
	m := &mockMonitor{}

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	events, done, err := d.Events()
	if err != nil {
		t.Error(err)
	}

	select {
	case <-events:
		done <- struct{}{}
	case <-time.After(time.Second * 2):
		t.Error("expected event")
	}
}

func TestEventsUnsupported(t *testing.T) {
	m := &mockMonitor{}
	m.eventsUnsupported = true

	d, err := NewDomain(m, "foo")
	if err != nil {
		t.Error(err)
	}

	_, _, err = d.Events()
	if err != qmp.ErrEventsNotSupported {
		t.Errorf("expected qmp.ErrEventsNotSupported, got %s", err.Error())
	}
}

func testDomain(t *testing.T, fn func(qmp.Cmd) interface{}) (*Domain, func()) {
	mon := &testMonitor{fn: fn}
	d, err := NewDomain(mon, "test")
	if err != nil {
		t.Fatalf("failed to create test domain: %v", err)
	}

	return d, func() {
		_ = d.Close()
	}
}

type testMonitor struct {
	fn func(qmp.Cmd) interface{}
	noopMonitor
}

func (t *testMonitor) Run(raw []byte) ([]byte, error) {
	var cmd qmp.Cmd
	if err := json.Unmarshal(raw, &cmd); err != nil {
		return nil, err
	}

	return json.Marshal(t.fn(cmd))
}

var _ qmp.Monitor = &noopMonitor{}

type noopMonitor struct{}

func (noopMonitor) Connect() error                    { return nil }
func (noopMonitor) Disconnect() error                 { return nil }
func (noopMonitor) Run(_ []byte) ([]byte, error)      { return nil, nil }
func (noopMonitor) Events() (<-chan qmp.Event, error) { return nil, nil }
<|endoftext|>


<commit_before>/*=======================================*/
//	project: gospatial
//	author: stefan safranek
//	email: sjsafranek@gmail.com
/*=======================================*/

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/paulmach/go.geojson"
	"gospatial/app"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"
)

var (
	database string
)

type DumpedDatabase struct {
	Apikeys map[string]app.Customer               `json:"apikeys"`
	Layers  map[string]*geojson.FeatureCollection `json:"layers"`
}

func list_datsources() {
	fmt.Println("Datasources:")
	// get datbase
	app.DB = app.Database{File: "./" + database + ".db"}
	conn, err := bolt.Open(app.DB.File, 0644, nil)
	if err != nil {
		conn.Close()
		fmt.Println(err)
		os.Exit(1)
	}
	// Get all layers
	conn.View(func(tx *bolt.Tx) error {
		// Assume bucket exists and has keys
		b := tx.Bucket([]byte("layers"))
		b.ForEach(func(k, v []byte) error {
			fmt.Println(string(k))
			return nil
		})
		return nil
	})
	// close database
	conn.Close()
}

func export_datasource(datasource string) {
	fmt.Println("Exporting datasource: ", datasource)
	// setup database
	app.DB = app.Database{File: "./" + database + ".db"}
	app.DB.Init()
	// get datasource from database
	lyr, err := app.DB.GetLayer(datasource)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// marshal to json
	b, err := lyr.MarshalJSON()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// Write to file
	savename := datasource + ".geojson"
	ioutil.WriteFile(savename, b, 0644)
}

func import_datasource(import_file string) {
	fmt.Println("Importing", import_file)
	// setup database
	app.DB = app.Database{File: "./" + database + ".db"}
	app.DB.Init()
	// get geojson file
	var geojson_file string
	ext := strings.Split(import_file, ".")[1]
	// convert shapefile
	if ext == "shp" {
		// Convert .shp to .geojson
		// ogr2ogr -f GeoJSON -t_srs crs:84 [name].geojson [name].shp
		geojson_file := strings.Replace(import_file, ".shp", ".geojson", -1)
		// fmt.Println("ogr2ogr", "-f", "GeoJSON", "-t_srs", "crs:84", geojson_file, shapefile)
		out, err := exec.Command("ogr2ogr", "-f", "GeoJSON", "-t_srs", "crs:84", geojson_file, import_file).Output()
		if err != nil {
			fmt.Println(err)
			fmt.Println(string(out))
			os.Exit(1)
		} else {
			fmt.Println(geojson_file, "created")
			fmt.Println(string(out))
		}
	} else if ext == "geojson" {
		geojson_file = import_file
	} else {
		fmt.Println("Unsupported file type", ext)
		os.Exit(1)
	}
	// Read .geojson file
	file, err := ioutil.ReadFile(geojson_file)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	// Unmarshal to geojson struct
	geojs, err := geojson.UnmarshalFeatureCollection(file)
	if err != nil {
		fmt.Printf("Unmarshal GeoJSON error: %v\n", err)
		os.Exit(1)
	}
	// Create datasource
	ds, _ := app.NewUUID()
	app.DB.InsertLayer(ds, geojs)
	fmt.Println("Datasource created:", ds)
	// Cleanup artifacts
	if geojson_file != import_file {
		os.Remove(geojson_file)
	}
}

func init() {
	// flag.Usage = func{
	// 	"stuff"
	// }
	flag.StringVar(&database, "db", "bolt", "app database")
	flag.Parse()
}

func main() {

	required_args := flag.Args()

	if len(required_args) == 0 {
		fmt.Println("Incorrect usage!")
		os.Exit(1)
	}

	method := required_args[0]

	if method == "ls" {
		list_datsources()
	} else if method == "export" {
		if len(required_args) != 2 {
			fmt.Println("Incorrect usage!")
			os.Exit(1)
		}
		datasource := required_args[1]
		export_datasource(datasource)
	} else if method == "import" {
		if len(required_args) != 2 {
			fmt.Println("Incorrect usage!")
			os.Exit(1)
		}
		import_file := required_args[1]
		import_datasource(import_file)
	} else if method == "create" {
		if len(required_args) != 2 {
			fmt.Println("Incorrect usage!")
			os.Exit(1)
		} else if required_args[1] == "datasource" {
			fmt.Println("Creating datasource")
			app.DB = app.Database{File: "./" + database + ".db"}
			app.DB.Init()
			ds, err := app.DB.NewLayer()
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Println("Datasource created:", ds)
		} else if required_args[1] == "customer" {
			fmt.Println("Creating customer")
			app.DB = app.Database{File: "./" + database + ".db"}
			app.DB.Init()
			apikey := app.NewAPIKey(12)
			customer := app.Customer{Apikey: apikey}
			err := app.DB.InsertCustomer(customer)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Println("Customer created:", apikey)
		} else {
			fmt.Println("Incorrect usage!")
			os.Exit(1)
		}
	} else if method == "backup" {
		fmt.Println("Backing up database...")
		app.DB = app.Database{File: "./" + database + ".db"}
		app.DB.Init()
		savefile := "backup_" + time.Now().String()
		app.DB.Backup(savefile)
		fmt.Println("Backup created:", savefile)
	} else if method == "load" {
		if len(required_args) != 2 {
			fmt.Println("Incorrect usage!")
			os.Exit(1)
		} else {
			filename := required_args[1]
			fmt.Println("Loading database...")
			app.DB = app.Database{File: "./" + database + ".db"}
			app.DB.Init()
			fmt.Printf("Loading database [%s]\n", filename)
			// check for file
			if _, err := os.Stat(filename); os.IsNotExist(err) {
				fmt.Println("File not found [" + filename + "]")
			}
			// open json file
			file, err := ioutil.ReadFile(filename)
			if err != nil {
				fmt.Println(err)
			}
			// unmarshal data
			var data DumpedDatabase
			data.Apikeys = make(map[string]app.Customer)
			data.Layers = make(map[string]*geojson.FeatureCollection)
			err = json.Unmarshal(file, &data)
			if err != nil {
				fmt.Println(err)
			}
			app.DB.InsertCustomers(data.Apikeys)
			app.DB.InsertLayers(data.Layers)
		}
	} else {
		fmt.Println("Incorrect usage!")
		os.Exit(1)
	}
	// exit
	os.Exit(0)
}
<commit_msg>cmd assign method<commit_after>/*=======================================*/
//	project: gospatial
//	author: stefan safranek
//	email: sjsafranek@gmail.com
/*=======================================*/

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/boltdb/bolt"
	"github.com/paulmach/go.geojson"
	"gospatial/app"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"
)

var (
	database string
)

type DumpedDatabase struct {
	Apikeys map[string]app.Customer               `json:"apikeys"`
	Layers  map[string]*geojson.FeatureCollection `json:"layers"`
}

func usage_error(message string) {
	fmt.Println("Incorrect usage!")
	fmt.Println(message)
	os.Exit(1)
}

func setup_db() {
	app.DB = app.Database{File: "./" + database + ".db"}
	app.DB.Init()
}

func list_datasources() {
	fmt.Println("Datasources:")
	// get datbase
	app.DB = app.Database{File: "./" + database + ".db"}
	conn, err := bolt.Open(app.DB.File, 0644, nil)
	if err != nil {
		conn.Close()
		fmt.Println(err)
		os.Exit(1)
	}
	// Get all layers
	conn.View(func(tx *bolt.Tx) error {
		// Assume bucket exists and has keys
		b := tx.Bucket([]byte("layers"))
		b.ForEach(func(k, v []byte) error {
			fmt.Println(string(k))
			return nil
		})
		return nil
	})
	// close database
	conn.Close()
}

func export_datasource(datasource string) {
	fmt.Println("Exporting datasource: ", datasource)
	// setup database
	setup_db()
	// get datasource from database
	lyr, err := app.DB.GetLayer(datasource)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// marshal to json
	b, err := lyr.MarshalJSON()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	// Write to file
	savename := datasource + ".geojson"
	ioutil.WriteFile(savename, b, 0644)
}

func import_datasource(import_file string) {
	fmt.Println("Importing", import_file)
	// setup database
	setup_db()
	// get geojson file
	var geojson_file string
	ext := strings.Split(import_file, ".")[1]
	// convert shapefile
	if ext == "shp" {
		// Convert .shp to .geojson
		// ogr2ogr -f GeoJSON -t_srs crs:84 [name].geojson [name].shp
		geojson_file := strings.Replace(import_file, ".shp", ".geojson", -1)
		// fmt.Println("ogr2ogr", "-f", "GeoJSON", "-t_srs", "crs:84", geojson_file, shapefile)
		out, err := exec.Command("ogr2ogr", "-f", "GeoJSON", "-t_srs", "crs:84", geojson_file, import_file).Output()
		if err != nil {
			fmt.Println(err)
			fmt.Println(string(out))
			os.Exit(1)
		} else {
			fmt.Println(geojson_file, "created")
			fmt.Println(string(out))
		}
	} else if ext == "geojson" {
		geojson_file = import_file
	} else {
		fmt.Println("Unsupported file type", ext)
		os.Exit(1)
	}
	// Read .geojson file
	file, err := ioutil.ReadFile(geojson_file)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	// Unmarshal to geojson struct
	geojs, err := geojson.UnmarshalFeatureCollection(file)
	if err != nil {
		fmt.Printf("Unmarshal GeoJSON error: %v\n", err)
		os.Exit(1)
	}
	// Create datasource
	ds, _ := app.NewUUID()
	app.DB.InsertLayer(ds, geojs)
	fmt.Println("Datasource created:", ds)
	// Cleanup artifacts
	if geojson_file != import_file {
		os.Remove(geojson_file)
	}
}

func init() {
	flag.Usage = func() {
		fmt.Println("Usage: gospatial_cmd [method] [option]\n")
		fmt.Printf("Methods:\n")
		fmt.Printf("  ls\n\tList all datasources from database\n")
		fmt.Printf("  export [datasource]\n\tExports datasource to GeoJSON file\n")
		fmt.Printf("  import [<filename>.shp || <filename>.geojson]\n\tImports datasource from shapefile or GeoJSON\n")
		fmt.Printf("  create [datasource || customer]\n\tCreates new datasource or customer\n")
		fmt.Printf("  assign [datasource] [customer]\n\tAssigns datasource to customer\n")
		fmt.Printf("\n")
		fmt.Printf("Defaults:\n")
		flag.PrintDefaults()
	}
	flag.StringVar(&database, "db", "bolt", "app database")
	flag.Parse()
}

func main() {

	required_args := flag.Args()

	if len(required_args) == 0 {
		usage_error("No method provided")
	}

	method := required_args[0]

	if method == "ls" {
		list_datasources()
	} else if method == "export" {
		if len(required_args) != 2 {
			usage_error("No datasource provided")
		}
		datasource := required_args[1]
		export_datasource(datasource)
	} else if method == "import" {
		if len(required_args) != 2 {
			usage_error("No file provided")
		}
		import_file := required_args[1]
		import_datasource(import_file)
	} else if method == "create" {
		if len(required_args) != 2 {
			usage_error("Please specify either 'datasource' or 'customer' to create")
		} else if required_args[1] == "datasource" {
			fmt.Println("Creating datasource")
			setup_db()
			ds, err := app.DB.NewLayer()
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Println("Datasource created:", ds)
		} else if required_args[1] == "customer" {
			fmt.Println("Creating customer")
			setup_db()
			apikey := app.NewAPIKey(12)
			customer := app.Customer{Apikey: apikey}
			err := app.DB.InsertCustomer(customer)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Println("Customer created:", apikey)
		} else {
			usage_error("Cannot create '" + required_args[1] + "'")
		}
	} else if method == "backup" {
		fmt.Println("Backing up database...")
		setup_db()
		savefile := "backup_" + time.Now().String()
		app.DB.Backup(savefile)
		fmt.Println("Backup created:", savefile)
	} else if method == "load" {
		if len(required_args) != 2 {
			usage_error("Please provide a database to load")
		} else {
			filename := required_args[1]
			fmt.Println("Loading database...")
			setup_db()
			fmt.Printf("Loading database [%s]\n", filename)
			// check for file
			if _, err := os.Stat(filename); os.IsNotExist(err) {
				fmt.Println("File not found [" + filename + "]")
			}
			// open json file
			file, err := ioutil.ReadFile(filename)
			if err != nil {
				fmt.Println(err)
			}
			// unmarshal data
			var data DumpedDatabase
			data.Apikeys = make(map[string]app.Customer)
			data.Layers = make(map[string]*geojson.FeatureCollection)
			err = json.Unmarshal(file, &data)
			if err != nil {
				fmt.Println(err)
			}
			app.DB.InsertCustomers(data.Apikeys)
			app.DB.InsertLayers(data.Layers)
		}
	} else if method == "assign" {
		if len(required_args) != 3 {
			usage_error("Please datasource and customer key")
		} else {
			setup_db()
			customer, err := app.DB.GetCustomer(required_args[2])
			if err != nil {
				fmt.Println("Customer key not found!")
				os.Exit(1)
			}
			// CHECK IF DATASOURCE EXISTS
			// Add datasource uuid to customer
			customer.Datasources = append(customer.Datasources, required_args[1])
			app.DB.InsertCustomer(customer)
		}
	} else {
		usage_error("Method not found")
	}
	// exit
	os.Exit(0)
}
<|endoftext|>


<commit_before>package cq_test

import (
	"database/sql"
	. "launchpad.net/gocheck"
	"log"
	"testing"
)

// This file is meant to hold integration tests where cq must be imported

type DriverSuite struct{}

var _ = Suite(&DriverSuite{})

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

func testConn() *sql.DB {
	db, err := sql.Open("neo4j-cypher", "http://localhost:7474/")
	if err != nil {
		log.Fatal(err)
	}
	return db
}

func prepareTest(query string) *sql.Stmt {
	db := testConn()
	stmt, err := db.Prepare(query)
	if err != nil {
		log.Fatal(err)
	}
	return stmt
}

func prepareAndQuery(query string) *sql.Rows {
	stmt := prepareTest(query)
	rows, err := stmt.Query()
	if err != nil {
		log.Fatal(err)
	}
	return rows
}

func (s *DriverSuite) TestDbQuery(c *C) {
	db := testConn()
	rows, err := db.Query("return 1")
	c.Assert(err, IsNil)

	if rows == nil {
		c.Fatal("rows shouldn't be nil")
	}
}

func (s *DriverSuite) TestDbExec(c *C) {
	db := testConn()
	result, err := db.Exec("return 1")
	c.Assert(err, IsNil)

	if result == nil {
		c.Fatal("result should not be nil")
	}
}

func (s *DriverSuite) TestQuerySimple(c *C) {
	rows := prepareAndQuery("return 1")
	hasNext := rows.Next()
	if !hasNext {
		c.Fatal("no next!")
	}

	var test int
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != 1 {
		c.Fatal("test != 1")
	}
}

func (s *DriverSuite) TestQuerySimpleFloat(c *C) {
	rows := prepareAndQuery("return 1.2")
	rows.Next()
	var test float64
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != 1.2 {
		c.Fatal("test != 1.2")
	}
}

func (s *DriverSuite) TestQueryFloatParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(1234567910.891)
	rows.Next()
	var test float64
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(test, Equals, 1234567910.891)
}

func (s *DriverSuite) TestQuerySimpleString(c *C) {
	rows := prepareAndQuery("return '123'")
	rows.Next()
	var test string
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != "123" {
		c.Fatal("test != '123';", test)
	}
}

func (s *DriverSuite) TestQueryStringParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query("123")
	rows.Next()
	var test string
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(test, Equals, "123")
}

func (s *DriverSuite) TestQueryArrayByteParam(c *C) {
	c.Skip("byte arrays don't work yet")
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query([]byte("123"))
	rows.Next()
	var test []byte
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(string(test), DeepEquals, string([]byte("123")))
}

func (s *DriverSuite) TestQuerySimpleBool(c *C) {
	rows := prepareAndQuery("return true")
	rows.Next()
	var test bool
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != true {
		c.Fatal("test != true;", test)
	}
}

func (s *DriverSuite) TestQueryBoolParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(true)
	rows.Next()
	var test bool
	err = rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != true {
		c.Fatal("test != true;", test)
	}
}

func (s *DriverSuite) TestQueryBoolFalseParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(false)
	rows.Next()
	var test bool
	err = rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != false {
		c.Fatal("test != true;", test)
	}
}

func (s *DriverSuite) TestQueryIntParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(123)
	c.Assert(err, IsNil)

	rows.Next()
	var test int
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(test, Equals, 123)
}

func (s *DriverSuite) TestQueryNullString(c *C) {
	rows := prepareAndQuery("return null")
	rows.Next()
	var nullString sql.NullString
	err := rows.Scan(&nullString)
	c.Assert(err, IsNil)
	c.Assert(nullString.Valid, Equals, false)
}

func (s *DriverSuite) TestScanNullInt64(c *C) {
	rows := prepareAndQuery("return 123456789")
	rows.Next()
	var nullInt64 sql.NullInt64
	err := rows.Scan(&nullInt64)
	c.Assert(err, IsNil)
	c.Assert(nullInt64.Valid, Equals, true)
	c.Assert(nullInt64.Int64, Equals, int64(123456789))
}

func (s *DriverSuite) TestScanBigInt64(c *C) {
	rows := prepareAndQuery("return 123456789101112")
	rows.Next()
	var i64 int64
	err := rows.Scan(&i64)
	c.Assert(err, IsNil)
	c.Assert(i64, Equals, int64(123456789101112))
}

func (s *DriverSuite) TestExecNilRows(c *C) {
	db := testConn()
	db.Exec("...")
}
<commit_msg>adding a test for big numbers that render in javascript badly<commit_after>package cq_test

import (
	"database/sql"
	"log"
	"testing"
	. "launchpad.net/gocheck"
)

// This file is meant to hold integration tests where cq must be imported

type DriverSuite struct{}

var _ = Suite(&DriverSuite{})

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

func testConn() *sql.DB {
	db, err := sql.Open("neo4j-cypher", "http://localhost:7474/")
	if err != nil {
		log.Fatal(err)
	}
	return db
}

func prepareTest(query string) *sql.Stmt {
	db := testConn()
	stmt, err := db.Prepare(query)
	if err != nil {
		log.Fatal(err)
	}
	return stmt
}

func prepareAndQuery(query string) *sql.Rows {
	stmt := prepareTest(query)
	rows, err := stmt.Query()
	if err != nil {
		log.Fatal(err)
	}
	return rows
}

func (s *DriverSuite) TestDbQuery(c *C) {
	db := testConn()
	rows, err := db.Query("return 1")
	c.Assert(err, IsNil)

	if rows == nil {
		c.Fatal("rows shouldn't be nil")
	}
}

func (s *DriverSuite) TestDbExec(c *C) {
	db := testConn()
	result, err := db.Exec("return 1")
	c.Assert(err, IsNil)

	if result == nil {
		c.Fatal("result should not be nil")
	}
}

func (s *DriverSuite) TestQuerySimple(c *C) {
	rows := prepareAndQuery("return 1")
	hasNext := rows.Next()
	if !hasNext {
		c.Fatal("no next!")
	}

	var test int
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != 1 {
		c.Fatal("test != 1")
	}
}

func (s *DriverSuite) TestQuerySimpleFloat(c *C) {
	rows := prepareAndQuery("return 1.2")
	rows.Next()
	var test float64
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != 1.2 {
		c.Fatal("test != 1.2")
	}
}

func (s *DriverSuite) TestQueryFloatParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(1234567910.891)
	rows.Next()
	var test float64
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(test, Equals, 1234567910.891)
}

func (s *DriverSuite) TestQuerySimpleString(c *C) {
	rows := prepareAndQuery("return '123'")
	rows.Next()
	var test string
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != "123" {
		c.Fatal("test != '123';", test)
	}
}

func (s *DriverSuite) TestQueryStringParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query("123")
	rows.Next()
	var test string
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(test, Equals, "123")
}

func (s *DriverSuite) TestQueryArrayByteParam(c *C) {
	c.Skip("byte arrays don't work yet")
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query([]byte("123"))
	rows.Next()
	var test []byte
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(string(test), DeepEquals, string([]byte("123")))
}

func (s *DriverSuite) TestQuerySimpleBool(c *C) {
	rows := prepareAndQuery("return true")
	rows.Next()
	var test bool
	err := rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != true {
		c.Fatal("test != true;", test)
	}
}

func (s *DriverSuite) TestQueryBoolParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(true)
	rows.Next()
	var test bool
	err = rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != true {
		c.Fatal("test != true;", test)
	}
}

func (s *DriverSuite) TestQueryBoolFalseParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(false)
	rows.Next()
	var test bool
	err = rows.Scan(&test)
	c.Assert(err, IsNil)

	if test != false {
		c.Fatal("test != true;", test)
	}
}

func (s *DriverSuite) TestQueryIntParam(c *C) {
	stmt := prepareTest("with {0} as test return test")
	rows, err := stmt.Query(123)
	c.Assert(err, IsNil)

	rows.Next()
	var test int
	err = rows.Scan(&test)
	c.Assert(err, IsNil)
	c.Assert(test, Equals, 123)
}

func (s *DriverSuite) TestQueryNullString(c *C) {
	rows := prepareAndQuery("return null")
	rows.Next()
	var nullString sql.NullString
	err := rows.Scan(&nullString)
	c.Assert(err, IsNil)
	c.Assert(nullString.Valid, Equals, false)
}

func (s *DriverSuite) TestScanNullInt64(c *C) {
	rows := prepareAndQuery("return 123456789")
	rows.Next()
	var nullInt64 sql.NullInt64
	err := rows.Scan(&nullInt64)
	c.Assert(err, IsNil)
	c.Assert(nullInt64.Valid, Equals, true)
	c.Assert(nullInt64.Int64, Equals, int64(123456789))
}

func (s *DriverSuite) TestScanBigInt64(c *C) {
	rows := prepareAndQuery("return 123456789101112")
	rows.Next()
	var i64 int64
	err := rows.Scan(&i64)
	c.Assert(err, IsNil)
	c.Assert(i64, Equals, int64(123456789101112))
}

func (s *DriverSuite) TestScanBigInt64FloatBug(c *C) {
	rows := prepareAndQuery("return 23371710262672408")
	rows.Next()
	var i64 int64
	err := rows.Scan(&i64)
	c.Assert(err, IsNil)
	c.Assert(i64, Equals, int64(23371710262672408))
}

func (s *DriverSuite) TestExecNilRows(c *C) {
	db := testConn()
	db.Exec("...")
}
<|endoftext|>