<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// 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 dig

import (
	"testing"

	"github.com/stretchr/testify/require"
)

type S struct{}

func noReturn() {}

func returnNonPointer() S {
	return S{}
}

func nonPointerParams(one, two string) *S {
	return &S{}
}

func TestRegister(t *testing.T) {
	t.Parallel()
	tts := []struct {
		name  string
		param interface{}
		err   error
	}{
		{"non function", struct{}{}, errParamType},
		{"wrong return count", noReturn, errReturnCount},
		{"non pointer return", returnNonPointer, errReturnKind},
		{"wrong parameters type", nonPointerParams, errArgKind},
		{"pointer register", &struct{}{}, nil},
	}

	for _, tc := range tts {
		t.Run(tc.name, func(t *testing.T) {
			g := New()
			err := g.Register(tc.param)

			if tc.err != nil {
				require.EqualError(t, err, tc.err.Error())
			} else {
				require.NoError(t, err)
			}
		})
	}
}

func TestResolve(t *testing.T) {
	t.Parallel()
	tts := []struct {
		name     string
		register func(g *Graph) error
		resolve  func(g *Graph) error
		es       string
	}{
		{
			"non pointer resolve",
			func(g *Graph) error { return g.Register(NewParent1) },
			func(g *Graph) error { return g.Resolve(S{}) },
			"can not resolve non-pointer object",
		},
		{
			"missing dependency",
			func(g *Graph) error { return nil },
			func(g *Graph) error {
				var p1 *Parent1
				return g.Resolve(p1)
			},
			"type *dig.Parent1 is not registered",
		},
	}

	for _, tc := range tts {
		t.Run(tc.name, func(t *testing.T) {
			g := New()

			err := tc.register(g)
			require.NoError(t, err, "Register part of the test cant have errors")

			err = tc.resolve(g)
			if err != nil {
				require.Contains(t, err.Error(), tc.es, "Unexpected error message")
			} else {
				require.NoError(t, err, "Did not expect a resolve error")
			}
		})
	}
}

func TestObjectRegister(t *testing.T) {
	t.Parallel()
	g := New()

	// register a fake struct type into the graph
	type Fake struct {
		Name string
	}
	err := g.Register(&Fake{Name: "I am a fake registered thing"})
	require.NoError(t, err)

	// get one pointer resolved
	var f1 *Fake
	err = g.Resolve(&f1)
	require.NoError(t, err)

	// get second pointer resolved
	var f2 *Fake
	err = g.Resolve(&f2)
	require.NoError(t, err)

	// make sure they are the same
	require.Equal(t, f1, f2)
	require.Equal(t, *f1, *f2)
}

func TestBasicRegisterResolve(t *testing.T) {
	t.Parallel()
	g := New()

	err := g.Register(NewGrandchild1)
	require.NoError(t, err)

	var first *Grandchild1
	require.NoError(t, g.Resolve(&first), "No error expected during first Resolve")

	var second *Grandchild1
	require.NoError(t, g.Resolve(&second), "No error expected during second Resolve")

	require.NotNil(t, first, "Child1 must have been registered")
	require.NotNil(t, second, "Child1 must have been registered")
	require.True(t, first == second, "Must point to the same object")
}

func TestRegisterAll(t *testing.T) {
	t.Parallel()
	g := New()

	err := g.RegisterAll(
		NewParent1,
		NewChild1,
		NewGrandchild1,
	)
	require.NoError(t, err)

	var p1 *Parent1
	err = g.Resolve(&p1)

	require.NoError(t, err, "No error expected during Resolve")
	require.NotNil(t, p1.c1, "Child1 must have been registered")
	require.NotNil(t, p1.c1.gc1, "Grandchild1 must have been registered")
}

func TestConcurrentAccess(t *testing.T) {
	t.Parallel()
	g := New()

	for i := 0; i < 10; i++ {
		go func() {
			require.NoError(t, g.Register(NewGrandchild1))

			var gc1 *Grandchild1
			require.NoError(t, g.Resolve(&gc1))
		}()
	}
}

func TestCycles(t *testing.T) {
	t.Parallel()
	g := New()

	//    Type1
	//    /    \
	// Type2  Type 3
	//   /       \
	// Type4    Type 1
	type Type1 interface{}
	type Type2 interface{}
	type Type3 interface{}
	type Type4 interface{}
	c1 := func(t2 Type2, t3 Type3) Type1 { return nil }
	c2 := func(t4 Type4) Type2 { return nil }
	c3 := func(t3 Type1) Type3 { return nil }

	require.NoError(t, g.Register(c1))
	require.NoError(t, g.Register(c2))
	err := g.Register(c3)

	require.Contains(t, err.Error(), "unable to register dig.Type3")
	require.Contains(t, err.Error(), "dig.Type3 -> dig.Type1 -> dig.Type3")
}

func TestResolveAll(t *testing.T) {
	t.Parallel()
	g := New()

	err := g.RegisterAll(
		NewGrandchild1,
		NewChild1,
		NewParent1,
	)
	require.NoError(t, err)

	var p1 *Parent1
	var p2 *Parent1
	var p3 *Parent1
	var p4 *Parent1

	err = g.ResolveAll(&p1, &p2, &p3, &p4)
	require.NoError(t, err, "Did not expect error on resolve all")
	require.Equal(t, p1.name, "Parent1")
	require.True(t, p1 == p2 && p2 == p3 && p3 == p4, "All pointers must be equal")
}

func TestEmptyAfterReset(t *testing.T) {
	t.Parallel()
	g := New()

	require.NoError(t, g.Register(NewGrandchild1))

	var first *Grandchild1
	require.NoError(t, g.Resolve(&first), "No error expected during first Resolve")
	g.Reset()
	require.Contains(t, g.Resolve(&first).Error(), "not registered")
}

func TestPanicConstructor(t *testing.T) {
	t.Parallel()
	g := New()

	type Type1 struct{}
	c := func() *Type1 {
		panic("RUH ROH")
	}

	require.NoError(t, g.Register(c))

	var v *Type1
	err := g.Resolve(&v)
	require.Contains(t, err.Error(), "panic during Resolve")
	require.Contains(t, err.Error(), "RUH ROH")
}

func TestMustFunctions(t *testing.T) {
	t.Parallel()
	tts := []struct {
		name          string
		f             func(g *Graph)
		panicExpected bool
	}{
		{
			"wrong register type",
			func(g *Graph) { g.MustRegister(2) },
			true,
		},
		{
			"wrong register all types",
			func(g *Graph) { g.MustRegisterAll("2", "3") },
			true,
		},
		{
			"unregistered type",
			func(g *Graph) {
				var v *Type1
				g.MustResolve(&v)
			},
			true,
		},
		{
			"correct register",
			func(g *Graph) { g.MustRegister(NewChild1) },
			false,
		},
		{
			"correct register all",
			func(g *Graph) { g.MustRegisterAll(NewChild1, NewChild2) },
			false,
		},
		{
			"unregistered types",
			func(g *Graph) {
				var v *Type1
				var v2 *Type2
				g.MustResolveAll(&v, &v2)
			},
			true,
		},
	}

	for _, tc := range tts {
		t.Run(tc.name, func(t *testing.T) {
			g := New()
			f := func() {
				tc.f(g)
			}

			if tc.panicExpected {
				require.Panics(t, f)
			} else {
				require.NotPanics(t, f)
			}
		})
	}
}
<commit_msg>dig: Fix invalid test (#373)<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// 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 dig

import (
	"testing"

	"github.com/stretchr/testify/require"
)

type S struct{}

func noReturn() {}

func returnNonPointer() S {
	return S{}
}

func nonPointerParams(one, two string) *S {
	return &S{}
}

func TestRegister(t *testing.T) {
	t.Parallel()
	tts := []struct {
		name  string
		param interface{}
		err   error
	}{
		{"non function", struct{}{}, errParamType},
		{"wrong return count", noReturn, errReturnCount},
		{"non pointer return", returnNonPointer, errReturnKind},
		{"wrong parameters type", nonPointerParams, errArgKind},
		{"pointer register", &struct{}{}, nil},
	}

	for _, tc := range tts {
		t.Run(tc.name, func(t *testing.T) {
			g := New()
			err := g.Register(tc.param)

			if tc.err != nil {
				require.EqualError(t, err, tc.err.Error())
			} else {
				require.NoError(t, err)
			}
		})
	}
}

func TestResolve(t *testing.T) {
	t.Parallel()
	tts := []struct {
		name     string
		register func(g *Graph) error
		resolve  func(g *Graph) error
		es       string
	}{
		{
			"non pointer resolve",
			func(g *Graph) error { return g.Register(NewParent1) },
			func(g *Graph) error { return g.Resolve(S{}) },
			"can not resolve non-pointer object",
		},
		{
			"missing dependency",
			func(g *Graph) error { return nil },
			func(g *Graph) error {
				var p1 *Parent1
				return g.Resolve(p1)
			},
			"type *dig.Parent1 is not registered",
		},
	}

	for _, tc := range tts {
		t.Run(tc.name, func(t *testing.T) {
			g := New()

			err := tc.register(g)
			require.NoError(t, err, "Register part of the test cant have errors")

			err = tc.resolve(g)
			if tc.es != "" {
				require.Error(t, err)
				require.Contains(t, err.Error(), tc.es, "Unexpected error message")
			} else {
				require.NoError(t, err, "Did not expect a resolve error")
			}
		})
	}
}

func TestObjectRegister(t *testing.T) {
	t.Parallel()
	g := New()

	// register a fake struct type into the graph
	type Fake struct {
		Name string
	}
	err := g.Register(&Fake{Name: "I am a fake registered thing"})
	require.NoError(t, err)

	// get one pointer resolved
	var f1 *Fake
	err = g.Resolve(&f1)
	require.NoError(t, err)

	// get second pointer resolved
	var f2 *Fake
	err = g.Resolve(&f2)
	require.NoError(t, err)

	// make sure they are the same
	require.Equal(t, f1, f2)
	require.Equal(t, *f1, *f2)
}

func TestBasicRegisterResolve(t *testing.T) {
	t.Parallel()
	g := New()

	err := g.Register(NewGrandchild1)
	require.NoError(t, err)

	var first *Grandchild1
	require.NoError(t, g.Resolve(&first), "No error expected during first Resolve")

	var second *Grandchild1
	require.NoError(t, g.Resolve(&second), "No error expected during second Resolve")

	require.NotNil(t, first, "Child1 must have been registered")
	require.NotNil(t, second, "Child1 must have been registered")
	require.True(t, first == second, "Must point to the same object")
}

func TestRegisterAll(t *testing.T) {
	t.Parallel()
	g := New()

	err := g.RegisterAll(
		NewParent1,
		NewChild1,
		NewGrandchild1,
	)
	require.NoError(t, err)

	var p1 *Parent1
	err = g.Resolve(&p1)

	require.NoError(t, err, "No error expected during Resolve")
	require.NotNil(t, p1.c1, "Child1 must have been registered")
	require.NotNil(t, p1.c1.gc1, "Grandchild1 must have been registered")
}

func TestConcurrentAccess(t *testing.T) {
	t.Parallel()
	g := New()

	for i := 0; i < 10; i++ {
		go func() {
			require.NoError(t, g.Register(NewGrandchild1))

			var gc1 *Grandchild1
			require.NoError(t, g.Resolve(&gc1))
		}()
	}
}

func TestCycles(t *testing.T) {
	t.Parallel()
	g := New()

	//    Type1
	//    /    \
	// Type2  Type 3
	//   /       \
	// Type4    Type 1
	type Type1 interface{}
	type Type2 interface{}
	type Type3 interface{}
	type Type4 interface{}
	c1 := func(t2 Type2, t3 Type3) Type1 { return nil }
	c2 := func(t4 Type4) Type2 { return nil }
	c3 := func(t3 Type1) Type3 { return nil }

	require.NoError(t, g.Register(c1))
	require.NoError(t, g.Register(c2))
	err := g.Register(c3)

	require.Contains(t, err.Error(), "unable to register dig.Type3")
	require.Contains(t, err.Error(), "dig.Type3 -> dig.Type1 -> dig.Type3")
}

func TestResolveAll(t *testing.T) {
	t.Parallel()
	g := New()

	err := g.RegisterAll(
		NewGrandchild1,
		NewChild1,
		NewParent1,
	)
	require.NoError(t, err)

	var p1 *Parent1
	var p2 *Parent1
	var p3 *Parent1
	var p4 *Parent1

	err = g.ResolveAll(&p1, &p2, &p3, &p4)
	require.NoError(t, err, "Did not expect error on resolve all")
	require.Equal(t, p1.name, "Parent1")
	require.True(t, p1 == p2 && p2 == p3 && p3 == p4, "All pointers must be equal")
}

func TestEmptyAfterReset(t *testing.T) {
	t.Parallel()
	g := New()

	require.NoError(t, g.Register(NewGrandchild1))

	var first *Grandchild1
	require.NoError(t, g.Resolve(&first), "No error expected during first Resolve")
	g.Reset()
	require.Contains(t, g.Resolve(&first).Error(), "not registered")
}

func TestPanicConstructor(t *testing.T) {
	t.Parallel()
	g := New()

	type Type1 struct{}
	c := func() *Type1 {
		panic("RUH ROH")
	}

	require.NoError(t, g.Register(c))

	var v *Type1
	err := g.Resolve(&v)
	require.Contains(t, err.Error(), "panic during Resolve")
	require.Contains(t, err.Error(), "RUH ROH")
}

func TestMustFunctions(t *testing.T) {
	t.Parallel()
	tts := []struct {
		name          string
		f             func(g *Graph)
		panicExpected bool
	}{
		{
			"wrong register type",
			func(g *Graph) { g.MustRegister(2) },
			true,
		},
		{
			"wrong register all types",
			func(g *Graph) { g.MustRegisterAll("2", "3") },
			true,
		},
		{
			"unregistered type",
			func(g *Graph) {
				var v *Type1
				g.MustResolve(&v)
			},
			true,
		},
		{
			"correct register",
			func(g *Graph) { g.MustRegister(NewChild1) },
			false,
		},
		{
			"correct register all",
			func(g *Graph) { g.MustRegisterAll(NewChild1, NewChild2) },
			false,
		},
		{
			"unregistered types",
			func(g *Graph) {
				var v *Type1
				var v2 *Type2
				g.MustResolveAll(&v, &v2)
			},
			true,
		},
	}

	for _, tc := range tts {
		t.Run(tc.name, func(t *testing.T) {
			g := New()
			f := func() {
				tc.f(g)
			}

			if tc.panicExpected {
				require.Panics(t, f)
			} else {
				require.NotPanics(t, f)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package link

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

	"github.com/documize/community/core/uniqueid"
	"github.com/documize/community/domain"
	"github.com/documize/community/domain/store"
	"github.com/documize/community/model/link"
	"github.com/pkg/errors"
)

// Store provides data access to space category information.
type Store struct {
	store.Context
	store.LinkStorer
}

// Add inserts wiki-link into the store.
// These links exist when content references another document or content.
func (s Store) Add(ctx domain.RequestContext, l link.Link) (err error) {
	l.Created = time.Now().UTC()
	l.Revised = time.Now().UTC()

	_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_doc_link (c_refid, c_orgid, c_spaceid, c_userid, c_sourcedocid, c_sourcesectionid, c_targetdocid, c_targetid, c_externalid, c_type, c_orphan, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
		l.RefID, l.OrgID, l.SpaceID, l.UserID, l.SourceDocumentID, l.SourceSectionID, l.TargetDocumentID, l.TargetID, l.ExternalID, l.LinkType, l.Orphan, l.Created, l.Revised)

	if err != nil {
		err = errors.Wrap(err, "execute link insert")
	}

	return
}

// GetDocumentOutboundLinks returns outbound links for specified document.
func (s Store) GetDocumentOutboundLinks(ctx domain.RequestContext, documentID string) (links []link.Link, err error) {
	err = s.Runtime.Db.Select(&links, s.Bind(`
		select c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_userid AS userid,
        c_sourcedocid AS sourcedocumentid, c_sourcesectionid AS sourcesectionid,
        c_targetdocid AS targetdocumentid, c_targetid AS targetid, c_externalid AS externalid,
        c_type as linktype, c_orphan As orphan, c_created AS created, c_revised AS revised
		FROM dmz_doc_link
		WHERE c_orgid=? AND c_sourcedocid=?`),
		ctx.OrgID, documentID)

	if err != nil && err != sql.ErrNoRows {
		err = errors.Wrap(err, "select document oubound links")
		return
	}
	if len(links) == 0 {
		links = []link.Link{}
	}

	return
}

// GetPageLinks returns outbound links for specified page in document.
func (s Store) GetPageLinks(ctx domain.RequestContext, documentID, pageID string) (links []link.Link, err error) {
	err = s.Runtime.Db.Select(&links, s.Bind(`
        select c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_userid AS userid,
        c_sourcedocid AS sourcedocumentid, c_sourcesectionid AS sourcesectionid,
        c_targetdocid AS targetdocumentid, c_targetid AS targetid, c_externalid AS externalid,
        c_type as linktype, c_orphan As orphan, c_created AS created, c_revised AS revised
		FROM dmz_doc_link
		WHERE c_orgid=? AND c_sourcedocid=? AND c_sourcesectionid=?`),
		ctx.OrgID, documentID, pageID)

	if err != nil && err != sql.ErrNoRows {
		err = errors.Wrap(err, "get page links")
		return
	}
	if len(links) == 0 {
		links = []link.Link{}
	}

	return
}

// MarkOrphanDocumentLink marks all link records referencing specified document.
func (s Store) MarkOrphanDocumentLink(ctx domain.RequestContext, documentID string) (err error) {
	revised := time.Now().UTC()
	_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_doc_link SET
        c_orphan=true, c_revised=?
        WHERE c_type='document' AND c_orgid=? AND c_targetdocid=?`),
		revised, ctx.OrgID, documentID)

	if err != nil {
		err = errors.Wrap(err, "mark link as orphan")
	}

	return
}

// MarkOrphanPageLink marks all link records referencing specified page.
func (s Store) MarkOrphanPageLink(ctx domain.RequestContext, pageID string) (err error) {
	revised := time.Now().UTC()
	_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_doc_link SET
        c_orphan=true, c_revised=?
        WHERE c_type='section' AND c_orgid=? AND c_targetid=?`),
		revised, ctx.OrgID, pageID)

	if err != nil {
		err = errors.Wrap(err, "mark orphan page link")
	}

	return
}

// MarkOrphanAttachmentLink marks all link records referencing specified attachment.
func (s Store) MarkOrphanAttachmentLink(ctx domain.RequestContext, attachmentID string) (err error) {
	revised := time.Now().UTC()
	_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_doc_link SET
        c_orphan=true, c_revised=?
        WHERE c_type='file' AND c_orgid=? AND c_targetid=?`),
		revised, ctx.OrgID, attachmentID)

	if err != nil {
		err = errors.Wrap(err, "mark orphan attachment link")
	}

	return
}

// DeleteSourcePageLinks removes saved links for given source.
func (s Store) DeleteSourcePageLinks(ctx domain.RequestContext, pageID string) (rows int64, err error) {
	return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_link WHERE c_orgid='%s' AND c_sourcesectionid='%s'", ctx.OrgID, pageID))
}

// DeleteSourceDocumentLinks removes saved links for given document.
func (s Store) DeleteSourceDocumentLinks(ctx domain.RequestContext, documentID string) (rows int64, err error) {
	return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_link WHERE c_orgid='%s' AND c_sourcedocid='%s'", ctx.OrgID, documentID))
}

// DeleteLink removes saved link from the store.
func (s Store) DeleteLink(ctx domain.RequestContext, id string) (rows int64, err error) {
	return s.DeleteConstrained(ctx.Transaction, "dmz_doc_link", ctx.OrgID, id)
}

// SearchCandidates returns matching documents, sections and attachments using keywords.
func (s Store) SearchCandidates(ctx domain.RequestContext, keywords string) (docs []link.Candidate,
	pages []link.Candidate, attachments []link.Candidate, err error) {

	// find matching documents
	temp := []link.Candidate{}
	keywords = strings.TrimSpace(strings.ToLower(keywords))
	likeQuery := "LOWER(d.c_name) LIKE '%" + keywords + "%'"

	err = s.Runtime.Db.Select(&temp, s.Bind(`
		SELECT d.c_refid AS documentid, d.c_spaceid AS spaceid, d.c_name AS title, l.c_name AS context
        FROM dmz_doc d LEFT JOIN dmz_space l ON d.c_spaceid=l.c_refid
        WHERE l.c_orgid=? AND `+likeQuery+` AND d.c_spaceid IN
		    (SELECT c_refid FROM dmz_space WHERE c_orgid=? AND c_refid IN
                (SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
                    (SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
				    UNION ALL
                    SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role'
                    AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
                    )
                )
            )
        ORDER BY title`),
		ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err != nil {
		err = errors.Wrap(err, "execute search links 1")
		return
	}

	for _, r := range temp {
		c := link.Candidate{
			RefID:      uniqueid.Generate(),
			SpaceID:    r.SpaceID,
			DocumentID: r.DocumentID,
			TargetID:   r.DocumentID,
			LinkType:   "document",
			Title:      r.Title,
			Context:    r.Context,
		}

		docs = append(docs, c)
	}

	// find matching sections
	likeQuery = "LOWER(p.c_name) LIKE '%" + keywords + "%'"
	temp = []link.Candidate{}

	err = s.Runtime.Db.Select(&temp, s.Bind(`
        SELECT p.c_refid AS targetid, p.c_docid AS documentid, p.c_name AS title,
        p.c_type AS linktype, d.c_name AS context, d.c_spaceid AS spaceid
        FROM dmz_section p LEFT JOIN dmz_doc d ON d.c_refid=p.c_docid
        WHERE p.c_orgid=? AND `+likeQuery+` AND d.c_spaceid IN
		    (SELECT c_refid FROM dmz_space WHERE c_orgid=? AND c_refid IN
                (SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
                    (SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
                    UNION ALL
                    SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role'
                    AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
                    )
                )
		    )
        ORDER BY p.c_name`),
		ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err != nil {
		err = errors.Wrap(err, "execute search links 2")
		return
	}

	for _, r := range temp {
		c := link.Candidate{
			RefID:      uniqueid.Generate(),
			SpaceID:    r.SpaceID,
			DocumentID: r.DocumentID,
			TargetID:   r.TargetID,
			LinkType:   r.LinkType,
			Title:      r.Title,
			Context:    r.Context,
		}

		pages = append(pages, c)
	}

	// find matching attachments
	likeQuery = "LOWER(a.c_filename) LIKE '%" + keywords + "%'"
	temp = []link.Candidate{}

	err = s.Runtime.Db.Select(&temp, s.Bind(`
        SELECT a.c_refid AS targetid, a.c_docid AS documentid, a.c_filename AS title, a.c_extension AS context, d.c_spaceid AS spaceid
        FROM dmz_doc_attachment a LEFT JOIN dmz_doc d ON d.c_refid=a.c_docid
        WHERE a.c_orgid=? AND `+likeQuery+` AND d.c_spaceid IN
            (SELECT c_refid FROM dmz_space WHERE c_orgid=? AND c_refid IN
                (SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
                    (SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
                    UNION ALL
                    SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role'
                    AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
                    )
                )
		    )
        ORDER BY a.c_filename`),
		ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err != nil {
		err = errors.Wrap(err, "execute search links 3")
		return
	}

	for _, r := range temp {
		c := link.Candidate{
			RefID:      uniqueid.Generate(),
			SpaceID:    r.SpaceID,
			DocumentID: r.DocumentID,
			TargetID:   r.TargetID,
			LinkType:   "file",
			Title:      r.Title,
			Context:    r.Context,
		}

		attachments = append(attachments, c)
	}

	if len(docs) == 0 {
		docs = []link.Candidate{}
	}
	if len(pages) == 0 {
		pages = []link.Candidate{}
	}
	if len(attachments) == 0 {
		attachments = []link.Candidate{}
	}

	return
}
<commit_msg>Faster candidate link selection via SQL.<commit_after>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package link

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

	"github.com/documize/community/core/uniqueid"
	"github.com/documize/community/domain"
	"github.com/documize/community/domain/store"
	"github.com/documize/community/model/link"
	"github.com/pkg/errors"
)

// Store provides data access to space category information.
type Store struct {
	store.Context
	store.LinkStorer
}

// Add inserts wiki-link into the store.
// These links exist when content references another document or content.
func (s Store) Add(ctx domain.RequestContext, l link.Link) (err error) {
	l.Created = time.Now().UTC()
	l.Revised = time.Now().UTC()

	_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_doc_link (c_refid, c_orgid, c_spaceid, c_userid, c_sourcedocid, c_sourcesectionid, c_targetdocid, c_targetid, c_externalid, c_type, c_orphan, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
		l.RefID, l.OrgID, l.SpaceID, l.UserID, l.SourceDocumentID, l.SourceSectionID, l.TargetDocumentID, l.TargetID, l.ExternalID, l.LinkType, l.Orphan, l.Created, l.Revised)

	if err != nil {
		err = errors.Wrap(err, "execute link insert")
	}

	return
}

// GetDocumentOutboundLinks returns outbound links for specified document.
func (s Store) GetDocumentOutboundLinks(ctx domain.RequestContext, documentID string) (links []link.Link, err error) {
	err = s.Runtime.Db.Select(&links, s.Bind(`
		select c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_userid AS userid,
        c_sourcedocid AS sourcedocumentid, c_sourcesectionid AS sourcesectionid,
        c_targetdocid AS targetdocumentid, c_targetid AS targetid, c_externalid AS externalid,
        c_type as linktype, c_orphan As orphan, c_created AS created, c_revised AS revised
		FROM dmz_doc_link
		WHERE c_orgid=? AND c_sourcedocid=?`),
		ctx.OrgID, documentID)

	if err != nil && err != sql.ErrNoRows {
		err = errors.Wrap(err, "select document oubound links")
		return
	}
	if len(links) == 0 {
		links = []link.Link{}
	}

	return
}

// GetPageLinks returns outbound links for specified page in document.
func (s Store) GetPageLinks(ctx domain.RequestContext, documentID, pageID string) (links []link.Link, err error) {
	err = s.Runtime.Db.Select(&links, s.Bind(`
        select c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_userid AS userid,
        c_sourcedocid AS sourcedocumentid, c_sourcesectionid AS sourcesectionid,
        c_targetdocid AS targetdocumentid, c_targetid AS targetid, c_externalid AS externalid,
        c_type as linktype, c_orphan As orphan, c_created AS created, c_revised AS revised
		FROM dmz_doc_link
		WHERE c_orgid=? AND c_sourcedocid=? AND c_sourcesectionid=?`),
		ctx.OrgID, documentID, pageID)

	if err != nil && err != sql.ErrNoRows {
		err = errors.Wrap(err, "get page links")
		return
	}
	if len(links) == 0 {
		links = []link.Link{}
	}

	return
}

// MarkOrphanDocumentLink marks all link records referencing specified document.
func (s Store) MarkOrphanDocumentLink(ctx domain.RequestContext, documentID string) (err error) {
	revised := time.Now().UTC()
	_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_doc_link SET
        c_orphan=true, c_revised=?
        WHERE c_type='document' AND c_orgid=? AND c_targetdocid=?`),
		revised, ctx.OrgID, documentID)

	if err != nil {
		err = errors.Wrap(err, "mark link as orphan")
	}

	return
}

// MarkOrphanPageLink marks all link records referencing specified page.
func (s Store) MarkOrphanPageLink(ctx domain.RequestContext, pageID string) (err error) {
	revised := time.Now().UTC()
	_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_doc_link SET
        c_orphan=true, c_revised=?
        WHERE c_type='section' AND c_orgid=? AND c_targetid=?`),
		revised, ctx.OrgID, pageID)

	if err != nil {
		err = errors.Wrap(err, "mark orphan page link")
	}

	return
}

// MarkOrphanAttachmentLink marks all link records referencing specified attachment.
func (s Store) MarkOrphanAttachmentLink(ctx domain.RequestContext, attachmentID string) (err error) {
	revised := time.Now().UTC()
	_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_doc_link SET
        c_orphan=true, c_revised=?
        WHERE c_type='file' AND c_orgid=? AND c_targetid=?`),
		revised, ctx.OrgID, attachmentID)

	if err != nil {
		err = errors.Wrap(err, "mark orphan attachment link")
	}

	return
}

// DeleteSourcePageLinks removes saved links for given source.
func (s Store) DeleteSourcePageLinks(ctx domain.RequestContext, pageID string) (rows int64, err error) {
	return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_link WHERE c_orgid='%s' AND c_sourcesectionid='%s'", ctx.OrgID, pageID))
}

// DeleteSourceDocumentLinks removes saved links for given document.
func (s Store) DeleteSourceDocumentLinks(ctx domain.RequestContext, documentID string) (rows int64, err error) {
	return s.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM dmz_doc_link WHERE c_orgid='%s' AND c_sourcedocid='%s'", ctx.OrgID, documentID))
}

// DeleteLink removes saved link from the store.
func (s Store) DeleteLink(ctx domain.RequestContext, id string) (rows int64, err error) {
	return s.DeleteConstrained(ctx.Transaction, "dmz_doc_link", ctx.OrgID, id)
}

// SearchCandidates returns matching documents, sections and attachments using keywords.
func (s Store) SearchCandidates(ctx domain.RequestContext, keywords string) (docs []link.Candidate,
	pages []link.Candidate, attachments []link.Candidate, err error) {

	// find matching documents
	temp := []link.Candidate{}
	keywords = strings.TrimSpace(strings.ToLower(keywords))
	likeQuery := "LOWER(d.c_name) LIKE '%" + keywords + "%'"

	err = s.Runtime.Db.Select(&temp, s.Bind(`
		SELECT d.c_refid AS documentid, d.c_spaceid AS spaceid, d.c_name AS title, l.c_name AS context
        FROM dmz_doc d LEFT JOIN dmz_space l ON d.c_spaceid=l.c_refid
        WHERE l.c_orgid=? AND `+likeQuery+` AND d.c_spaceid IN
            (SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
                (SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
                UNION ALL
                SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role'
                AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
                )
            )
        ORDER BY title`),
		ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
	if err != nil {
		err = errors.Wrap(err, "execute search links 1")
		return
	}

	for _, r := range temp {
		c := link.Candidate{
			RefID:      uniqueid.Generate(),
			SpaceID:    r.SpaceID,
			DocumentID: r.DocumentID,
			TargetID:   r.DocumentID,
			LinkType:   "document",
			Title:      r.Title,
			Context:    r.Context,
		}

		docs = append(docs, c)
	}

	// find matching sections
	likeQuery = "LOWER(p.c_name) LIKE '%" + keywords + "%'"
	temp = []link.Candidate{}

	err = s.Runtime.Db.Select(&temp, s.Bind(`
        SELECT p.c_refid AS targetid, p.c_docid AS documentid, p.c_name AS title,
        p.c_type AS linktype, d.c_name AS context, d.c_spaceid AS spaceid
        FROM dmz_section p LEFT JOIN dmz_doc d ON d.c_refid=p.c_docid
        WHERE p.c_orgid=? AND `+likeQuery+` AND d.c_spaceid IN
                (SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
                    (SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
                    UNION ALL
                    SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role'
                    AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
                    )
                )
        ORDER BY title`),
		ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
	if err != nil {
		err = errors.Wrap(err, "execute search links 2")
		return
	}

	for _, r := range temp {
		c := link.Candidate{
			RefID:      uniqueid.Generate(),
			SpaceID:    r.SpaceID,
			DocumentID: r.DocumentID,
			TargetID:   r.TargetID,
			LinkType:   r.LinkType,
			Title:      r.Title,
			Context:    r.Context,
		}

		pages = append(pages, c)
	}

	// find matching attachments
	likeQuery = "LOWER(a.c_filename) LIKE '%" + keywords + "%'"
	temp = []link.Candidate{}

	err = s.Runtime.Db.Select(&temp, s.Bind(`
        SELECT a.c_refid AS targetid, a.c_docid AS documentid, a.c_filename AS title, a.c_extension AS context, d.c_spaceid AS spaceid
        FROM dmz_doc_attachment a LEFT JOIN dmz_doc d ON d.c_refid=a.c_docid
        WHERE a.c_orgid=? AND `+likeQuery+` AND d.c_spaceid IN
            (SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
                (SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
                UNION ALL
                SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role'
                AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
                )
            )
        ORDER BY a.c_filename`),
		ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
	if err != nil {
		err = errors.Wrap(err, "execute search links 3")
		return
	}

	for _, r := range temp {
		c := link.Candidate{
			RefID:      uniqueid.Generate(),
			SpaceID:    r.SpaceID,
			DocumentID: r.DocumentID,
			TargetID:   r.TargetID,
			LinkType:   "file",
			Title:      r.Title,
			Context:    r.Context,
		}

		attachments = append(attachments, c)
	}

	if len(docs) == 0 {
		docs = []link.Candidate{}
	}
	if len(pages) == 0 {
		pages = []link.Candidate{}
	}
	if len(attachments) == 0 {
		attachments = []link.Candidate{}
	}

	return
}
<|endoftext|>


<commit_before>package download

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/vcs"
)

// Download takes a user-provided string that represents a remote
// Go repository, and attempts to download it in a way similar to go get.
// It is forgiving in terms of the exact path given: the path may have
// a scheme or username, which will be trimmed.
func Download(path, dest string) (root *vcs.RepoRoot, err error) {
	return download(path, dest, false)
}

func download(path, dest string, firstAttempt bool) (root *vcs.RepoRoot, err error) {
	vcs.ShowCmd = true

	path, err = Clean(path)
	if err != nil {
		return root, err
	}

	root, err = vcs.RepoRootForImportPath(path, true)
	if err != nil {
		return root, err
	}

	localDirPath := filepath.Join(dest, root.Root, "..")

	err = os.MkdirAll(localDirPath, 0777)
	if err != nil {
		return root, err
	}

	fullLocalPath := filepath.Join(dest, root.Root)
	ex, err := exists(fullLocalPath)
	if err != nil {
		return root, err
	}
	if ex {
		log.Println("Update", root.Repo)
		err = root.VCS.Download(fullLocalPath)
		if err != nil && firstAttempt {
			// may have been rebased; we delete the directory, then try one more time:
			log.Printf("Failed to download %q (%v), trying again...", root.Repo, err.Error())
			err = os.Remove(fullLocalPath)
			return download(path, dest, false)
		} else if err != nil {
			return root, err
		}
	} else {
		log.Println("Create", root.Repo)

		err = root.VCS.Create(fullLocalPath, root.Repo)
		if err != nil {
			return root, err
		}
	}
	err = root.VCS.TagSync(fullLocalPath, "")
	if err != nil && firstAttempt {
		// may have been rebased; we delete the directory, then try one more time:
		log.Printf("Failed to update %q (%v), trying again...", root.Repo, err.Error())
		err = os.Remove(fullLocalPath)
		return download(path, dest, false)
	}
	return root, err
}

// Clean trims any URL parts, like the scheme or username, that might be present
// in a user-submitted URL
func Clean(path string) (string, error) {
	importPath := trimUsername(trimScheme(path))
	root, err := vcs.RepoRootForImportPath(importPath, true)
	if err != nil {
		return "", err
	}
	if root != nil && (root.Root == "" || root.Repo == "") {
		return root.Root, errors.New("Empty repo root")
	}
	return root.Root, err
}

// trimScheme removes a scheme (e.g. https://) from the URL for more
// convenient pasting from browsers.
func trimScheme(repo string) string {
	schemeSep := "://"
	schemeSepIdx := strings.Index(repo, schemeSep)
	if schemeSepIdx > -1 {
		return repo[schemeSepIdx+len(schemeSep):]
	}

	return repo
}

// trimUsername removes the username for a URL, if it is present
func trimUsername(repo string) string {
	usernameSep := "@"
	usernameSepIdx := strings.Index(repo, usernameSep)
	if usernameSepIdx > -1 {
		return repo[usernameSepIdx+len(usernameSep):]
	}

	return repo
}

// exists returns whether the given file or directory exists or not
// from http://stackoverflow.com/a/10510783
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}
<commit_msg>lowercase error message<commit_after>package download

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/vcs"
)

// Download takes a user-provided string that represents a remote
// Go repository, and attempts to download it in a way similar to go get.
// It is forgiving in terms of the exact path given: the path may have
// a scheme or username, which will be trimmed.
func Download(path, dest string) (root *vcs.RepoRoot, err error) {
	return download(path, dest, false)
}

func download(path, dest string, firstAttempt bool) (root *vcs.RepoRoot, err error) {
	vcs.ShowCmd = true

	path, err = Clean(path)
	if err != nil {
		return root, err
	}

	root, err = vcs.RepoRootForImportPath(path, true)
	if err != nil {
		return root, err
	}

	localDirPath := filepath.Join(dest, root.Root, "..")

	err = os.MkdirAll(localDirPath, 0777)
	if err != nil {
		return root, err
	}

	fullLocalPath := filepath.Join(dest, root.Root)
	ex, err := exists(fullLocalPath)
	if err != nil {
		return root, err
	}
	if ex {
		log.Println("Update", root.Repo)
		err = root.VCS.Download(fullLocalPath)
		if err != nil && firstAttempt {
			// may have been rebased; we delete the directory, then try one more time:
			log.Printf("Failed to download %q (%v), trying again...", root.Repo, err.Error())
			err = os.Remove(fullLocalPath)
			return download(path, dest, false)
		} else if err != nil {
			return root, err
		}
	} else {
		log.Println("Create", root.Repo)

		err = root.VCS.Create(fullLocalPath, root.Repo)
		if err != nil {
			return root, err
		}
	}
	err = root.VCS.TagSync(fullLocalPath, "")
	if err != nil && firstAttempt {
		// may have been rebased; we delete the directory, then try one more time:
		log.Printf("Failed to update %q (%v), trying again...", root.Repo, err.Error())
		err = os.Remove(fullLocalPath)
		return download(path, dest, false)
	}
	return root, err
}

// Clean trims any URL parts, like the scheme or username, that might be present
// in a user-submitted URL
func Clean(path string) (string, error) {
	importPath := trimUsername(trimScheme(path))
	root, err := vcs.RepoRootForImportPath(importPath, true)
	if err != nil {
		return "", err
	}
	if root != nil && (root.Root == "" || root.Repo == "") {
		return root.Root, errors.New("empty repo root")
	}
	return root.Root, err
}

// trimScheme removes a scheme (e.g. https://) from the URL for more
// convenient pasting from browsers.
func trimScheme(repo string) string {
	schemeSep := "://"
	schemeSepIdx := strings.Index(repo, schemeSep)
	if schemeSepIdx > -1 {
		return repo[schemeSepIdx+len(schemeSep):]
	}

	return repo
}

// trimUsername removes the username for a URL, if it is present
func trimUsername(repo string) string {
	usernameSep := "@"
	usernameSepIdx := strings.Index(repo, usernameSep)
	if usernameSepIdx > -1 {
		return repo[usernameSepIdx+len(usernameSep):]
	}

	return repo
}

// exists returns whether the given file or directory exists or not
// from http://stackoverflow.com/a/10510783
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}
<|endoftext|>


<commit_before>package download

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/blang/semver"
	"github.com/subutai-io/agent/log"
	"github.com/subutai-io/gorjun/config"
	"github.com/subutai-io/gorjun/db"
	"net/url"
)

// ListItem describes Gorjun entity. It can be APT package, Subutai template or Raw file.
type ListItem struct {
	ID           string            `json:"id"`
	Hash         hashsums          `json:"hash"`
	Size         int               `json:"size"`
	Date         time.Time         `json:"upload-date-formatted"`
	Timestamp    string            `json:"upload-date-timestamp,omitempty"`
	Name         string            `json:"name,omitempty"`
	Tags         []string          `json:"tags,omitempty"`
	Owner        []string          `json:"owner,omitempty"`
	Parent       string            `json:"parent,omitempty"`
	Version      string            `json:"version,omitempty"`
	Filename     string            `json:"filename,omitempty"`
	Prefsize     string            `json:"prefsize,omitempty"`
	Signature    map[string]string `json:"signature,omitempty"`
	Description  string            `json:"description,omitempty"`
	Architecture string            `json:"architecture,omitempty"`
}

type hashsums struct {
	Md5    string `json:"md5,omitempty"`
	Sha256 string `json:"sha256,omitempty"`
}

// Handler provides download functionality for all artifacts.
func Handler(repo string, w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	name := r.URL.Query().Get("name")
	if len(id) == 0 && len(name) == 0 {
		io.WriteString(w, "Please specify id or name")
		return
	} else if len(name) != 0 {
		id = db.LastHash(name, repo)
	}

	if len(db.Read(id)) > 0 && !db.Public(id) && !db.CheckShare(id, db.CheckToken(r.URL.Query().Get("token"))) {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Not found"))
		return
	}

	path := config.Storage.Path + id
	if md5, _ := db.Hash(id); len(md5) != 0 {
		path = config.Storage.Path + md5
	}

	f, err := os.Open(path)
	defer f.Close()

	if log.Check(log.WarnLevel, "Opening file "+config.Storage.Path+id, err) || len(id) == 0 {
		if len(config.CDN.Node) > 0 {
			client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
			resp, err := client.Get(config.CDN.Node + r.URL.RequestURI())
			if !log.Check(log.WarnLevel, "Getting file from CDN", err) {
				w.Header().Set("Content-Length", resp.Header.Get("Content-Length"))
				w.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
				w.Header().Set("Last-Modified", resp.Header.Get("Last-Modified"))
				w.Header().Set("Content-Disposition", resp.Header.Get("Content-Disposition"))

				io.Copy(w, resp.Body)
				resp.Body.Close()
				return
			}
		}

		w.WriteHeader(http.StatusNotFound)
		io.WriteString(w, "File not found")
		return
	}
	fi, _ := f.Stat()

	if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && fi.ModTime().Unix() <= t.Unix() {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	w.Header().Set("Content-Length", fmt.Sprint(fi.Size()))
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	w.Header().Set("Last-Modified", fi.ModTime().Format(http.TimeFormat))

	if name = db.Read(id); len(name) == 0 && len(config.CDN.Node) > 0 {
		httpclient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
		resp, err := httpclient.Get(config.CDN.Node + "/kurjun/rest/template/info?id=" + id)
		if !log.Check(log.WarnLevel, "Getting info from CDN", err) {
			var info ListItem
			rsp, err := ioutil.ReadAll(resp.Body)
			if log.Check(log.WarnLevel, "Reading from CDN response", err) {
				w.WriteHeader(http.StatusNotFound)
				io.WriteString(w, "File not found")
				return
			}
			if !log.Check(log.WarnLevel, "Decrypting request", json.Unmarshal([]byte(rsp), &info)) {
				w.Header().Set("Content-Disposition", "attachment; filename=\""+info.Filename+"\"")
			}
			resp.Body.Close()
		}
	} else {
		w.Header().Set("Content-Disposition", "attachment; filename=\""+db.Read(id)+"\"")
	}

	io.Copy(w, f)
}

// Info returns JSON formatted list of elements. It allows to apply some filters to Search.
func Info(repo string, r *http.Request) []byte {
	var items []ListItem
	var fullname bool
	var itemLatestVersion ListItem
	p := []int{0, 1000}
	id := r.URL.Query().Get("id")
	tag := r.URL.Query().Get("tag")
	name := r.URL.Query().Get("name")
	page := r.URL.Query().Get("page")
	owner := r.URL.Query().Get("owner")
	token := r.URL.Query().Get("token")
	subname := r.URL.Query().Get("subname")
	version := r.URL.Query().Get("version")
	verified := r.URL.Query().Get("verified")
	if len(subname) != 0 {
		name = subname
	}

	list := db.Search(name)
	if len(tag) > 0 {
		listByTag, err := db.Tag(tag)
		log.Check(log.DebugLevel, "Looking for artifacts with tag "+tag, err)
		list = intersect(list, listByTag)
	}
	if onlyOneParameterProvided("name", r) {
		verified = "true"
	}
	if len(name) > 0 && token == "" {
		verified = "true"
	}
	if len(id) > 0 {
		list = append(list[:0], id)
	} else if verified == "true" {
		items = append(items, getVerified(list, name, repo))
		items[0].Signature = db.FileSignatures(items[0].ID)
		output, err := json.Marshal(items)
		if err == nil && len(items) > 0 && items[0].ID != "" {
			return output
		}
		return nil
	}

	pstr := strings.Split(page, ",")
	p[0], _ = strconv.Atoi(pstr[0])
	if len(pstr) == 2 {
		p[1], _ = strconv.Atoi(pstr[1])
	}
	latestVersion, _ := semver.Make("")
	for _, k := range list {
		if (!db.Public(k) && !db.CheckShare(k, db.CheckToken(token))) ||
			(len(owner) > 0 && db.CheckRepo(owner, repo, k) == 0) ||
			db.CheckRepo("", repo, k) == 0 {
			continue
		}

		if p[0]--; p[0] > 0 {
			continue
		}

		item := FormatItem(db.Info(k), repo, name)
		if len(subname) == 0 && name == item.Name {
			if strings.HasSuffix(item.Version, version) || len(version) == 0 {
				items = []ListItem{item}
				fullname = true
				itemVersion, _ := semver.Make(item.Version)
				if itemVersion.GTE(latestVersion) {
					latestVersion = itemVersion
					itemLatestVersion = item
				}
			}
		} else if !fullname && (len(version) == 0 || item.Version == version) {
			items = append(items, item)
		}

		if len(items) >= p[1] {
			break
		}
	}
	if len(items) == 1 {
		if version == "" && repo == "template" && itemLatestVersion.ID != "" {
			items[0] = itemLatestVersion
		}
		items[0].Signature = db.FileSignatures(items[0].ID)
	}
	output, err := json.Marshal(items)
	if err != nil || string(output) == "null" {
		return nil
	}
	return output
}

func in(str string, list []string) bool {
	for _, s := range list {
		if s == str {
			return true
		}
	}
	return false
}

func getVerified(list []string, name, repo string) ListItem {
	latestVersion, _ := semver.Make("")
	var itemLatestVersion ListItem
	for _, k := range list {
		if info := db.Info(k); db.CheckRepo("", repo, k) > 0 {
			if info["name"] == name || (strings.HasPrefix(info["name"], name+"-subutai-template") && repo == "template") {
				for _, owner := range db.FileField(info["id"], "owner") {
					itemVersion, _ := semver.Make(info["version"])
					if in(owner, []string{"subutai", "jenkins", "docker"}) &&
						itemVersion.GTE(latestVersion) {
						latestVersion = itemVersion
						itemLatestVersion = FormatItem(db.Info(k), repo, name)
					}
				}
			}
		}
	}
	return itemLatestVersion
}

func FormatItem(info map[string]string, repo, name string) ListItem {
	if len(info["prefsize"]) == 0 && repo == "template" {
		info["prefsize"] = "tiny"
	}

	date, _ := time.Parse(time.RFC3339Nano, info["date"])
	timestamp := strconv.FormatInt(date.Unix(), 10)
	item := ListItem{
		ID:           info["id"],
		Date:         date,
		Hash:         hashsums{Md5: info["md5"], Sha256: info["sha256"]},
		Name:         strings.Split(info["name"], "-subutai-template")[0],
		Tags:         db.FileField(info["id"], "tags"),
		Owner:        db.FileField(info["id"], "owner"),
		Version:      info["version"],
		Filename:     info["name"],
		Parent:       info["parent"],
		Prefsize:     info["prefsize"],
		Architecture: strings.ToUpper(info["arch"]),
		Description:  info["Description"],
		Timestamp:    timestamp,
	}
	item.Size, _ = strconv.Atoi(info["size"])

	if repo == "apt" {
		item.Version = info["Version"]
		item.Architecture = info["Architecture"]
		item.Size, _ = strconv.Atoi(info["Size"])
	}
	if len(item.Hash.Md5) == 0 {
		item.Hash.Md5 = item.ID
	}
	return item
}

func intersect(listA, listB []string) (list []string) {
	mapA := map[string]bool{}
	for _, item := range listA {
		mapA[item] = true
	}
	for _, item := range listB {
		if mapA[item] {
			list = append(list, item)
		}
	}
	return list
}

func onlyOneParameterProvided(parameter string, r *http.Request) bool {
	u, _ := url.Parse(r.RequestURI)
	parameters, _ := url.ParseQuery(u.RawQuery)
	for key, _ := range parameters {
		if key != parameter {
			return false
		}
	}
	return len(parameters) > 0
}
<commit_msg>Hot-fix for empty name and token<commit_after>package download

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/blang/semver"
	"github.com/subutai-io/agent/log"
	"github.com/subutai-io/gorjun/config"
	"github.com/subutai-io/gorjun/db"
	"net/url"
)

// ListItem describes Gorjun entity. It can be APT package, Subutai template or Raw file.
type ListItem struct {
	ID           string            `json:"id"`
	Hash         hashsums          `json:"hash"`
	Size         int               `json:"size"`
	Date         time.Time         `json:"upload-date-formatted"`
	Timestamp    string            `json:"upload-date-timestamp,omitempty"`
	Name         string            `json:"name,omitempty"`
	Tags         []string          `json:"tags,omitempty"`
	Owner        []string          `json:"owner,omitempty"`
	Parent       string            `json:"parent,omitempty"`
	Version      string            `json:"version,omitempty"`
	Filename     string            `json:"filename,omitempty"`
	Prefsize     string            `json:"prefsize,omitempty"`
	Signature    map[string]string `json:"signature,omitempty"`
	Description  string            `json:"description,omitempty"`
	Architecture string            `json:"architecture,omitempty"`
}

type hashsums struct {
	Md5    string `json:"md5,omitempty"`
	Sha256 string `json:"sha256,omitempty"`
}

// Handler provides download functionality for all artifacts.
func Handler(repo string, w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	name := r.URL.Query().Get("name")
	if len(id) == 0 && len(name) == 0 {
		io.WriteString(w, "Please specify id or name")
		return
	} else if len(name) != 0 {
		id = db.LastHash(name, repo)
	}

	if len(db.Read(id)) > 0 && !db.Public(id) && !db.CheckShare(id, db.CheckToken(r.URL.Query().Get("token"))) {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Not found"))
		return
	}

	path := config.Storage.Path + id
	if md5, _ := db.Hash(id); len(md5) != 0 {
		path = config.Storage.Path + md5
	}

	f, err := os.Open(path)
	defer f.Close()

	if log.Check(log.WarnLevel, "Opening file "+config.Storage.Path+id, err) || len(id) == 0 {
		if len(config.CDN.Node) > 0 {
			client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
			resp, err := client.Get(config.CDN.Node + r.URL.RequestURI())
			if !log.Check(log.WarnLevel, "Getting file from CDN", err) {
				w.Header().Set("Content-Length", resp.Header.Get("Content-Length"))
				w.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
				w.Header().Set("Last-Modified", resp.Header.Get("Last-Modified"))
				w.Header().Set("Content-Disposition", resp.Header.Get("Content-Disposition"))

				io.Copy(w, resp.Body)
				resp.Body.Close()
				return
			}
		}

		w.WriteHeader(http.StatusNotFound)
		io.WriteString(w, "File not found")
		return
	}
	fi, _ := f.Stat()

	if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && fi.ModTime().Unix() <= t.Unix() {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	w.Header().Set("Content-Length", fmt.Sprint(fi.Size()))
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	w.Header().Set("Last-Modified", fi.ModTime().Format(http.TimeFormat))

	if name = db.Read(id); len(name) == 0 && len(config.CDN.Node) > 0 {
		httpclient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
		resp, err := httpclient.Get(config.CDN.Node + "/kurjun/rest/template/info?id=" + id)
		if !log.Check(log.WarnLevel, "Getting info from CDN", err) {
			var info ListItem
			rsp, err := ioutil.ReadAll(resp.Body)
			if log.Check(log.WarnLevel, "Reading from CDN response", err) {
				w.WriteHeader(http.StatusNotFound)
				io.WriteString(w, "File not found")
				return
			}
			if !log.Check(log.WarnLevel, "Decrypting request", json.Unmarshal([]byte(rsp), &info)) {
				w.Header().Set("Content-Disposition", "attachment; filename=\""+info.Filename+"\"")
			}
			resp.Body.Close()
		}
	} else {
		w.Header().Set("Content-Disposition", "attachment; filename=\""+db.Read(id)+"\"")
	}

	io.Copy(w, f)
}

// Info returns JSON formatted list of elements. It allows to apply some filters to Search.
func Info(repo string, r *http.Request) []byte {
	var items []ListItem
	var fullname bool
	var itemLatestVersion ListItem
	p := []int{0, 1000}
	id := r.URL.Query().Get("id")
	tag := r.URL.Query().Get("tag")
	name := r.URL.Query().Get("name")
	page := r.URL.Query().Get("page")
	owner := r.URL.Query().Get("owner")
	token := r.URL.Query().Get("token")
	subname := r.URL.Query().Get("subname")
	version := r.URL.Query().Get("version")
	verified := r.URL.Query().Get("verified")
	if len(subname) != 0 {
		name = subname
	}

	list := db.Search(name)
	if len(tag) > 0 {
		listByTag, err := db.Tag(tag)
		log.Check(log.DebugLevel, "Looking for artifacts with tag "+tag, err)
		list = intersect(list, listByTag)
	}
	if onlyOneParameterProvided("name", r) {
		verified = "true"
	}
	if len(name) > 0 && token == "" && owner == "" {
		verified = "true"
	}
	if len(id) > 0 {
		list = append(list[:0], id)
	} else if verified == "true" {
		items = append(items, getVerified(list, name, repo))
		items[0].Signature = db.FileSignatures(items[0].ID)
		output, err := json.Marshal(items)
		if err == nil && len(items) > 0 && items[0].ID != "" {
			return output
		}
		return nil
	}

	pstr := strings.Split(page, ",")
	p[0], _ = strconv.Atoi(pstr[0])
	if len(pstr) == 2 {
		p[1], _ = strconv.Atoi(pstr[1])
	}
	latestVersion, _ := semver.Make("")
	for _, k := range list {
		if (!db.Public(k) && !db.CheckShare(k, db.CheckToken(token))) ||
			(len(owner) > 0 && db.CheckRepo(owner, repo, k) == 0) ||
			db.CheckRepo("", repo, k) == 0 {
			continue
		}

		if p[0]--; p[0] > 0 {
			continue
		}

		item := FormatItem(db.Info(k), repo, name)
		if len(subname) == 0 && name == item.Name {
			if strings.HasSuffix(item.Version, version) || len(version) == 0 {
				items = []ListItem{item}
				fullname = true
				itemVersion, _ := semver.Make(item.Version)
				if itemVersion.GTE(latestVersion) {
					latestVersion = itemVersion
					itemLatestVersion = item
				}
			}
		} else if !fullname && (len(version) == 0 || item.Version == version) {
			items = append(items, item)
		}

		if len(items) >= p[1] {
			break
		}
	}
	if len(items) == 1 {
		if version == "" && repo == "template" && itemLatestVersion.ID != "" {
			items[0] = itemLatestVersion
		}
		items[0].Signature = db.FileSignatures(items[0].ID)
	}
	output, err := json.Marshal(items)
	if err != nil || string(output) == "null" {
		return nil
	}
	return output
}

func in(str string, list []string) bool {
	for _, s := range list {
		if s == str {
			return true
		}
	}
	return false
}

func getVerified(list []string, name, repo string) ListItem {
	latestVersion, _ := semver.Make("")
	var itemLatestVersion ListItem
	for _, k := range list {
		if info := db.Info(k); db.CheckRepo("", repo, k) > 0 {
			if info["name"] == name || (strings.HasPrefix(info["name"], name+"-subutai-template") && repo == "template") {
				for _, owner := range db.FileField(info["id"], "owner") {
					itemVersion, _ := semver.Make(info["version"])
					if in(owner, []string{"subutai", "jenkins", "docker"}) &&
						itemVersion.GTE(latestVersion) {
						latestVersion = itemVersion
						itemLatestVersion = FormatItem(db.Info(k), repo, name)
					}
				}
			}
		}
	}
	return itemLatestVersion
}

func FormatItem(info map[string]string, repo, name string) ListItem {
	if len(info["prefsize"]) == 0 && repo == "template" {
		info["prefsize"] = "tiny"
	}

	date, _ := time.Parse(time.RFC3339Nano, info["date"])
	timestamp := strconv.FormatInt(date.Unix(), 10)
	item := ListItem{
		ID:           info["id"],
		Date:         date,
		Hash:         hashsums{Md5: info["md5"], Sha256: info["sha256"]},
		Name:         strings.Split(info["name"], "-subutai-template")[0],
		Tags:         db.FileField(info["id"], "tags"),
		Owner:        db.FileField(info["id"], "owner"),
		Version:      info["version"],
		Filename:     info["name"],
		Parent:       info["parent"],
		Prefsize:     info["prefsize"],
		Architecture: strings.ToUpper(info["arch"]),
		Description:  info["Description"],
		Timestamp:    timestamp,
	}
	item.Size, _ = strconv.Atoi(info["size"])

	if repo == "apt" {
		item.Version = info["Version"]
		item.Architecture = info["Architecture"]
		item.Size, _ = strconv.Atoi(info["Size"])
	}
	if len(item.Hash.Md5) == 0 {
		item.Hash.Md5 = item.ID
	}
	return item
}

func intersect(listA, listB []string) (list []string) {
	mapA := map[string]bool{}
	for _, item := range listA {
		mapA[item] = true
	}
	for _, item := range listB {
		if mapA[item] {
			list = append(list, item)
		}
	}
	return list
}

func onlyOneParameterProvided(parameter string, r *http.Request) bool {
	u, _ := url.Parse(r.RequestURI)
	parameters, _ := url.ParseQuery(u.RawQuery)
	for key, _ := range parameters {
		if key != parameter {
			return false
		}
	}
	return len(parameters) > 0
}
<|endoftext|>


<commit_before>package wundergo_integration_test

import (
	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/robdimsdale/wundergo"
)

var _ = Describe("basic task functionality", func() {
	var (
		newList wundergo.List
		newTask wundergo.Task
		err     error
	)

	BeforeEach(func() {
		By("Creating a new list")
		uuid1, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		newListTitle := uuid1.String()

		newList, err = client.CreateList(newListTitle)
		Expect(err).NotTo(HaveOccurred())

		By("Creating task in new list")
		uuid, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		newTaskTitle := uuid.String()

		Eventually(func() error {
			newTask, err = client.CreateTask(
				newTaskTitle,
				newList.ID,
				0,
				false,
				"",
				0,
				"1970-01-01",
				false,
			)
			return err
		}).ShouldNot(HaveOccurred())
	})

	AfterEach(func() {
		By("Deleting task")
		Eventually(func() error {
			newTask, err = client.Task(newTask.ID)
			return client.DeleteTask(newTask)
		}).Should(Succeed())

		var tasks []wundergo.Task
		Eventually(func() (bool, error) {
			tasks, err = client.TasksForListID(newList.ID)
			return taskContains(tasks, newTask), err
		}).Should(BeFalse())

		By("Deleting new list")
		Eventually(func() error {
			newList, err = client.List(newList.ID)
			return client.DeleteList(newList)
		}).Should(Succeed())

		var lists []wundergo.List
		Eventually(func() (bool, error) {
			lists, err = client.Lists()
			return listContains(lists, newList), err
		}).Should(BeFalse())
	})

	Describe("moving a tasks between lists", func() {
		var secondList wundergo.List

		BeforeEach(func() {
			By("Creating a second list")
			uuid1, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			secondListTitle := uuid1.String()

			secondList, err = client.CreateList(secondListTitle)
			Expect(err).NotTo(HaveOccurred())
		})

		AfterEach(func() {
			By("Deleting second list")
			Eventually(func() error {
				secondList, err = client.List(secondList.ID)
				return client.DeleteList(secondList)
			}).Should(Succeed())

			var lists []wundergo.List
			Eventually(func() (bool, error) {
				lists, err = client.Lists()
				return listContains(lists, secondList), err
			}).Should(BeFalse())
		})

		It("can move a task between lists", func() {
			By("Moving task to second list")
			newTask.ListID = secondList.ID
			Eventually(func() error {
				newTask, err = client.UpdateTask(newTask)
				return err
			}).Should(Succeed())

			By("Verifying task appears in tasks for second list")
			var completedTasksForSecondList []wundergo.Task
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForSecondList, err = client.CompletedTasksForListID(secondList.ID, showCompletedTasks)
				return taskContains(completedTasksForSecondList, newTask), err
			}).Should(BeTrue())

			By("Verifying task does not appear in tasks for first list")
			var completedTasksForFirstList []wundergo.Task
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForFirstList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
				return taskContains(completedTasksForFirstList, newTask), err
			}).Should(BeFalse())

			By("Moving task back to first list")
			newTask.ListID = newList.ID
			Eventually(func() error {
				newTask, err = client.UpdateTask(newTask)
				return err
			}).Should(Succeed())

			By("Verifying task does not appear in tasks for second list")
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForSecondList, err = client.CompletedTasksForListID(secondList.ID, showCompletedTasks)
				return taskContains(completedTasksForSecondList, newTask), err
			}).Should(BeFalse())

			By("Verifying task does appear in tasks for first list")
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForFirstList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
				return taskContains(completedTasksForFirstList, newTask), err
			}).Should(BeTrue())
		})
	})

	It("can complete tasks", func() {
		var completedTasksForList []wundergo.Task
		showCompletedTasks := true
		Eventually(func() (bool, error) {
			completedTasksForList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
			return taskContains(completedTasksForList, newTask), err
		}).Should(BeFalse())

		By("Completing task")
		newTask.Completed = true
		Eventually(func() error {
			newTask, err = client.UpdateTask(newTask)
			return err
		}).Should(Succeed())

		By("Verifying task appears in completed tasks for list")
		Eventually(func() (bool, error) {
			completedTasksForList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
			return taskContains(completedTasksForList, newTask), err
		}).Should(BeTrue())

		By("Verifying task appears in completed tasks")
		var completedTasks []wundergo.Task
		Eventually(func() (bool, error) {
			completedTasks, err = client.CompletedTasks(showCompletedTasks)
			return taskContains(completedTasks, newTask), err
		}).Should(BeTrue())
	})

	It("can update tasks", func() {
		By("Setting properties")
		newTask.DueDate = "1971-01-01"
		newTask.Starred = true
		newTask.Completed = true
		newTask.RecurrenceType = "week"
		newTask.RecurrenceCount = 2

		By("Updating task")
		Eventually(func() error {
			newTask, err = client.UpdateTask(newTask)
			return err
		}).Should(Succeed())

		By("Getting task again")
		var taskAgain wundergo.Task
		Eventually(func() error {
			taskAgain, err = client.Task(newTask.ID)
			return err
		}).Should(Succeed())

		By("Ensuring properties are set")
		Expect(taskAgain.DueDate).Should(Equal("1971-01-01"))
		Expect(taskAgain.Starred).Should(BeTrue())
		Expect(taskAgain.Completed).Should(BeTrue())
		Expect(taskAgain.RecurrenceType).Should(Equal("week"))
		Expect(taskAgain.RecurrenceCount).Should(Equal(uint(2)))

		By("Resetting properties")
		taskAgain.DueDate = ""
		taskAgain.Starred = false
		taskAgain.Completed = false
		taskAgain.RecurrenceType = ""
		taskAgain.RecurrenceCount = 0

		By("Updating task")
		Eventually(func() error {
			taskAgain, err = client.UpdateTask(taskAgain)
			return err
		}).Should(Succeed())

		By("Verifying properties are reset")
		Expect(taskAgain.DueDate).Should(Equal(""))
		Expect(taskAgain.Starred).Should(BeFalse())
		Expect(taskAgain.Completed).Should(BeFalse())
		Expect(taskAgain.RecurrenceType).Should(Equal(""))
		Expect(taskAgain.RecurrenceCount).Should(Equal(uint(0)))
	})

	It("can perform subtask CRUD", func() {
		By("Creating subtask")
		var subtask wundergo.Subtask
		subtaskComplete := false
		Eventually(func() error {
			subtask, err = client.CreateSubtask("mySubtaskTitle", newTask.ID, subtaskComplete)
			return err
		}).Should(Succeed())

		By("Getting subtask")
		var aSubtask wundergo.Subtask
		Eventually(func() error {
			aSubtask, err = client.Subtask(subtask.ID)
			return err
		}).Should(Succeed())
		Expect(aSubtask.ID).To(Equal(subtask.ID))

		By("Validating subtask exists in all subtasks")
		Eventually(func() bool {
			// It is statistically probable that one of the lists will
			// be deleted, so we ignore error here.
			subtasks, _ := client.Subtasks()
			return subtaskContains(subtasks, subtask)
		}).Should(BeTrue())

		By("Validating subtask exists in subtasks for list")
		Eventually(func() (bool, error) {
			subtasksForList, err := client.SubtasksForListID(newList.ID)
			return subtaskContains(subtasksForList, subtask), err
		}).Should(BeTrue())

		By("Validating subtask exists in subtasks for task")
		Eventually(func() (bool, error) {
			subtasksForTask, err := client.SubtasksForTaskID(newTask.ID)
			return subtaskContains(subtasksForTask, subtask), err
		}).Should(BeTrue())

		By("Completing subtask")
		subtask.Completed = true
		Eventually(func() error {
			subtask, err = client.UpdateSubtask(subtask)
			return err
		}).Should(Succeed())

		By("Validating subtask exists in all completed subtasks")
		showCompletedSubtasks := true
		Eventually(func() (bool, error) {
			subtasks, err := client.CompletedSubtasks(showCompletedSubtasks)
			return subtaskContains(subtasks, subtask), err
		}).Should(BeTrue())

		By("Validating subtask exists in completed subtasks for list")
		Eventually(func() (bool, error) {
			subtasksForList, err := client.CompletedSubtasksForListID(newList.ID, showCompletedSubtasks)
			return subtaskContains(subtasksForList, subtask), err
		}).Should(BeTrue())

		By("Validating subtask exists in completed subtasks for task")
		Eventually(func() (bool, error) {
			subtasksForTask, err := client.CompletedSubtasksForTaskID(newTask.ID, showCompletedSubtasks)
			return subtaskContains(subtasksForTask, subtask), err
		}).Should(BeTrue())

		By("Deleting subtask")
		Eventually(func() error {
			return client.DeleteSubtask(subtask)
		}).Should(Succeed())
	})
})

func subtaskContains(subtasks []wundergo.Subtask, subtask wundergo.Subtask) bool {
	for _, t := range subtasks {
		if t.ID == subtask.ID {
			return true
		}
	}
	return false
}
<commit_msg>More ignoring of errors during integration tests.<commit_after>package wundergo_integration_test

import (
	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/robdimsdale/wundergo"
)

var _ = Describe("basic task functionality", func() {
	var (
		newList wundergo.List
		newTask wundergo.Task
		err     error
	)

	BeforeEach(func() {
		By("Creating a new list")
		uuid1, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		newListTitle := uuid1.String()

		newList, err = client.CreateList(newListTitle)
		Expect(err).NotTo(HaveOccurred())

		By("Creating task in new list")
		uuid, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		newTaskTitle := uuid.String()

		Eventually(func() error {
			newTask, err = client.CreateTask(
				newTaskTitle,
				newList.ID,
				0,
				false,
				"",
				0,
				"1970-01-01",
				false,
			)
			return err
		}).ShouldNot(HaveOccurred())
	})

	AfterEach(func() {
		By("Deleting task")
		Eventually(func() error {
			newTask, err = client.Task(newTask.ID)
			return client.DeleteTask(newTask)
		}).Should(Succeed())

		var tasks []wundergo.Task
		Eventually(func() (bool, error) {
			tasks, err = client.TasksForListID(newList.ID)
			return taskContains(tasks, newTask), err
		}).Should(BeFalse())

		By("Deleting new list")
		Eventually(func() error {
			newList, err = client.List(newList.ID)
			return client.DeleteList(newList)
		}).Should(Succeed())

		var lists []wundergo.List
		Eventually(func() (bool, error) {
			lists, err = client.Lists()
			return listContains(lists, newList), err
		}).Should(BeFalse())
	})

	Describe("moving a tasks between lists", func() {
		var secondList wundergo.List

		BeforeEach(func() {
			By("Creating a second list")
			uuid1, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())
			secondListTitle := uuid1.String()

			secondList, err = client.CreateList(secondListTitle)
			Expect(err).NotTo(HaveOccurred())
		})

		AfterEach(func() {
			By("Deleting second list")
			Eventually(func() error {
				secondList, err = client.List(secondList.ID)
				return client.DeleteList(secondList)
			}).Should(Succeed())

			var lists []wundergo.List
			Eventually(func() (bool, error) {
				lists, err = client.Lists()
				return listContains(lists, secondList), err
			}).Should(BeFalse())
		})

		It("can move a task between lists", func() {
			By("Moving task to second list")
			newTask.ListID = secondList.ID
			Eventually(func() error {
				newTask, err = client.UpdateTask(newTask)
				return err
			}).Should(Succeed())

			By("Verifying task appears in tasks for second list")
			var completedTasksForSecondList []wundergo.Task
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForSecondList, err = client.CompletedTasksForListID(secondList.ID, showCompletedTasks)
				return taskContains(completedTasksForSecondList, newTask), err
			}).Should(BeTrue())

			By("Verifying task does not appear in tasks for first list")
			var completedTasksForFirstList []wundergo.Task
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForFirstList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
				return taskContains(completedTasksForFirstList, newTask), err
			}).Should(BeFalse())

			By("Moving task back to first list")
			newTask.ListID = newList.ID
			Eventually(func() error {
				newTask, err = client.UpdateTask(newTask)
				return err
			}).Should(Succeed())

			By("Verifying task does not appear in tasks for second list")
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForSecondList, err = client.CompletedTasksForListID(secondList.ID, showCompletedTasks)
				return taskContains(completedTasksForSecondList, newTask), err
			}).Should(BeFalse())

			By("Verifying task does appear in tasks for first list")
			Eventually(func() (bool, error) {
				showCompletedTasks := false
				completedTasksForFirstList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
				return taskContains(completedTasksForFirstList, newTask), err
			}).Should(BeTrue())
		})
	})

	It("can complete tasks", func() {
		var completedTasksForList []wundergo.Task
		showCompletedTasks := true
		Eventually(func() (bool, error) {
			completedTasksForList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
			return taskContains(completedTasksForList, newTask), err
		}).Should(BeFalse())

		By("Completing task")
		newTask.Completed = true
		Eventually(func() error {
			newTask, err = client.UpdateTask(newTask)
			return err
		}).Should(Succeed())

		By("Verifying task appears in completed tasks for list")
		Eventually(func() (bool, error) {
			completedTasksForList, err = client.CompletedTasksForListID(newList.ID, showCompletedTasks)
			return taskContains(completedTasksForList, newTask), err
		}).Should(BeTrue())

		By("Verifying task appears in completed tasks")
		var completedTasks []wundergo.Task
		Eventually(func() (bool, error) {
			completedTasks, err = client.CompletedTasks(showCompletedTasks)
			return taskContains(completedTasks, newTask), err
		}).Should(BeTrue())
	})

	It("can update tasks", func() {
		By("Setting properties")
		newTask.DueDate = "1971-01-01"
		newTask.Starred = true
		newTask.Completed = true
		newTask.RecurrenceType = "week"
		newTask.RecurrenceCount = 2

		By("Updating task")
		Eventually(func() error {
			newTask, err = client.UpdateTask(newTask)
			return err
		}).Should(Succeed())

		By("Getting task again")
		var taskAgain wundergo.Task
		Eventually(func() error {
			taskAgain, err = client.Task(newTask.ID)
			return err
		}).Should(Succeed())

		By("Ensuring properties are set")
		Expect(taskAgain.DueDate).Should(Equal("1971-01-01"))
		Expect(taskAgain.Starred).Should(BeTrue())
		Expect(taskAgain.Completed).Should(BeTrue())
		Expect(taskAgain.RecurrenceType).Should(Equal("week"))
		Expect(taskAgain.RecurrenceCount).Should(Equal(uint(2)))

		By("Resetting properties")
		taskAgain.DueDate = ""
		taskAgain.Starred = false
		taskAgain.Completed = false
		taskAgain.RecurrenceType = ""
		taskAgain.RecurrenceCount = 0

		By("Updating task")
		Eventually(func() error {
			taskAgain, err = client.UpdateTask(taskAgain)
			return err
		}).Should(Succeed())

		By("Verifying properties are reset")
		Expect(taskAgain.DueDate).Should(Equal(""))
		Expect(taskAgain.Starred).Should(BeFalse())
		Expect(taskAgain.Completed).Should(BeFalse())
		Expect(taskAgain.RecurrenceType).Should(Equal(""))
		Expect(taskAgain.RecurrenceCount).Should(Equal(uint(0)))
	})

	It("can perform subtask CRUD", func() {
		By("Creating subtask")
		var subtask wundergo.Subtask
		subtaskComplete := false
		Eventually(func() error {
			subtask, err = client.CreateSubtask("mySubtaskTitle", newTask.ID, subtaskComplete)
			return err
		}).Should(Succeed())

		By("Getting subtask")
		var aSubtask wundergo.Subtask
		Eventually(func() error {
			aSubtask, err = client.Subtask(subtask.ID)
			return err
		}).Should(Succeed())
		Expect(aSubtask.ID).To(Equal(subtask.ID))

		By("Validating subtask exists in all subtasks")
		Eventually(func() bool {
			// It is statistically probable that one of the lists will
			// be deleted, so we ignore error here.
			subtasks, _ := client.Subtasks()
			return subtaskContains(subtasks, subtask)
		}).Should(BeTrue())

		By("Validating subtask exists in subtasks for list")
		Eventually(func() (bool, error) {
			subtasksForList, err := client.SubtasksForListID(newList.ID)
			return subtaskContains(subtasksForList, subtask), err
		}).Should(BeTrue())

		By("Validating subtask exists in subtasks for task")
		Eventually(func() (bool, error) {
			subtasksForTask, err := client.SubtasksForTaskID(newTask.ID)
			return subtaskContains(subtasksForTask, subtask), err
		}).Should(BeTrue())

		By("Completing subtask")
		subtask.Completed = true
		Eventually(func() error {
			subtask, err = client.UpdateSubtask(subtask)
			return err
		}).Should(Succeed())

		By("Validating subtask exists in all completed subtasks")
		showCompletedSubtasks := true
		Eventually(func() bool {
			// It is statistically probable that one of the lists will
			// be deleted, so we ignore error here.
			subtasks, _ := client.CompletedSubtasks(showCompletedSubtasks)
			return subtaskContains(subtasks, subtask)
		}).Should(BeTrue())

		By("Validating subtask exists in completed subtasks for list")
		Eventually(func() (bool, error) {
			subtasksForList, err := client.CompletedSubtasksForListID(newList.ID, showCompletedSubtasks)
			return subtaskContains(subtasksForList, subtask), err
		}).Should(BeTrue())

		By("Validating subtask exists in completed subtasks for task")
		Eventually(func() (bool, error) {
			subtasksForTask, err := client.CompletedSubtasksForTaskID(newTask.ID, showCompletedSubtasks)
			return subtaskContains(subtasksForTask, subtask), err
		}).Should(BeTrue())

		By("Deleting subtask")
		Eventually(func() error {
			return client.DeleteSubtask(subtask)
		}).Should(Succeed())
	})
})

func subtaskContains(subtasks []wundergo.Subtask, subtask wundergo.Subtask) bool {
	for _, t := range subtasks {
		if t.ID == subtask.ID {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package wl_integration_test

import (
	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/robdimsdale/wl"
)

var _ = Describe("basic user functionality", func() {
	It("can update the user's name", func() {
		By("Creating a new random user name")
		uuid1, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		newUserName := uuid1.String()

		By("Getting user")
		var user wl.User
		Eventually(func() error {
			user, err = client.User()
			return err
		}).Should(Succeed())

		By("Updating user")
		var updatedUser wl.User
		user.Name = "test-" + newUserName
		Eventually(func() error {
			updatedUser, err = client.UpdateUser(user)
			return err
		}).Should(Succeed())

		Expect(updatedUser.ID).To(Equal(user.ID))
	})
})
<commit_msg>Make user integration test more resilient to 409 errors.<commit_after>package wl_integration_test

import (
	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/robdimsdale/wl"
)

var _ = Describe("basic user functionality", func() {
	It("can update the user's name", func() {
		By("Creating a new random user name")
		uuid1, err := uuid.NewV4()
		Expect(err).NotTo(HaveOccurred())
		newUserName := uuid1.String()

		By("Getting and updating user")
		var user wl.User
		var updatedUser wl.User
		Eventually(func() error {
			user, err = client.User()
			user.Name = "test-" + newUserName
			updatedUser, err = client.UpdateUser(user)
			return err
		}).Should(Succeed())

		Expect(updatedUser.ID).To(Equal(user.ID))
	})
})
<|endoftext|>


<commit_before>package statsd

import (
	"bytes"
	"log"
	"reflect"
	"strings"
	"testing"
	"time"
)

var statsdSubStatterPacketTests = []struct {
	Prefix    string
	SubPrefix string
	Method    string
	Stat      string
	Value     interface{}
	Rate      float32
	Expected  string
}{
	{"test", "sub", "Gauge", "gauge", int64(1), 1.0, "test.sub.gauge:1|g"},
	{"test", "sub", "Inc", "count", int64(1), 0.999999, "test.sub.count:1|c|@0.999999"},
	{"test", "sub", "Inc", "count", int64(1), 1.0, "test.sub.count:1|c"},
	{"test", "sub", "Dec", "count", int64(1), 1.0, "test.sub.count:-1|c"},
	{"test", "sub", "Timing", "timing", int64(1), 1.0, "test.sub.timing:1|ms"},
	{"test", "sub", "TimingDuration", "timing", 1500 * time.Microsecond, 1.0, "test.sub.timing:1.5|ms"},
	{"test", "sub", "TimingDuration", "timing", 3 * time.Microsecond, 1.0, "test.sub.timing:0.003|ms"},
	{"test", "sub", "Set", "strset", "pickle", 1.0, "test.sub.strset:pickle|s"},
	{"test", "sub", "SetInt", "intset", int64(1), 1.0, "test.sub.intset:1|s"},
	{"test", "sub", "GaugeDelta", "gauge", int64(1), 1.0, "test.sub.gauge:+1|g"},
	{"test", "sub", "GaugeDelta", "gauge", int64(-1), 1.0, "test.sub.gauge:-1|g"},
	// empty sub prefix -- note: not used in subsub tests
	{"test", "", "Inc", "count", int64(1), 1.0, "test.count:1|c"},
}

func TestSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s := c.NewSubStatter(tt.SubPrefix)
		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		_, _, err = l.ReadFrom(data)
		if err != nil {
			c.Close()
			t.Fatal(err)
		}

		data = bytes.TrimRight(data, "\x00")
		if bytes.Equal(data, []byte(tt.Expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'", tt.Method, data, tt.Expected)
		}
		c.Close()
	}
}

func TestMultSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		// ignore empty sub test for this, as there is nothing to regex sub
		if tt.SubPrefix == "" {
			continue
		}
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s1 := c.NewSubStatter("sub1")
		s2 := c.NewSubStatter("sub2")

		responses := [][]byte{}
		for _, s := range []SubStatter{s1, s2} {
			method := reflect.ValueOf(s).MethodByName(tt.Method)
			e := method.Call([]reflect.Value{
				reflect.ValueOf(tt.Stat),
				reflect.ValueOf(tt.Value),
				reflect.ValueOf(tt.Rate)})[0]
			errInter := e.Interface()
			if errInter != nil {
				t.Fatal(errInter.(error))
			}

			data := make([]byte, 128)
			_, _, err = l.ReadFrom(data)
			if err != nil {
				c.Close()
				t.Fatal(err)
			}

			data = bytes.TrimRight(data, "\x00")
			responses = append(responses, data)
		}

		expected := strings.Replace(tt.Expected, "sub.", "sub1.", -1)
		if bytes.Equal(responses[0], []byte(expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'",
				tt.Method, responses[0], tt.Expected)
		}

		expected = strings.Replace(tt.Expected, "sub.", "sub2.", -1)
		if bytes.Equal(responses[1], []byte(expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'",
				tt.Method, responses[1], tt.Expected)
		}
		c.Close()
	}
}

func TestSubSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		// ignore empty sub test for this, as there is nothing to regex sub
		if tt.SubPrefix == "" {
			continue
		}
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s := c.NewSubStatter(tt.SubPrefix).NewSubStatter("sub2")

		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		_, _, err = l.ReadFrom(data)
		if err != nil {
			c.Close()
			t.Fatal(err)
		}

		data = bytes.TrimRight(data, "\x00")
		expected := strings.Replace(tt.Expected, "sub.", "sub.sub2.", -1)
		if bytes.Equal(data, []byte(expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'", tt.Method, data, tt.Expected)
		}
		c.Close()
	}
}

func TestSubStatterClosedClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
		s := c.NewSubStatter(tt.SubPrefix)
		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter == nil {
			t.Fatal("Expected error but got none")
		}
	}
}

func TestNilSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()

	for _, tt := range statsdSubStatterPacketTests {
		var c *Client
		s := c.NewSubStatter(tt.SubPrefix)

		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		n, _, err := l.ReadFrom(data)
		// this is expected to error, since there should
		// be no udp data sent, so the read will time out
		if err == nil || n != 0 {
			c.Close()
			t.Fatal(err)
		}
		c.Close()
	}
}

func TestNoopSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		c, err := NewNoopClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s := c.NewSubStatter(tt.SubPrefix)
		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		n, _, err := l.ReadFrom(data)
		// this is expected to error, since there should
		// be no udp data sent, so the read will time out
		if err == nil || n != 0 {
			c.Close()
			t.Fatal(err)
		}
		c.Close()
	}
}

func ExampleClient_substatter() {
	// first create a client
	client, err := NewClient("127.0.0.1:8125", "test-client")
	// handle any errors
	if err != nil {
		log.Fatal(err)
	}
	// make sure to clean up
	defer client.Close()

	// create a substatter
	subclient := NewSubStatter("sub")
	// send a stat
	err = subclient.Inc("stat1", 42, 1.0)
	// handle any errors
	if err != nil {
		log.Printf("Error sending metric: %+v", err)
	}
}
<commit_msg>fix example<commit_after>package statsd

import (
	"bytes"
	"log"
	"reflect"
	"strings"
	"testing"
	"time"
)

var statsdSubStatterPacketTests = []struct {
	Prefix    string
	SubPrefix string
	Method    string
	Stat      string
	Value     interface{}
	Rate      float32
	Expected  string
}{
	{"test", "sub", "Gauge", "gauge", int64(1), 1.0, "test.sub.gauge:1|g"},
	{"test", "sub", "Inc", "count", int64(1), 0.999999, "test.sub.count:1|c|@0.999999"},
	{"test", "sub", "Inc", "count", int64(1), 1.0, "test.sub.count:1|c"},
	{"test", "sub", "Dec", "count", int64(1), 1.0, "test.sub.count:-1|c"},
	{"test", "sub", "Timing", "timing", int64(1), 1.0, "test.sub.timing:1|ms"},
	{"test", "sub", "TimingDuration", "timing", 1500 * time.Microsecond, 1.0, "test.sub.timing:1.5|ms"},
	{"test", "sub", "TimingDuration", "timing", 3 * time.Microsecond, 1.0, "test.sub.timing:0.003|ms"},
	{"test", "sub", "Set", "strset", "pickle", 1.0, "test.sub.strset:pickle|s"},
	{"test", "sub", "SetInt", "intset", int64(1), 1.0, "test.sub.intset:1|s"},
	{"test", "sub", "GaugeDelta", "gauge", int64(1), 1.0, "test.sub.gauge:+1|g"},
	{"test", "sub", "GaugeDelta", "gauge", int64(-1), 1.0, "test.sub.gauge:-1|g"},
	// empty sub prefix -- note: not used in subsub tests
	{"test", "", "Inc", "count", int64(1), 1.0, "test.count:1|c"},
}

func TestSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s := c.NewSubStatter(tt.SubPrefix)
		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		_, _, err = l.ReadFrom(data)
		if err != nil {
			c.Close()
			t.Fatal(err)
		}

		data = bytes.TrimRight(data, "\x00")
		if bytes.Equal(data, []byte(tt.Expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'", tt.Method, data, tt.Expected)
		}
		c.Close()
	}
}

func TestMultSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		// ignore empty sub test for this, as there is nothing to regex sub
		if tt.SubPrefix == "" {
			continue
		}
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s1 := c.NewSubStatter("sub1")
		s2 := c.NewSubStatter("sub2")

		responses := [][]byte{}
		for _, s := range []SubStatter{s1, s2} {
			method := reflect.ValueOf(s).MethodByName(tt.Method)
			e := method.Call([]reflect.Value{
				reflect.ValueOf(tt.Stat),
				reflect.ValueOf(tt.Value),
				reflect.ValueOf(tt.Rate)})[0]
			errInter := e.Interface()
			if errInter != nil {
				t.Fatal(errInter.(error))
			}

			data := make([]byte, 128)
			_, _, err = l.ReadFrom(data)
			if err != nil {
				c.Close()
				t.Fatal(err)
			}

			data = bytes.TrimRight(data, "\x00")
			responses = append(responses, data)
		}

		expected := strings.Replace(tt.Expected, "sub.", "sub1.", -1)
		if bytes.Equal(responses[0], []byte(expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'",
				tt.Method, responses[0], tt.Expected)
		}

		expected = strings.Replace(tt.Expected, "sub.", "sub2.", -1)
		if bytes.Equal(responses[1], []byte(expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'",
				tt.Method, responses[1], tt.Expected)
		}
		c.Close()
	}
}

func TestSubSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		// ignore empty sub test for this, as there is nothing to regex sub
		if tt.SubPrefix == "" {
			continue
		}
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s := c.NewSubStatter(tt.SubPrefix).NewSubStatter("sub2")

		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		_, _, err = l.ReadFrom(data)
		if err != nil {
			c.Close()
			t.Fatal(err)
		}

		data = bytes.TrimRight(data, "\x00")
		expected := strings.Replace(tt.Expected, "sub.", "sub.sub2.", -1)
		if bytes.Equal(data, []byte(expected)) != true {
			c.Close()
			t.Fatalf("%s got '%s' expected '%s'", tt.Method, data, tt.Expected)
		}
		c.Close()
	}
}

func TestSubStatterClosedClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		c, err := NewClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
		s := c.NewSubStatter(tt.SubPrefix)
		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter == nil {
			t.Fatal("Expected error but got none")
		}
	}
}

func TestNilSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()

	for _, tt := range statsdSubStatterPacketTests {
		var c *Client
		s := c.NewSubStatter(tt.SubPrefix)

		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		n, _, err := l.ReadFrom(data)
		// this is expected to error, since there should
		// be no udp data sent, so the read will time out
		if err == nil || n != 0 {
			c.Close()
			t.Fatal(err)
		}
		c.Close()
	}
}

func TestNoopSubStatterClient(t *testing.T) {
	l, err := newUDPListener("127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	for _, tt := range statsdSubStatterPacketTests {
		c, err := NewNoopClient(l.LocalAddr().String(), tt.Prefix)
		if err != nil {
			t.Fatal(err)
		}
		s := c.NewSubStatter(tt.SubPrefix)
		method := reflect.ValueOf(s).MethodByName(tt.Method)
		e := method.Call([]reflect.Value{
			reflect.ValueOf(tt.Stat),
			reflect.ValueOf(tt.Value),
			reflect.ValueOf(tt.Rate)})[0]
		errInter := e.Interface()
		if errInter != nil {
			t.Fatal(errInter.(error))
		}

		data := make([]byte, 128)
		n, _, err := l.ReadFrom(data)
		// this is expected to error, since there should
		// be no udp data sent, so the read will time out
		if err == nil || n != 0 {
			c.Close()
			t.Fatal(err)
		}
		c.Close()
	}
}

func ExampleClient_substatter() {
	// first create a client
	client, err := NewClient("127.0.0.1:8125", "test-client")
	// handle any errors
	if err != nil {
		log.Fatal(err)
	}
	// make sure to clean up
	defer client.Close()

	// create a substatter
	subclient := client.NewSubStatter("sub")
	// send a stat
	err = subclient.Inc("stat1", 42, 1.0)
	// handle any errors
	if err != nil {
		log.Printf("Error sending metric: %+v", err)
	}
}
<|endoftext|>