<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"k8s.io/kubernetes/pkg/api"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/labels"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	smallRCSize       = 5
	mediumRCSize      = 30
	bigRCSize         = 250
	smallRCGroupName  = "load-test-small-rc"
	mediumRCGroupName = "load-test-medium-rc"
	bigRCGroupName    = "load-test-big-rc"
	smallRCBatchSize  = 30
	mediumRCBatchSize = 5
	bigRCBatchSize    = 1
)

// This test suite can take a long time to run, so by default it is added to
// the ginkgo.skip list (see driver.go).
// To run this suite you must explicitly ask for it by setting the
// -t/--test flag or ginkgo.focus flag.
var _ = KubeDescribe("Load capacity", func() {
	var c *client.Client
	var nodeCount int
	var ns string
	var configs []*RCConfig

	// Gathers metrics before teardown
	// TODO add flag that allows to skip cleanup on failure
	AfterEach(func() {
		// Verify latency metrics
		highLatencyRequests, err := HighLatencyRequests(c)
		expectNoError(err, "Too many instances metrics above the threshold")
		Expect(highLatencyRequests).NotTo(BeNumerically(">", 0))
	})

	// Explicitly put here, to delete namespace at the end of the test
	// (after measuring latency metrics, etc.).
	options := FrameworkOptions{
		clientQPS:   50,
		clientBurst: 100,
	}
	framework := NewFramework("load", options)
	framework.NamespaceDeletionTimeout = time.Hour

	BeforeEach(func() {
		c = framework.Client

		ns = framework.Namespace.Name
		nodes := ListSchedulableNodesOrDie(c)
		nodeCount = len(nodes.Items)
		Expect(nodeCount).NotTo(BeZero())

		// Terminating a namespace (deleting the remaining objects from it - which
		// generally means events) can affect the current run. Thus we wait for all
		// terminating namespace to be finally deleted before starting this test.
		err := checkTestingNSDeletedExcept(c, ns)
		expectNoError(err)

		expectNoError(resetMetrics(c))
	})

	type Load struct {
		podsPerNode int
		image       string
		command     []string
	}

	loadTests := []Load{
		// The container will consume 1 cpu and 512mb of memory.
		{podsPerNode: 3, image: "jess/stress", command: []string{"stress", "-c", "1", "-m", "2"}},
		{podsPerNode: 30, image: "gcr.io/google_containers/serve_hostname:v1.4"},
	}

	for _, testArg := range loadTests {
		name := fmt.Sprintf("should be able to handle %v pods per node", testArg.podsPerNode)
		if testArg.podsPerNode == 30 {
			name = "[Feature:Performance] " + name
		} else {
			name = "[Feature:ManualPerformance] " + name
		}
		itArg := testArg

		It(name, func() {
			totalPods := itArg.podsPerNode * nodeCount
			configs = generateRCConfigs(totalPods, itArg.image, itArg.command, c, ns)

			// Simulate lifetime of RC:
			//  * create with initial size
			//  * scale RC to a random size and list all pods
			//  * scale RC to a random size and list all pods
			//  * delete it
			//
			// This will generate ~5 creations/deletions per second assuming:
			//  - X small RCs each 5 pods   [ 5 * X = totalPods / 2 ]
			//  - Y medium RCs each 30 pods [ 30 * Y = totalPods / 4 ]
			//  - Z big RCs each 250 pods   [ 250 * Z = totalPods / 4]

			// We would like to spread creating replication controllers over time
			// to make it possible to create/schedule them in the meantime.
			// Currently we assume 5 pods/second average throughput.
			// We may want to revisit it in the future.
			creatingTime := time.Duration(totalPods/5) * time.Second
			createAllRC(configs, creatingTime)
			By("============================================================================")

			// We would like to spread scaling replication controllers over time
			// to make it possible to create/schedule & delete them in the meantime.
			// Currently we assume that 5 pods/second average throughput.
			// The expected number of created/deleted pods is less than totalPods/3.
			scalingTime := time.Duration(totalPods/15) * time.Second
			scaleAllRC(configs, scalingTime)
			By("============================================================================")

			scaleAllRC(configs, scalingTime)
			By("============================================================================")

			// Cleanup all created replication controllers.
			// Currently we assume 5 pods/second average deletion throughput.
			// We may want to revisit it in the future.
			deletingTime := time.Duration(totalPods/5) * time.Second
			deleteAllRC(configs, deletingTime)
		})
	}
})

func computeRCCounts(total int) (int, int, int) {
	// Small RCs owns ~0.5 of total number of pods, medium and big RCs ~0.25 each.
	// For example for 3000 pods (100 nodes, 30 pods per node) there are:
	//  - 300 small RCs each 5 pods
	//  - 25 medium RCs each 30 pods
	//  - 3 big RCs each 250 pods
	bigRCCount := total / 4 / bigRCSize
	total -= bigRCCount * bigRCSize
	mediumRCCount := total / 3 / mediumRCSize
	total -= mediumRCCount * mediumRCSize
	smallRCCount := total / smallRCSize
	return smallRCCount, mediumRCCount, bigRCCount
}

func generateRCConfigs(totalPods int, image string, command []string, c *client.Client, ns string) []*RCConfig {
	configs := make([]*RCConfig, 0)

	smallRCCount, mediumRCCount, bigRCCount := computeRCCounts(totalPods)
	configs = append(configs, generateRCConfigsForGroup(c, ns, smallRCGroupName, smallRCSize, smallRCCount, image, command)...)
	configs = append(configs, generateRCConfigsForGroup(c, ns, mediumRCGroupName, mediumRCSize, mediumRCCount, image, command)...)
	configs = append(configs, generateRCConfigsForGroup(c, ns, bigRCGroupName, bigRCSize, bigRCCount, image, command)...)

	return configs
}

func generateRCConfigsForGroup(c *client.Client, ns, groupName string, size, count int, image string, command []string) []*RCConfig {
	configs := make([]*RCConfig, 0, count)
	for i := 1; i <= count; i++ {
		config := &RCConfig{
			Client:     c,
			Name:       groupName + "-" + strconv.Itoa(i),
			Namespace:  ns,
			Timeout:    10 * time.Minute,
			Image:      image,
			Command:    command,
			Replicas:   size,
			CpuRequest: 10,       // 0.01 core
			MemRequest: 26214400, // 25MB
		}
		configs = append(configs, config)
	}
	return configs
}

func sleepUpTo(d time.Duration) {
	time.Sleep(time.Duration(rand.Int63n(d.Nanoseconds())))
}

func createAllRC(configs []*RCConfig, creatingTime time.Duration) {
	var wg sync.WaitGroup
	wg.Add(len(configs))
	for _, config := range configs {
		go createRC(&wg, config, creatingTime)
	}
	wg.Wait()
}

func createRC(wg *sync.WaitGroup, config *RCConfig, creatingTime time.Duration) {
	defer GinkgoRecover()
	defer wg.Done()

	sleepUpTo(creatingTime)
	expectNoError(RunRC(*config), fmt.Sprintf("creating rc %s", config.Name))
}

func scaleAllRC(configs []*RCConfig, scalingTime time.Duration) {
	var wg sync.WaitGroup
	wg.Add(len(configs))
	for _, config := range configs {
		go scaleRC(&wg, config, scalingTime)
	}
	wg.Wait()
}

// Scales RC to a random size within [0.5*size, 1.5*size] and lists all the pods afterwards.
// Scaling happens always based on original size, not the current size.
func scaleRC(wg *sync.WaitGroup, config *RCConfig, scalingTime time.Duration) {
	defer GinkgoRecover()
	defer wg.Done()

	sleepUpTo(scalingTime)
	newSize := uint(rand.Intn(config.Replicas) + config.Replicas/2)
	expectNoError(ScaleRC(config.Client, config.Namespace, config.Name, newSize, true),
		fmt.Sprintf("scaling rc %s for the first time", config.Name))
	selector := labels.SelectorFromSet(labels.Set(map[string]string{"name": config.Name}))
	options := api.ListOptions{
		LabelSelector:   selector,
		ResourceVersion: "0",
	}
	_, err := config.Client.Pods(config.Namespace).List(options)
	expectNoError(err, fmt.Sprintf("listing pods from rc %v", config.Name))
}

func deleteAllRC(configs []*RCConfig, deletingTime time.Duration) {
	var wg sync.WaitGroup
	wg.Add(len(configs))
	for _, config := range configs {
		go deleteRC(&wg, config, deletingTime)
	}
	wg.Wait()
}

func deleteRC(wg *sync.WaitGroup, config *RCConfig, deletingTime time.Duration) {
	defer GinkgoRecover()
	defer wg.Done()

	sleepUpTo(deletingTime)
	expectNoError(DeleteRC(config.Client, config.Namespace, config.Name), fmt.Sprintf("deleting rc %s", config.Name))
}
<commit_msg>Speedup load test<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"k8s.io/kubernetes/pkg/api"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/labels"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	smallRCSize       = 5
	mediumRCSize      = 30
	bigRCSize         = 250
	smallRCGroupName  = "load-test-small-rc"
	mediumRCGroupName = "load-test-medium-rc"
	bigRCGroupName    = "load-test-big-rc"
	smallRCBatchSize  = 30
	mediumRCBatchSize = 5
	bigRCBatchSize    = 1
)

// This test suite can take a long time to run, so by default it is added to
// the ginkgo.skip list (see driver.go).
// To run this suite you must explicitly ask for it by setting the
// -t/--test flag or ginkgo.focus flag.
var _ = KubeDescribe("Load capacity", func() {
	var c *client.Client
	var nodeCount int
	var ns string
	var configs []*RCConfig

	// Gathers metrics before teardown
	// TODO add flag that allows to skip cleanup on failure
	AfterEach(func() {
		// Verify latency metrics
		highLatencyRequests, err := HighLatencyRequests(c)
		expectNoError(err, "Too many instances metrics above the threshold")
		Expect(highLatencyRequests).NotTo(BeNumerically(">", 0))
	})

	// Explicitly put here, to delete namespace at the end of the test
	// (after measuring latency metrics, etc.).
	options := FrameworkOptions{
		clientQPS:   50,
		clientBurst: 100,
	}
	framework := NewFramework("load", options)
	framework.NamespaceDeletionTimeout = time.Hour

	BeforeEach(func() {
		c = framework.Client

		ns = framework.Namespace.Name
		nodes := ListSchedulableNodesOrDie(c)
		nodeCount = len(nodes.Items)
		Expect(nodeCount).NotTo(BeZero())

		// Terminating a namespace (deleting the remaining objects from it - which
		// generally means events) can affect the current run. Thus we wait for all
		// terminating namespace to be finally deleted before starting this test.
		err := checkTestingNSDeletedExcept(c, ns)
		expectNoError(err)

		expectNoError(resetMetrics(c))
	})

	type Load struct {
		podsPerNode int
		image       string
		command     []string
	}

	loadTests := []Load{
		// The container will consume 1 cpu and 512mb of memory.
		{podsPerNode: 3, image: "jess/stress", command: []string{"stress", "-c", "1", "-m", "2"}},
		{podsPerNode: 30, image: "gcr.io/google_containers/serve_hostname:v1.4"},
	}

	for _, testArg := range loadTests {
		name := fmt.Sprintf("should be able to handle %v pods per node", testArg.podsPerNode)
		if testArg.podsPerNode == 30 {
			name = "[Feature:Performance] " + name
		} else {
			name = "[Feature:ManualPerformance] " + name
		}
		itArg := testArg

		It(name, func() {
			totalPods := itArg.podsPerNode * nodeCount
			configs = generateRCConfigs(totalPods, itArg.image, itArg.command, c, ns)

			// Simulate lifetime of RC:
			//  * create with initial size
			//  * scale RC to a random size and list all pods
			//  * scale RC to a random size and list all pods
			//  * delete it
			//
			// This will generate ~5 creations/deletions per second assuming:
			//  - X small RCs each 5 pods   [ 5 * X = totalPods / 2 ]
			//  - Y medium RCs each 30 pods [ 30 * Y = totalPods / 4 ]
			//  - Z big RCs each 250 pods   [ 250 * Z = totalPods / 4]

			// We would like to spread creating replication controllers over time
			// to make it possible to create/schedule them in the meantime.
			// Currently we assume 10 pods/second average throughput.
			// We may want to revisit it in the future.
			creatingTime := time.Duration(totalPods/10) * time.Second
			createAllRC(configs, creatingTime)
			By("============================================================================")

			// We would like to spread scaling replication controllers over time
			// to make it possible to create/schedule & delete them in the meantime.
			// Currently we assume that 10 pods/second average throughput.
			// The expected number of created/deleted pods is less than totalPods/3.
			scalingTime := time.Duration(totalPods/30) * time.Second
			scaleAllRC(configs, scalingTime)
			By("============================================================================")

			scaleAllRC(configs, scalingTime)
			By("============================================================================")

			// Cleanup all created replication controllers.
			// Currently we assume 5 pods/second average deletion throughput.
			// We may want to revisit it in the future.
			deletingTime := time.Duration(totalPods/5) * time.Second
			deleteAllRC(configs, deletingTime)
		})
	}
})

func computeRCCounts(total int) (int, int, int) {
	// Small RCs owns ~0.5 of total number of pods, medium and big RCs ~0.25 each.
	// For example for 3000 pods (100 nodes, 30 pods per node) there are:
	//  - 300 small RCs each 5 pods
	//  - 25 medium RCs each 30 pods
	//  - 3 big RCs each 250 pods
	bigRCCount := total / 4 / bigRCSize
	total -= bigRCCount * bigRCSize
	mediumRCCount := total / 3 / mediumRCSize
	total -= mediumRCCount * mediumRCSize
	smallRCCount := total / smallRCSize
	return smallRCCount, mediumRCCount, bigRCCount
}

func generateRCConfigs(totalPods int, image string, command []string, c *client.Client, ns string) []*RCConfig {
	configs := make([]*RCConfig, 0)

	smallRCCount, mediumRCCount, bigRCCount := computeRCCounts(totalPods)
	configs = append(configs, generateRCConfigsForGroup(c, ns, smallRCGroupName, smallRCSize, smallRCCount, image, command)...)
	configs = append(configs, generateRCConfigsForGroup(c, ns, mediumRCGroupName, mediumRCSize, mediumRCCount, image, command)...)
	configs = append(configs, generateRCConfigsForGroup(c, ns, bigRCGroupName, bigRCSize, bigRCCount, image, command)...)

	return configs
}

func generateRCConfigsForGroup(c *client.Client, ns, groupName string, size, count int, image string, command []string) []*RCConfig {
	configs := make([]*RCConfig, 0, count)
	for i := 1; i <= count; i++ {
		config := &RCConfig{
			Client:     c,
			Name:       groupName + "-" + strconv.Itoa(i),
			Namespace:  ns,
			Timeout:    10 * time.Minute,
			Image:      image,
			Command:    command,
			Replicas:   size,
			CpuRequest: 10,       // 0.01 core
			MemRequest: 26214400, // 25MB
		}
		configs = append(configs, config)
	}
	return configs
}

func sleepUpTo(d time.Duration) {
	time.Sleep(time.Duration(rand.Int63n(d.Nanoseconds())))
}

func createAllRC(configs []*RCConfig, creatingTime time.Duration) {
	var wg sync.WaitGroup
	wg.Add(len(configs))
	for _, config := range configs {
		go createRC(&wg, config, creatingTime)
	}
	wg.Wait()
}

func createRC(wg *sync.WaitGroup, config *RCConfig, creatingTime time.Duration) {
	defer GinkgoRecover()
	defer wg.Done()

	sleepUpTo(creatingTime)
	expectNoError(RunRC(*config), fmt.Sprintf("creating rc %s", config.Name))
}

func scaleAllRC(configs []*RCConfig, scalingTime time.Duration) {
	var wg sync.WaitGroup
	wg.Add(len(configs))
	for _, config := range configs {
		go scaleRC(&wg, config, scalingTime)
	}
	wg.Wait()
}

// Scales RC to a random size within [0.5*size, 1.5*size] and lists all the pods afterwards.
// Scaling happens always based on original size, not the current size.
func scaleRC(wg *sync.WaitGroup, config *RCConfig, scalingTime time.Duration) {
	defer GinkgoRecover()
	defer wg.Done()

	sleepUpTo(scalingTime)
	newSize := uint(rand.Intn(config.Replicas) + config.Replicas/2)
	expectNoError(ScaleRC(config.Client, config.Namespace, config.Name, newSize, true),
		fmt.Sprintf("scaling rc %s for the first time", config.Name))
	selector := labels.SelectorFromSet(labels.Set(map[string]string{"name": config.Name}))
	options := api.ListOptions{
		LabelSelector:   selector,
		ResourceVersion: "0",
	}
	_, err := config.Client.Pods(config.Namespace).List(options)
	expectNoError(err, fmt.Sprintf("listing pods from rc %v", config.Name))
}

func deleteAllRC(configs []*RCConfig, deletingTime time.Duration) {
	var wg sync.WaitGroup
	wg.Add(len(configs))
	for _, config := range configs {
		go deleteRC(&wg, config, deletingTime)
	}
	wg.Wait()
}

func deleteRC(wg *sync.WaitGroup, config *RCConfig, deletingTime time.Duration) {
	defer GinkgoRecover()
	defer wg.Done()

	sleepUpTo(deletingTime)
	expectNoError(DeleteRC(config.Client, config.Namespace, config.Name), fmt.Sprintf("deleting rc %s", config.Name))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"syscall"
	"time"

	"github.com/sapcc/kubernikus/pkg/api/client/operations"
	"github.com/sapcc/kubernikus/pkg/api/models"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"

	"k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

func (s *E2ETestSuite) waitForCluster(klusterName, errorString string, waitUntilFunc func(k *models.Kluster, err error) bool) error {
	for start := time.Now(); time.Since(start) < Timeout; time.Sleep(CheckInterval) {
		select {
		default:
			kluster, err := s.kubernikusClient.Operations.ShowCluster(
				operations.NewShowClusterParams().WithName(klusterName),
				s.authFunc(),
			)
			// pass an handle err in waitUntilFunc
			if err != nil {
				if waitUntilFunc(nil, err) {
					return nil
				}
			}
			if waitUntilFunc(kluster.Payload, err) {
				return nil
			}
		case <-s.stopCh:
			os.Exit(1)
		}
	}
	return fmt.Errorf(errorString)
}

func isNodePoolsUpdated(nodePools []models.NodePool) bool {
	// check if both nodePools exists and one of the is medium nodePool
	for _, v := range nodePools {
		if v.Name == "medium" && len(nodePools) == 2 {
			return true
		}
	}
	return false
}

func isNodesHealthyAndRunning(nodePoolsStatus []models.NodePoolInfo, nodePoolsSpec []models.NodePool) bool {
	for _, v := range nodePoolsStatus {
		expectedSize := getNodePoolSizeFromSpec(nodePoolsSpec, v.Name)
		if expectedSize == -1 {
			log.Printf("couldn't find nodepool with name %v in spec", v.Name)
			return false
		}
		if v.Healthy != expectedSize || v.Running != expectedSize {
			log.Printf("nodepool %v: expected %v node(s), actual: healthy %v, running %v", v.Name, expectedSize, v.Healthy, v.Running)
			return false
		}
	}
	return true
}

func getNodePoolSizeFromSpec(nodePoolsSpec []models.NodePool, name string) int64 {
	for _, v := range nodePoolsSpec {
		if v.Name == name {
			return v.Size
		}
	}
	return -1
}

func (s *E2ETestSuite) emptyNodePoolsOfKluster() {

	log.Printf("stopping all nodes of cluster %v", s.ClusterName)

	cluster, err := s.kubernikusClient.Operations.ShowCluster(
		operations.NewShowClusterParams().WithName(s.ClusterName),
		s.authFunc(),
	)
	s.handleError(err)

	nodePools := []models.NodePool{}
	for _, nodePool := range cluster.Payload.Spec.NodePools {
		nodePool.Size = 0
		nodePools = append(nodePools, nodePool)
	}
	cluster.Payload.Spec.NodePools = nodePools

	// empty node pools
	_, err = s.kubernikusClient.Operations.UpdateCluster(
		operations.NewUpdateClusterParams().
			WithName(s.ClusterName).
			WithBody(cluster.Payload),
		s.authFunc(),
	)
	s.handleError(err)

	err = s.waitForCluster(
		s.ClusterName,
		fmt.Sprintf("Not all nodes of cluster %v could be terminated in time", s.ClusterName),
		func(k *models.Kluster, err error) bool {
			if err != nil {
				log.Println(err)
				return false
			}
			for _, node := range k.Status.NodePools {
				if node.Running != 0 {
					log.Printf("Cluster %v has nodes in state running", k.Name)
					return false
				}
			}
			return true
		},
	)
	s.handleError(err)
}

func newE2ECluster(klusterName string) *models.Kluster {
	return &models.Kluster{
		Name: klusterName,
		Spec: models.KlusterSpec{
			NodePools: []models.NodePool{
				{
					Name:   "small",
					Flavor: "m1.small",
					Image:  "coreos-stable-amd64",
					Size:   ClusterSmallNodePoolSize,
				},
			},
		},
	}
}

func mediumNodePoolItem() *models.NodePool {
	return &models.NodePool{
		Name:   "medium",
		Image:  "coreos-stable-amd64",
		Flavor: "m1.medium",
		Size:   ClusterMediumNodePoolSize,
	}
}

func newE2ESmokeTestCluster(klusterName string) *models.Kluster {
	return &models.Kluster{
		Name: klusterName,
		Spec: models.KlusterSpec{
			NodePools: []models.NodePool{
				{
					Name:   "small",
					Flavor: "m1.small",
					Image:  "coreos-stable-amd64",
					Size:   ClusterSmallNodePoolSize,
				},
				{
					Name:   "medium",
					Image:  "coreos-stable-amd64",
					Flavor: "m1.medium",
					Size:   ClusterMediumNodePoolSize,
				},
			},
		},
	}
}

func isPodRunning(event watch.Event) (bool, error) {
	switch event.Type {
	case watch.Deleted:
		return false, errors.NewNotFound(schema.GroupResource{Resource: "pods"}, "")
	}
	switch t := event.Object.(type) {
	case *v1.Pod:
		switch t.Status.Phase {
		case v1.PodRunning:
			return true, nil
		case v1.PodFailed, v1.PodSucceeded:
			return false, fmt.Errorf("pod failed or ran to completion")
		}
	}
	return false, nil
}

func (s *E2ETestSuite) waitForPodDeleted(namespace, name string) (bool, error) {
	for start := time.Now(); time.Since(start) < TimeoutPod; time.Sleep(CheckInterval) {
		select {
		default:
			_, err := s.clientSet.CoreV1().Pods(namespace).Get(name, meta_v1.GetOptions{})
			if err != nil {
				if errors.IsNotFound(err) {
					log.Printf("pod %v/%v was deleted", namespace, name)
					return true, nil
				}
				return false, err
			}
			log.Printf("pod %v/%v still exists", namespace, name)
		case <-s.stopCh:
			os.Exit(1)
		}
	}
	return false, nil
}

func (s *E2ETestSuite) handleError(err error) {
	if err == nil {
		return
	}
	log.Print(err)
	// cleanup
	if !s.IsNoTeardown {
		s.tearDownCluster()
	}
	os.Exit(1)
}

func (s *E2ETestSuite) tearDownCluster() {
	s.emptyNodePoolsOfKluster()
	log.Printf("Deleting cluster %v", s.ClusterName)

	_, err := s.kubernikusClient.Operations.TerminateCluster(
		operations.NewTerminateClusterParams().WithName(s.ClusterName),
		s.authFunc(),
	)
	s.handleError(err)

	if err := s.waitForCluster(
		s.ClusterName,
		fmt.Sprintf("Cluster %s wasn't terminated in time", s.ClusterName),
		func(k *models.Kluster, err error) bool {
			if err != nil {
				switch err.(type) {
				case *operations.ShowClusterDefault:
					result := err.(*operations.ShowClusterDefault)
					if result.Code() == 404 {
						log.Printf("Cluster %v was terminated", s.ClusterName)
						return true
					}
				case error:
					log.Println("Failed to show cluster %v: %v", s.ClusterName, err)
					return false
				}
			}
			log.Printf("Cluster %v was not terminated. Still in %v", s.ClusterName, k.Status.Phase)
			return false
		},
	); err != nil {
		s.handleError(fmt.Errorf("error while waiting for cluster %v to be terminated: %v", s.ClusterName, err))
	}
}

func (s *E2ETestSuite) NewClientSet() {
	config := s.newClientConfig()
	clientSet, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Fatalf("Couldn't create Kubernetes client: %s", err)
		return
	}
	s.clientSet = clientSet
}

func (s *E2ETestSuite) newClientConfig() *rest.Config {
	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	overrides := &clientcmd.ConfigOverrides{}

	if s.KubeConfig != "" {
		rules.ExplicitPath = s.KubeConfig
	}

	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig()
	if err != nil {
		fmt.Errorf("couldn't get Kubernetes default config: %s", err)
	}

	return config
}

func (s *E2ETestSuite) isClusterUpOrWait() {
	err := s.waitForCluster(
		s.ClusterName,
		fmt.Sprintf("Cluster %s wasn't created in time", s.ClusterName),
		func(k *models.Kluster, err error) bool {
			if err != nil {
				switch err.(type) {
				case *operations.ShowClusterDefault:
					result := err.(*operations.ShowClusterDefault)
					if result.Code() == 404 {
						log.Printf("Cluster %v does not exist. Creating it", s.ClusterName)
						_, err := s.kubernikusClient.Operations.CreateCluster(
							operations.NewCreateClusterParams().WithBody(newE2ESmokeTestCluster(ClusterName)),
							s.authFunc(),
						)
						s.handleError(err)
						return true
					}
				case error:
					log.Printf("Failed to show cluster %v: %v", s.ClusterName, err)
					return false
				}
				return false
			}
			if k.Status.Phase == models.KlusterPhaseRunning {
				log.Printf("Cluster %v is ready. Checking nodes..", k.Name)
				if isNodesHealthyAndRunning(k.Status.NodePools, k.Spec.NodePools) {
					return true
				}
			} else {
				log.Printf("Cluster %v not ready for smoke test. Still in %v", k.Name, k.Status.Phase)
			}
			return false
		},
	)
	s.handleError(err)
}

func (s *E2ETestSuite) waitForPVC(pvc *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {
	w, err := s.clientSet.CoreV1().PersistentVolumeClaims(pvc.GetNamespace()).Watch(
		meta_v1.SingleObject(
			meta_v1.ObjectMeta{
				Name: pvc.GetName(),
			},
		),
	)
	if err != nil {
		return nil, err
	}

	_, err = watch.Until(5*time.Minute, w, isPVCBound)
	if err != nil {
		return nil, err
	}

	return pvc, nil
}

func isPVCBound(event watch.Event) (bool, error) {
	switch event.Type {
	case watch.Deleted:
		return false, errors.NewNotFound(schema.GroupResource{Resource: "pvc"}, "")
	}
	switch t := event.Object.(type) {
	case *v1.PersistentVolumeClaim:
		switch t.Status.Phase {
		case v1.ClaimBound:
			return true, nil
		case v1.ClaimPending:
			return false, nil
		case v1.ClaimLost:
			return false, fmt.Errorf("pvc is lost")
		}
	}
	return false, nil
}

func (s *E2ETestSuite) exitGraceful(sigs chan os.Signal) {
	sigs <- syscall.SIGTERM
}
<commit_msg>keep e2e klusters in case of errors<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"syscall"
	"time"

	"github.com/sapcc/kubernikus/pkg/api/client/operations"
	"github.com/sapcc/kubernikus/pkg/api/models"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"

	"k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

func (s *E2ETestSuite) waitForCluster(klusterName, errorString string, waitUntilFunc func(k *models.Kluster, err error) bool) error {
	for start := time.Now(); time.Since(start) < Timeout; time.Sleep(CheckInterval) {
		select {
		default:
			kluster, err := s.kubernikusClient.Operations.ShowCluster(
				operations.NewShowClusterParams().WithName(klusterName),
				s.authFunc(),
			)
			// pass an handle err in waitUntilFunc
			if err != nil {
				if waitUntilFunc(nil, err) {
					return nil
				}
			}
			if waitUntilFunc(kluster.Payload, err) {
				return nil
			}
		case <-s.stopCh:
			os.Exit(1)
		}
	}
	return fmt.Errorf(errorString)
}

func isNodePoolsUpdated(nodePools []models.NodePool) bool {
	// check if both nodePools exists and one of the is medium nodePool
	for _, v := range nodePools {
		if v.Name == "medium" && len(nodePools) == 2 {
			return true
		}
	}
	return false
}

func isNodesHealthyAndRunning(nodePoolsStatus []models.NodePoolInfo, nodePoolsSpec []models.NodePool) bool {
	for _, v := range nodePoolsStatus {
		expectedSize := getNodePoolSizeFromSpec(nodePoolsSpec, v.Name)
		if expectedSize == -1 {
			log.Printf("couldn't find nodepool with name %v in spec", v.Name)
			return false
		}
		if v.Healthy != expectedSize || v.Running != expectedSize {
			log.Printf("nodepool %v: expected %v node(s), actual: healthy %v, running %v", v.Name, expectedSize, v.Healthy, v.Running)
			return false
		}
	}
	return true
}

func getNodePoolSizeFromSpec(nodePoolsSpec []models.NodePool, name string) int64 {
	for _, v := range nodePoolsSpec {
		if v.Name == name {
			return v.Size
		}
	}
	return -1
}

func (s *E2ETestSuite) emptyNodePoolsOfKluster() {

	log.Printf("stopping all nodes of cluster %v", s.ClusterName)

	cluster, err := s.kubernikusClient.Operations.ShowCluster(
		operations.NewShowClusterParams().WithName(s.ClusterName),
		s.authFunc(),
	)
	s.handleError(err)

	nodePools := []models.NodePool{}
	for _, nodePool := range cluster.Payload.Spec.NodePools {
		nodePool.Size = 0
		nodePools = append(nodePools, nodePool)
	}
	cluster.Payload.Spec.NodePools = nodePools

	// empty node pools
	_, err = s.kubernikusClient.Operations.UpdateCluster(
		operations.NewUpdateClusterParams().
			WithName(s.ClusterName).
			WithBody(cluster.Payload),
		s.authFunc(),
	)
	s.handleError(err)

	err = s.waitForCluster(
		s.ClusterName,
		fmt.Sprintf("Not all nodes of cluster %v could be terminated in time", s.ClusterName),
		func(k *models.Kluster, err error) bool {
			if err != nil {
				log.Println(err)
				return false
			}
			for _, node := range k.Status.NodePools {
				if node.Running != 0 {
					log.Printf("Cluster %v has nodes in state running", k.Name)
					return false
				}
			}
			return true
		},
	)
	s.handleError(err)
}

func newE2ECluster(klusterName string) *models.Kluster {
	return &models.Kluster{
		Name: klusterName,
		Spec: models.KlusterSpec{
			NodePools: []models.NodePool{
				{
					Name:   "small",
					Flavor: "m1.small",
					Image:  "coreos-stable-amd64",
					Size:   ClusterSmallNodePoolSize,
				},
			},
		},
	}
}

func mediumNodePoolItem() *models.NodePool {
	return &models.NodePool{
		Name:   "medium",
		Image:  "coreos-stable-amd64",
		Flavor: "m1.medium",
		Size:   ClusterMediumNodePoolSize,
	}
}

func newE2ESmokeTestCluster(klusterName string) *models.Kluster {
	return &models.Kluster{
		Name: klusterName,
		Spec: models.KlusterSpec{
			NodePools: []models.NodePool{
				{
					Name:   "small",
					Flavor: "m1.small",
					Image:  "coreos-stable-amd64",
					Size:   ClusterSmallNodePoolSize,
				},
				{
					Name:   "medium",
					Image:  "coreos-stable-amd64",
					Flavor: "m1.medium",
					Size:   ClusterMediumNodePoolSize,
				},
			},
		},
	}
}

func isPodRunning(event watch.Event) (bool, error) {
	switch event.Type {
	case watch.Deleted:
		return false, errors.NewNotFound(schema.GroupResource{Resource: "pods"}, "")
	}
	switch t := event.Object.(type) {
	case *v1.Pod:
		switch t.Status.Phase {
		case v1.PodRunning:
			return true, nil
		case v1.PodFailed, v1.PodSucceeded:
			return false, fmt.Errorf("pod failed or ran to completion")
		}
	}
	return false, nil
}

func (s *E2ETestSuite) waitForPodDeleted(namespace, name string) (bool, error) {
	for start := time.Now(); time.Since(start) < TimeoutPod; time.Sleep(CheckInterval) {
		select {
		default:
			_, err := s.clientSet.CoreV1().Pods(namespace).Get(name, meta_v1.GetOptions{})
			if err != nil {
				if errors.IsNotFound(err) {
					log.Printf("pod %v/%v was deleted", namespace, name)
					return true, nil
				}
				return false, err
			}
			log.Printf("pod %v/%v still exists", namespace, name)
		case <-s.stopCh:
			os.Exit(1)
		}
	}
	return false, nil
}

func (s *E2ETestSuite) handleError(err error) {
	if err == nil {
		return
	}
	log.Print(err)
	// cleanup
	//if !s.IsNoTeardown {
	//  s.tearDownCluster()
	//}
	os.Exit(1)
}

func (s *E2ETestSuite) tearDownCluster() {
	s.emptyNodePoolsOfKluster()
	log.Printf("Deleting cluster %v", s.ClusterName)

	_, err := s.kubernikusClient.Operations.TerminateCluster(
		operations.NewTerminateClusterParams().WithName(s.ClusterName),
		s.authFunc(),
	)
	s.handleError(err)

	if err := s.waitForCluster(
		s.ClusterName,
		fmt.Sprintf("Cluster %s wasn't terminated in time", s.ClusterName),
		func(k *models.Kluster, err error) bool {
			if err != nil {
				switch err.(type) {
				case *operations.ShowClusterDefault:
					result := err.(*operations.ShowClusterDefault)
					if result.Code() == 404 {
						log.Printf("Cluster %v was terminated", s.ClusterName)
						return true
					}
				case error:
					log.Println("Failed to show cluster %v: %v", s.ClusterName, err)
					return false
				}
			}
			log.Printf("Cluster %v was not terminated. Still in %v", s.ClusterName, k.Status.Phase)
			return false
		},
	); err != nil {
		s.handleError(fmt.Errorf("error while waiting for cluster %v to be terminated: %v", s.ClusterName, err))
	}
}

func (s *E2ETestSuite) NewClientSet() {
	config := s.newClientConfig()
	clientSet, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Fatalf("Couldn't create Kubernetes client: %s", err)
		return
	}
	s.clientSet = clientSet
}

func (s *E2ETestSuite) newClientConfig() *rest.Config {
	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	overrides := &clientcmd.ConfigOverrides{}

	if s.KubeConfig != "" {
		rules.ExplicitPath = s.KubeConfig
	}

	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig()
	if err != nil {
		fmt.Errorf("couldn't get Kubernetes default config: %s", err)
	}

	return config
}

func (s *E2ETestSuite) isClusterUpOrWait() {
	err := s.waitForCluster(
		s.ClusterName,
		fmt.Sprintf("Cluster %s wasn't created in time", s.ClusterName),
		func(k *models.Kluster, err error) bool {
			if err != nil {
				switch err.(type) {
				case *operations.ShowClusterDefault:
					result := err.(*operations.ShowClusterDefault)
					if result.Code() == 404 {
						log.Printf("Cluster %v does not exist. Creating it", s.ClusterName)
						_, err := s.kubernikusClient.Operations.CreateCluster(
							operations.NewCreateClusterParams().WithBody(newE2ESmokeTestCluster(ClusterName)),
							s.authFunc(),
						)
						s.handleError(err)
						return true
					}
				case error:
					log.Printf("Failed to show cluster %v: %v", s.ClusterName, err)
					return false
				}
				return false
			}
			if k.Status.Phase == models.KlusterPhaseRunning {
				log.Printf("Cluster %v is ready. Checking nodes..", k.Name)
				if isNodesHealthyAndRunning(k.Status.NodePools, k.Spec.NodePools) {
					return true
				}
			} else {
				log.Printf("Cluster %v not ready for smoke test. Still in %v", k.Name, k.Status.Phase)
			}
			return false
		},
	)
	s.handleError(err)
}

func (s *E2ETestSuite) waitForPVC(pvc *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {
	w, err := s.clientSet.CoreV1().PersistentVolumeClaims(pvc.GetNamespace()).Watch(
		meta_v1.SingleObject(
			meta_v1.ObjectMeta{
				Name: pvc.GetName(),
			},
		),
	)
	if err != nil {
		return nil, err
	}

	_, err = watch.Until(5*time.Minute, w, isPVCBound)
	if err != nil {
		return nil, err
	}

	return pvc, nil
}

func isPVCBound(event watch.Event) (bool, error) {
	switch event.Type {
	case watch.Deleted:
		return false, errors.NewNotFound(schema.GroupResource{Resource: "pvc"}, "")
	}
	switch t := event.Object.(type) {
	case *v1.PersistentVolumeClaim:
		switch t.Status.Phase {
		case v1.ClaimBound:
			return true, nil
		case v1.ClaimPending:
			return false, nil
		case v1.ClaimLost:
			return false, fmt.Errorf("pvc is lost")
		}
	}
	return false, nil
}

func (s *E2ETestSuite) exitGraceful(sigs chan os.Signal) {
	sigs <- syscall.SIGTERM
}
<|endoftext|>


<commit_before>package tmpl

import (
	"bytes"
	"html/template"
	"strings"
	"time"

	"strconv"

	"fmt"

	"bitbucket.org/mundipagg/boletoapi/models"
	"bitbucket.org/mundipagg/boletoapi/util"
)

var funcMap = template.FuncMap{
	"today":            today,
	"brdate":           brDate,
	"replace":          replace,
	"docType":          docType,
	"trim":             trim,
	"padLeft":          util.PadLeft,
	"toString":         toString,
	"fmtDigitableLine": fmtDigitableLine,
	"fmtCNPJ":          fmtCNPJ,
	"fmtCPF":           fmtCPF,
	"fmtDoc":           fmtDoc,
	"fmtNumber":        fmtNumber,
}

func fmtNumber(n int64) string {
	real := n / 100
	cents := n % 100
	return fmt.Sprintf("%d,%02d", real, cents)
}

func fmtDoc(doc models.Document) string {
	if doc.Number.IsCpf() {
		return fmtCPF(string(doc.Number))
	}
	return fmtCNPJ(string(doc.Number))
}

func toString(number int) string {
	return strconv.Itoa(number)
}

func today() time.Time {
	return time.Now()
}

func brDate(d time.Time) string {
	return d.Format("02/01/2006")
}

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func docType(s models.DocumentType) int {
	if s.IsCpf() {
		return 1
	}
	return 2
}

func trim(s string) string {
	return strings.TrimSpace(s)
}
func fmtDigitableLine(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 5 || idx == 15 || idx == 26 {
			buf.WriteString(".")
		}
		if idx == 10 || idx == 21 || idx == 32 || idx == 33 {
			buf.WriteString(" ")
		}
		buf.WriteByte(byte(c))
	}
	return buf.String()
}

func fmtCNPJ(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 2 || idx == 5 {
			buf.WriteString(".")
		}
		if idx == 8 {
			buf.WriteString("/")
		}
		if idx == 12 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}

func fmtCPF(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 3 || idx == 6 {
			buf.WriteString(".")
		}
		if idx == 9 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}
<commit_msg>:bug: Corrige tmpl não reconhecendo função de padLeft<commit_after>package tmpl

import (
	"bytes"
	"html/template"
	"strings"
	"time"

	"strconv"

	"fmt"

	"bitbucket.org/mundipagg/boletoapi/models"
	"bitbucket.org/mundipagg/boletoapi/util"
)

var funcMap = template.FuncMap{
	"today":            today,
	"brdate":           brDate,
	"replace":          replace,
	"docType":          docType,
	"trim":             trim,
	"padLeft":          padLeft,
	"toString":         toString,
	"fmtDigitableLine": fmtDigitableLine,
	"fmtCNPJ":          fmtCNPJ,
	"fmtCPF":           fmtCPF,
	"fmtDoc":           fmtDoc,
	"fmtNumber":        fmtNumber,
}

func padLeft(value, char string, total uint) string {
	return util.PadLeft(value, char, total)
}

func fmtNumber(n int64) string {
	real := n / 100
	cents := n % 100
	return fmt.Sprintf("%d,%02d", real, cents)
}

func fmtDoc(doc models.Document) string {
	if doc.Number.IsCpf() {
		return fmtCPF(string(doc.Number))
	}
	return fmtCNPJ(string(doc.Number))
}

func toString(number int) string {
	return strconv.Itoa(number)
}

func today() time.Time {
	return time.Now()
}

func brDate(d time.Time) string {
	return d.Format("02/01/2006")
}

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func docType(s models.DocumentType) int {
	if s.IsCpf() {
		return 1
	}
	return 2
}

func trim(s string) string {
	return strings.TrimSpace(s)
}
func fmtDigitableLine(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 5 || idx == 15 || idx == 26 {
			buf.WriteString(".")
		}
		if idx == 10 || idx == 21 || idx == 32 || idx == 33 {
			buf.WriteString(" ")
		}
		buf.WriteByte(byte(c))
	}
	return buf.String()
}

func fmtCNPJ(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 2 || idx == 5 {
			buf.WriteString(".")
		}
		if idx == 8 {
			buf.WriteString("/")
		}
		if idx == 12 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}

func fmtCPF(s string) string {
	buf := bytes.Buffer{}
	for idx, c := range s {
		if idx == 3 || idx == 6 {
			buf.WriteString(".")
		}
		if idx == 9 {
			buf.WriteString("-")
		}
		buf.WriteRune(c)
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package generate

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/gobuffalo/makr"
	"github.com/pkg/errors"

	"github.com/markbates/going/defaults"
	"github.com/markbates/inflect"
	"github.com/markbates/pop"
	"github.com/spf13/cobra"
)

var skipMigration bool
var structTag string

func init() {
	ModelCmd.Flags().StringVarP(&structTag, "struct-tag", "", "json", "sets the struct tags for model (xml or json) (default \"json\")")
	ModelCmd.Flags().BoolVarP(&skipMigration, "skip-migration", "s", false, "Skip creating a new fizz migration for this model.")
}

var nrx = regexp.MustCompile(`^nulls\.(.+)`)

type names struct {
	Original string
	Table    string
	Proper   string
	File     string
	Plural   string
	Char     string
}

func newName(name string) names {
	return names{
		Original: name,
		File:     inflect.Underscore(inflect.Singularize(name)),
		Table:    inflect.Tableize(inflect.Pluralize(name)),
		Proper:   inflect.ForeignKeyToAttribute(name),
		Plural:   inflect.Pluralize(inflect.Camelize(name)),
		Char:     strings.ToLower(string([]byte(name)[0])),
	}
}

type attribute struct {
	Names        names
	OriginalType string
	GoType       string
	Nullable     bool
}

func (a attribute) String() string {
	return fmt.Sprintf("\t%s %s `%s:\"%s\" db:\"%s\"`", a.Names.Proper, a.GoType, structTag, a.Names.Original, a.Names.Original)
}

type model struct {
	Package               string
	Imports               []string
	Names                 names
	Attributes            []attribute
	ValidatableAttributes []attribute
}

func (m model) Generate() error {
	g := makr.New()
	defer g.Fmt(".")
	ctx := makr.Data{}
	ctx["model"] = m
	ctx["plural_model_name"] = m.Names.Plural
	ctx["model_name"] = m.Names.Proper
	ctx["package_name"] = m.Package
	ctx["char"] = m.Names.Char
	ctx["encoding_type"] = structTag
	ctx["encoding_type_char"] = strings.ToLower(string([]byte(structTag)[0]))

	fname := filepath.Join(m.Package, m.Names.File+".go")
	g.Add(makr.NewFile(fname, modelTemplate))
	tfname := filepath.Join(m.Package, m.Names.File+"_test.go")
	g.Add(makr.NewFile(tfname, modelTestTemplate))
	return g.Run(".", ctx)
}

func (m model) Fizz() string {
	s := []string{fmt.Sprintf("create_table(\"%s\", func(t) {", m.Names.Table)}
	for _, a := range m.Attributes {
		switch a.Names.Original {
		case "created_at", "updated_at":
		case "id":
			s = append(s, fmt.Sprintf("\tt.Column(\"id\", \"%s\", {\"primary\": true})", fizzColType(a.OriginalType)))
		default:
			x := fmt.Sprintf("\tt.Column(\"%s\", \"%s\", {})", a.Names.Original, fizzColType(a.OriginalType))
			if a.Nullable {
				x = strings.Replace(x, "{}", `{"null": true}`, -1)
			}
			s = append(s, x)
		}
	}
	s = append(s, "})")
	return strings.Join(s, "\n")
}

func newModel(name string) model {
	m := model{
		Package: "models",
		Imports: []string{"time", "github.com/markbates/pop", "github.com/markbates/validate"},
		Names:   newName(name),
		Attributes: []attribute{
			{Names: newName("created_at"), OriginalType: "time.Time", GoType: "time.Time"},
			{Names: newName("updated_at"), OriginalType: "time.Time", GoType: "time.Time"},
		},
		ValidatableAttributes: []attribute{},
	}
	m.Names.Proper = inflect.Singularize(m.Names.Proper)
	return m
}

var ModelCmd = &cobra.Command{
	Use:     "model [name]",
	Aliases: []string{"m"},
	Short:   "Generates a model for your database",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("You must supply a name for your model!")
		}

		if structTag != "json" && structTag != "xml" {
			return errors.New("Invalid struct tags (use xml or json)!")
		}

		model := newModel(args[0])

		if structTag == "json" {
			model.Imports = append(model.Imports, "encoding/json")
		} else if structTag == "xml" {
			model.Imports = append(model.Imports, "encoding/xml")
		}

		hasId := false
		hasUuid := false
		hasNulls := false
		for _, def := range args[1:] {
			col := strings.Split(def, ":")
			if len(col) == 1 {
				col = append(col, "string")
			}

			nullable := nrx.MatchString(col[1])
			if !hasNulls && nullable {
				hasNulls = true
				model.Imports = append(model.Imports, "github.com/markbates/pop/nulls")
			}
			if strings.HasPrefix(col[1], "slices.") {
				model.Imports = append(model.Imports, "github.com/markbates/pop/slices")
			} else if !hasUuid && col[1] == "uuid" {
				hasUuid = true
				model.Imports = append(model.Imports, "github.com/satori/go.uuid")
			}

			a := attribute{
				Names:        newName(col[0]),
				OriginalType: col[1],
				GoType:       colType(col[1]),
				Nullable:     nullable,
			}
			if col[0] == "id" {
				// No need to create a default ID
				hasId = true
				// Ensure ID is the first attribute
				model.Attributes = append([]attribute{a}, model.Attributes...)
			} else {
				model.Attributes = append(model.Attributes, a)
			}
			if !a.Nullable {
				if a.GoType == "string" || a.GoType == "time.Time" || a.GoType == "int" {
					if a.GoType == "time.Time" {
						a.GoType = "Time"
					}
					model.ValidatableAttributes = append(model.ValidatableAttributes, a)
				}
			}
		}

		// Add a default UUID, if no custom ID is provided
		if !hasId {
			if !hasUuid {
				model.Imports = append(model.Imports, "github.com/satori/go.uuid")
			}
			id := newName("id")
			id.Proper = "ID"
			a := attribute{Names: id, OriginalType: "uuid.UUID", GoType: "uuid.UUID"}
			// Ensure ID is the first attribute
			model.Attributes = append([]attribute{a}, model.Attributes...)
		}

		err := os.MkdirAll(model.Package, 0766)
		if err != nil {
			return errors.Wrapf(err, "couldn't create folder %s", model.Package)
		}

		err = model.Generate()
		if err != nil {
			return err
		}

		if !skipMigration {
			cflag := cmd.Flag("path")
			migrationPath := defaults.String(cflag.Value.String(), "./migrations")
			err = pop.MigrationCreate(migrationPath, fmt.Sprintf("create_%s", model.Names.Table), "fizz", []byte(model.Fizz()), []byte(fmt.Sprintf("drop_table(\"%s\")", model.Names.Table)))
			if err != nil {
				return err
			}
		}

		return nil
	},
}

func colType(s string) string {
	switch strings.ToLower(s) {
	case "text":
		return "string"
	case "time", "timestamp", "datetime":
		return "time.Time"
	case "nulls.text":
		return "nulls.String"
	case "uuid":
		return "uuid.UUID"
	case "json", "jsonb":
		return "slices.Map"
	case "[]string":
		return "slices.String"
	case "[]int":
		return "slices.Int"
	case "slices.float", "[]float", "[]float32", "[]float64":
		return "slices.Float"
	default:
		return s
	}
}

func fizzColType(s string) string {
	switch strings.ToLower(s) {
	case "int":
		return "integer"
	case "time", "datetime":
		return "timestamp"
	case "uuid.uuid", "uuid":
		return "uuid"
	case "nulls.float32", "nulls.float64":
		return "float"
	case "slices.string", "[]string":
		return "varchar[]"
	case "slices.float", "[]float", "[]float32", "[]float64":
		return "numeric[]"
	case "slices.int":
		return "int[]"
	case "slices.map":
		return "jsonb"
	default:
		if nrx.MatchString(s) {
			return fizzColType(strings.Replace(s, "nulls.", "", -1))
		}
		return strings.ToLower(s)
	}
}
<commit_msg>Remove information about default value from struct-tag description<commit_after>package generate

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/gobuffalo/makr"
	"github.com/pkg/errors"

	"github.com/markbates/going/defaults"
	"github.com/markbates/inflect"
	"github.com/markbates/pop"
	"github.com/spf13/cobra"
)

var skipMigration bool
var structTag string

func init() {
	ModelCmd.Flags().StringVarP(&structTag, "struct-tag", "", "json", "sets the struct tags for model (xml or json)")
	ModelCmd.Flags().BoolVarP(&skipMigration, "skip-migration", "s", false, "Skip creating a new fizz migration for this model.")
}

var nrx = regexp.MustCompile(`^nulls\.(.+)`)

type names struct {
	Original string
	Table    string
	Proper   string
	File     string
	Plural   string
	Char     string
}

func newName(name string) names {
	return names{
		Original: name,
		File:     inflect.Underscore(inflect.Singularize(name)),
		Table:    inflect.Tableize(inflect.Pluralize(name)),
		Proper:   inflect.ForeignKeyToAttribute(name),
		Plural:   inflect.Pluralize(inflect.Camelize(name)),
		Char:     strings.ToLower(string([]byte(name)[0])),
	}
}

type attribute struct {
	Names        names
	OriginalType string
	GoType       string
	Nullable     bool
}

func (a attribute) String() string {
	return fmt.Sprintf("\t%s %s `%s:\"%s\" db:\"%s\"`", a.Names.Proper, a.GoType, structTag, a.Names.Original, a.Names.Original)
}

type model struct {
	Package               string
	Imports               []string
	Names                 names
	Attributes            []attribute
	ValidatableAttributes []attribute
}

func (m model) Generate() error {
	g := makr.New()
	defer g.Fmt(".")
	ctx := makr.Data{}
	ctx["model"] = m
	ctx["plural_model_name"] = m.Names.Plural
	ctx["model_name"] = m.Names.Proper
	ctx["package_name"] = m.Package
	ctx["char"] = m.Names.Char
	ctx["encoding_type"] = structTag
	ctx["encoding_type_char"] = strings.ToLower(string([]byte(structTag)[0]))

	fname := filepath.Join(m.Package, m.Names.File+".go")
	g.Add(makr.NewFile(fname, modelTemplate))
	tfname := filepath.Join(m.Package, m.Names.File+"_test.go")
	g.Add(makr.NewFile(tfname, modelTestTemplate))
	return g.Run(".", ctx)
}

func (m model) Fizz() string {
	s := []string{fmt.Sprintf("create_table(\"%s\", func(t) {", m.Names.Table)}
	for _, a := range m.Attributes {
		switch a.Names.Original {
		case "created_at", "updated_at":
		case "id":
			s = append(s, fmt.Sprintf("\tt.Column(\"id\", \"%s\", {\"primary\": true})", fizzColType(a.OriginalType)))
		default:
			x := fmt.Sprintf("\tt.Column(\"%s\", \"%s\", {})", a.Names.Original, fizzColType(a.OriginalType))
			if a.Nullable {
				x = strings.Replace(x, "{}", `{"null": true}`, -1)
			}
			s = append(s, x)
		}
	}
	s = append(s, "})")
	return strings.Join(s, "\n")
}

func newModel(name string) model {
	m := model{
		Package: "models",
		Imports: []string{"time", "github.com/markbates/pop", "github.com/markbates/validate"},
		Names:   newName(name),
		Attributes: []attribute{
			{Names: newName("created_at"), OriginalType: "time.Time", GoType: "time.Time"},
			{Names: newName("updated_at"), OriginalType: "time.Time", GoType: "time.Time"},
		},
		ValidatableAttributes: []attribute{},
	}
	m.Names.Proper = inflect.Singularize(m.Names.Proper)
	return m
}

var ModelCmd = &cobra.Command{
	Use:     "model [name]",
	Aliases: []string{"m"},
	Short:   "Generates a model for your database",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("You must supply a name for your model!")
		}

		if structTag != "json" && structTag != "xml" {
			return errors.New("Invalid struct tags (use xml or json)!")
		}

		model := newModel(args[0])

		if structTag == "json" {
			model.Imports = append(model.Imports, "encoding/json")
		} else if structTag == "xml" {
			model.Imports = append(model.Imports, "encoding/xml")
		}

		hasId := false
		hasUuid := false
		hasNulls := false
		for _, def := range args[1:] {
			col := strings.Split(def, ":")
			if len(col) == 1 {
				col = append(col, "string")
			}

			nullable := nrx.MatchString(col[1])
			if !hasNulls && nullable {
				hasNulls = true
				model.Imports = append(model.Imports, "github.com/markbates/pop/nulls")
			}
			if strings.HasPrefix(col[1], "slices.") {
				model.Imports = append(model.Imports, "github.com/markbates/pop/slices")
			} else if !hasUuid && col[1] == "uuid" {
				hasUuid = true
				model.Imports = append(model.Imports, "github.com/satori/go.uuid")
			}

			a := attribute{
				Names:        newName(col[0]),
				OriginalType: col[1],
				GoType:       colType(col[1]),
				Nullable:     nullable,
			}
			if col[0] == "id" {
				// No need to create a default ID
				hasId = true
				// Ensure ID is the first attribute
				model.Attributes = append([]attribute{a}, model.Attributes...)
			} else {
				model.Attributes = append(model.Attributes, a)
			}
			if !a.Nullable {
				if a.GoType == "string" || a.GoType == "time.Time" || a.GoType == "int" {
					if a.GoType == "time.Time" {
						a.GoType = "Time"
					}
					model.ValidatableAttributes = append(model.ValidatableAttributes, a)
				}
			}
		}

		// Add a default UUID, if no custom ID is provided
		if !hasId {
			if !hasUuid {
				model.Imports = append(model.Imports, "github.com/satori/go.uuid")
			}
			id := newName("id")
			id.Proper = "ID"
			a := attribute{Names: id, OriginalType: "uuid.UUID", GoType: "uuid.UUID"}
			// Ensure ID is the first attribute
			model.Attributes = append([]attribute{a}, model.Attributes...)
		}

		err := os.MkdirAll(model.Package, 0766)
		if err != nil {
			return errors.Wrapf(err, "couldn't create folder %s", model.Package)
		}

		err = model.Generate()
		if err != nil {
			return err
		}

		if !skipMigration {
			cflag := cmd.Flag("path")
			migrationPath := defaults.String(cflag.Value.String(), "./migrations")
			err = pop.MigrationCreate(migrationPath, fmt.Sprintf("create_%s", model.Names.Table), "fizz", []byte(model.Fizz()), []byte(fmt.Sprintf("drop_table(\"%s\")", model.Names.Table)))
			if err != nil {
				return err
			}
		}

		return nil
	},
}

func colType(s string) string {
	switch strings.ToLower(s) {
	case "text":
		return "string"
	case "time", "timestamp", "datetime":
		return "time.Time"
	case "nulls.text":
		return "nulls.String"
	case "uuid":
		return "uuid.UUID"
	case "json", "jsonb":
		return "slices.Map"
	case "[]string":
		return "slices.String"
	case "[]int":
		return "slices.Int"
	case "slices.float", "[]float", "[]float32", "[]float64":
		return "slices.Float"
	default:
		return s
	}
}

func fizzColType(s string) string {
	switch strings.ToLower(s) {
	case "int":
		return "integer"
	case "time", "datetime":
		return "timestamp"
	case "uuid.uuid", "uuid":
		return "uuid"
	case "nulls.float32", "nulls.float64":
		return "float"
	case "slices.string", "[]string":
		return "varchar[]"
	case "slices.float", "[]float", "[]float32", "[]float64":
		return "numeric[]"
	case "slices.int":
		return "int[]"
	case "slices.map":
		return "jsonb"
	default:
		if nrx.MatchString(s) {
			return fizzColType(strings.Replace(s, "nulls.", "", -1))
		}
		return strings.ToLower(s)
	}
}
<|endoftext|>


<commit_before>package test

import "testing"

func TestSendEcho(t *testing.T) {
	s := NewServerHelper(t).SeedDB()
	defer s.Stop()
	c := NewClientHelper(t, s).AsUser(&s.SeedData.User1).Dial()
	defer c.Close()

	id := c.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
	_, res, _ := c.ReadMsg(nil, nil)

	resMap := res.Result.(map[string]interface{})
	if res.ID != id || resMap["echo"] != "echo" {
		t.Fatal("Failed to receive echo message in proper format:", res)
	}

	// t.Fatal("Failed to send a message to the echo user")
	// t.Fatal("Failed to send batch message to the echo user")
	// t.Fatal("Failed to send large message to the echo user")
	// t.Fatal("Did not receive ACK for a message sent")
	// t.Fatal("Failed to receive a response from echo user")
	// t.Fatal("Could not send an ACK for an incoming message")
}

func TestMsgSend(t *testing.T) {
	s := NewServerHelper(t).SeedDB()
	defer s.Stop()
	c1 := NewClientHelper(t, s).AsUser(&s.SeedData.User1).Dial()
	defer c1.Close()
	c2 := NewClientHelper(t, s).AsUser(&s.SeedData.User2).Dial()
	defer c2.Close()

	type sendMsgReq struct {
		to      string
		message string
	}

	c1.WriteRequest("msg.send", sendMsgReq{to: "2", message: "lorem ip sum"})
	res := c2.ReadReq(nil)

	// t.Fatal("Failed to send message to an online peer.")
}

func TestMsgRecv(t *testing.T) {
	s := NewServerHelper(t).SeedDB()
	defer s.Stop()
	c1 := NewClientHelper(t, s).AsUser(&s.SeedData.User1).Dial()
	defer c1.Close()

	_ = c1.WriteRequest("msg.send", map[string]string{"to": "2", "msg": "How do you do?"})
	// todo: read ack manually or automate ack (just like sender does with channels and promises)

	c2 := NewClientHelper(t, s).AsUser(&s.SeedData.User2).Dial()
	defer c2.Close()

	// _ = c1.WriteRequest("msg.recv", nil)

	// t.Fatal("Failed to receive queued messages after coming online")
	// t.Fatal("Failed to send ACK for received message queue")
}
<commit_msg>add half of send test<commit_after>package test

import (
	"log"
	"testing"
)

func TestSendEcho(t *testing.T) {
	s := NewServerHelper(t).SeedDB()
	defer s.Stop()
	c := NewClientHelper(t, s).AsUser(&s.SeedData.User1).Dial()
	defer c.Close()

	id := c.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
	_, res, _ := c.ReadMsg(nil, nil)

	resMap := res.Result.(map[string]interface{})
	if res.ID != id || resMap["echo"] != "echo" {
		t.Fatal("Failed to receive echo message in proper format:", res)
	}

	// t.Fatal("Failed to send a message to the echo user")
	// t.Fatal("Failed to send batch message to the echo user")
	// t.Fatal("Failed to send large message to the echo user")
	// t.Fatal("Did not receive ACK for a message sent")
	// t.Fatal("Failed to receive a response from echo user")
	// t.Fatal("Could not send an ACK for an incoming message")
}

func TestMsgSend(t *testing.T) {
	s := NewServerHelper(t).SeedDB()
	defer s.Stop()
	c1 := NewClientHelper(t, s).AsUser(&s.SeedData.User1).Dial()
	defer c1.Close()
	c2 := NewClientHelper(t, s).AsUser(&s.SeedData.User2).Dial()
	defer c2.Close()

	type sendMsgReq struct {
		to      string
		message string
	}

	c1.WriteRequest("msg.send", sendMsgReq{to: "2", message: "lorem ip sum"})
	res := c1.ReadRes(nil)
	if res.Result != "ACK" {
		log.Fatal("Failed to send message to peer with response:", res)
	}

	type recvMsgReq struct {
		from    string
		message string
	}

	// var r recvMsgReq
	// req := c2.ReadReq(&r)

	// t.Fatal("Failed to send message to an online peer.")
}

func TestMsgRecv(t *testing.T) {
	s := NewServerHelper(t).SeedDB()
	defer s.Stop()
	c1 := NewClientHelper(t, s).AsUser(&s.SeedData.User1).Dial()
	defer c1.Close()

	_ = c1.WriteRequest("msg.send", map[string]string{"to": "2", "msg": "How do you do?"})
	// todo: read ack manually or automate ack (just like sender does with channels and promises)

	c2 := NewClientHelper(t, s).AsUser(&s.SeedData.User2).Dial()
	defer c2.Close()

	// _ = c1.WriteRequest("msg.recv", nil)

	// t.Fatal("Failed to receive queued messages after coming online")
	// t.Fatal("Failed to send ACK for received message queue")
}
<|endoftext|>


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

package tools

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/tomachalek/gloomy/vertical"
)

// NgramExtractor is used for extracting n-ngrams
// from a vertical file to a raw form
// TODO: specify format
type NgramExtractor struct {
	ngramSize   int
	buffer      *vertical.NgramBuffer
	buffPos     int
	stopWords   []string
	ignoreWords []string
	counter     map[string]int
}

func (n *NgramExtractor) isStopWord(w string) bool {
	for _, w2 := range n.stopWords {
		if w == w2 {
			return true
		}
	}
	return false
}

func (n *NgramExtractor) isIgnoreWord(w string) bool {
	for _, w2 := range n.ignoreWords {
		if w == w2 {
			return true
		}
	}
	return false
}

func (n *NgramExtractor) SaveNgrams(outPath string) error {
	out, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		panic(err) // TODO what can we do here?
	}
	for k, v := range n.counter {
		if v > 1 {
			_, werr := out.WriteString(fmt.Sprintf("%s %d\n", k, v))
			if werr != nil {
				panic(werr)
			}
		}
	}
	return nil
}

// ProcessLine processes a parsed vertical file line
func (n *NgramExtractor) ProcessLine(vline *vertical.Token) {
	if vline != nil {
		wordLC := vline.WordLC()
		if n.isStopWord(wordLC) {
			n.buffer.Reset()

		} else if !n.isIgnoreWord(wordLC) {
			n.buffer.AddToken(wordLC)
			if n.buffer.IsValid() {
				n.counter[n.buffer.Stringer()]++
			}
		}

	} else { // parser encoutered a structure
		n.buffer.Reset()
	}
}

// -----------------------------------------------------------

// ExtractNgrams runs the n-gram extraction process
func ExtractNgrams(conf *vertical.ParserConf, ngramSize int) {
	baseIndexPath := filepath.Join(conf.OutDirectory,
		fmt.Sprintf("ngrams-%s", filepath.Base(conf.VerticalFilePath)))
	extractor := &NgramExtractor{
		ngramSize:   ngramSize,
		buffer:      vertical.NewNgramBuffer(ngramSize),
		buffPos:     0,
		stopWords:   conf.NgramStopStrings,
		ignoreWords: conf.NgramIgnoreStrings,
		counter:     make(map[string]int),
	}
	vertical.ParseVerticalFile(conf, extractor)
	extractor.SaveNgrams(baseIndexPath)
}
<commit_msg>Remove unused property<commit_after>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tools

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/tomachalek/gloomy/vertical"
)

// NgramExtractor is used for extracting n-ngrams
// from a vertical file to a raw form
// TODO: specify format
type NgramExtractor struct {
	ngramSize   int
	buffer      *vertical.NgramBuffer
	stopWords   []string
	ignoreWords []string
	counter     map[string]int
}

func (n *NgramExtractor) isStopWord(w string) bool {
	for _, w2 := range n.stopWords {
		if w == w2 {
			return true
		}
	}
	return false
}

func (n *NgramExtractor) isIgnoreWord(w string) bool {
	for _, w2 := range n.ignoreWords {
		if w == w2 {
			return true
		}
	}
	return false
}

func (n *NgramExtractor) SaveNgrams(outPath string) error {
	out, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		panic(err) // TODO what can we do here?
	}
	for k, v := range n.counter {
		if v > 1 {
			_, werr := out.WriteString(fmt.Sprintf("%s %d\n", k, v))
			if werr != nil {
				panic(werr)
			}
		}
	}
	return nil
}

// ProcessLine processes a parsed vertical file line
func (n *NgramExtractor) ProcessLine(vline *vertical.Token) {
	if vline != nil {
		wordLC := vline.WordLC()
		if n.isStopWord(wordLC) {
			n.buffer.Reset()

		} else if !n.isIgnoreWord(wordLC) {
			n.buffer.AddToken(wordLC)
			if n.buffer.IsValid() {
				n.counter[n.buffer.Stringer()]++
			}
		}

	} else { // parser encoutered a structure
		n.buffer.Reset()
	}
}

// -----------------------------------------------------------

// ExtractNgrams runs the n-gram extraction process
func ExtractNgrams(conf *vertical.ParserConf, ngramSize int) {
	baseIndexPath := filepath.Join(conf.OutDirectory,
		fmt.Sprintf("ngrams-%s", filepath.Base(conf.VerticalFilePath)))
	extractor := &NgramExtractor{
		ngramSize:   ngramSize,
		buffer:      vertical.NewNgramBuffer(ngramSize),
		stopWords:   conf.NgramStopStrings,
		ignoreWords: conf.NgramIgnoreStrings,
		counter:     make(map[string]int),
	}
	vertical.ParseVerticalFile(conf, extractor)
	extractor.SaveNgrams(baseIndexPath)
}
<|endoftext|>


<commit_before>package test

import "testing"

func TestSendEcho(t *testing.T) {
	// sh := NewServerHelper(t).SeedDB()
	// defer sh.ListenAndServe().CloseWait()
	//
	// ch := sh.GetClientHelper().AsUser(&sh.SeedData.User1)
	// defer ch.Connect().CloseWait()
	//
	// id := c.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
	// res := c.ReadRes(nil)
	//
	// resMap := res.Result.(map[string]interface{})
	// if res.ID != id || resMap["echo"] != "echo" {
	// 	t.Fatal("Failed to receive echo message in proper format:", res)
	// }

	// t.Fatal("Failed to send a message to the echo user")
	// t.Fatal("Failed to send batch message to the echo user")
	// t.Fatal("Failed to send large message to the echo user")
	// t.Fatal("Did not receive ACK for a message sent")
	// t.Fatal("Failed to receive a response from echo user")
	// t.Fatal("Could not send an ACK for an incoming message")
}

//
// type sendMsgReq struct {
// 	To      string `json:"to"`
// 	Message string `json:"message"`
// }
//
// type recvMsgReq struct {
// 	From    string `json:"from"`
// 	Message string `json:"message"`
// }
//
// func TestSendMsgOnline(t *testing.T) {
// 	s := NewServerHelper(t).SeedDB()
// 	defer s.Stop()
// 	c1 := NewConnHelper(t, s).AsUser(&s.SeedData.User1).Dial()
// 	defer c1.Close()
// 	c2 := NewConnHelper(t, s).AsUser(&s.SeedData.User2).Dial()
// 	defer c2.Close()
//
// 	// send msg.recv request from user 2 to announce availability and complete client-cert auth
// 	c2.WriteRequest("msg.recv", nil)
// 	res := c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send msg.recv request from client 2 to server:", res)
// 	}
//
// 	// send message to user 2 with a basic hello message
// 	c1.WriteRequest("msg.send", sendMsgReq{To: "2", Message: "Hello, how are you?"})
// 	res = c1.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 2:", res)
// 	}
//
// 	// receive the hello message from user 1 (online) as user 2 (online)
// 	var c2r recvMsgReq
// 	c2req := c2.ReadReq(&c2r)
// 	if c2r.From != "1" {
// 		t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
// 	} else if c2r.Message != "Hello, how are you?" {
// 		t.Fatal("Received wrong message content:", c2r.Message)
// 	}
//
// 	c2.WriteResponse(c2req.ID, "ACK", nil)
//
// 	// send back a hello response to user 1 (online) as user 2 (online)
// 	c2.WriteRequest("msg.send", sendMsgReq{To: "1", Message: "I'm fine, thank you."})
// 	res = c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 1:", res)
// 	}
//
// 	// receive hello response from user 1 (online) as user 2 (online)
// 	var c1r recvMsgReq
// 	c1req := c1.ReadReq(&c1r)
// 	if c1r.From != "2" {
// 		t.Fatal("Received message from wrong sender instead of 2:", c1r.From)
// 	} else if c1r.Message != "I'm fine, thank you." {
// 		t.Fatal("Received wrong message content:", c1r.Message)
// 	}
//
// 	c1.WriteResponse(c1req.ID, "ACK", nil)
//
// 	// todo: verify that there are no pending requests for either user 1 or 2
// 	// todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
// 	c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
// 	resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
// 	if resfin != "echo" {
// 		t.Fatal("Last echo did return an invalid response:", resfin)
// 	}
// }
//
// func TestSendMsgOffline(t *testing.T) {
// 	s := NewServerHelper(t).SeedDB()
// 	defer s.Stop()
// 	c1 := NewConnHelper(t, s).AsUser(&s.SeedData.User1).Dial()
// 	defer c1.Close()
//
// 	// send message to user 2 with a basic hello message
// 	c1.WriteRequest("msg.send", sendMsgReq{To: "2", Message: "Hello, how are you?"})
// 	res := c1.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 2:", res)
// 	}
//
// 	// connect as user 2 and send msg.recv request to announce availability and complete client-cert auth
// 	c2 := NewConnHelper(t, s).AsUser(&s.SeedData.User2).Dial()
// 	defer c2.Close()
//
// 	c2.WriteRequest("msg.recv", nil)
// 	res = c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send msg.recv request from client 2 to server:", res)
// 	}
//
// 	// receive the hello message from user 1 (online) as user 2 (was offline at the time message was sent)
// 	var c2r recvMsgReq
// 	c2req := c2.ReadReq(&c2r)
// 	if c2r.From != "1" {
// 		t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
// 	} else if c2r.Message != "Hello, how are you?" {
// 		t.Fatal("Received wrong message content:", c2r.Message)
// 	}
//
// 	c2.WriteResponse(c2req.ID, "ACK", nil)
//
// 	// todo: verify that there are no pending requests for either user 1 or 2
// 	// todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
// 	c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
// 	resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
// 	if resfin != "echo" {
// 		t.Fatal("Last echo did return an invalid response:", resfin)
// 	}
//
// 	// todo: as client_helper is implicitly logging errors with t.Fatal(), we can't currently add useful information like below:
// 	// t.Fatal("Failed to receive queued messages after coming online")
// 	// t.Fatal("Failed to send ACK for received message queue")
// }
//
// func TestSendAsync(t *testing.T) {
// 	// test case to do all of the following simultaneously to test the async nature of titan server
// 	// - cert.auth
// 	// - msg.recv
// 	// - msg.send (bath to multiple people where some of whom are online)
// }
<commit_msg>renable TestSendEcho<commit_after>package test

import "testing"

func TestSendEcho(t *testing.T) {
	sh := NewServerHelper(t).SeedDB()
	defer sh.ListenAndServe().CloseWait()

	ch := sh.GetClientHelper().AsUser(&sh.SeedData.User1)
	defer ch.Connect().CloseWait()

	ch.Client.Echo(map[string]string{"echo": "echo", "token": "wow"}, nil)

	// resMap := res.Result.(map[string]interface{})
	// if res.ID != id || resMap["echo"] != "echo" {
	// 	t.Fatal("Failed to receive echo message in proper format:", res)
	// }

	// t.Fatal("Failed to send a message to the echo user")
	// t.Fatal("Failed to send batch message to the echo user")
	// t.Fatal("Failed to send large message to the echo user")
	// t.Fatal("Did not receive ACK for a message sent")
	// t.Fatal("Failed to receive a response from echo user")
	// t.Fatal("Could not send an ACK for an incoming message")
}

//
// type sendMsgReq struct {
// 	To      string `json:"to"`
// 	Message string `json:"message"`
// }
//
// type recvMsgReq struct {
// 	From    string `json:"from"`
// 	Message string `json:"message"`
// }
//
// func TestSendMsgOnline(t *testing.T) {
// 	s := NewServerHelper(t).SeedDB()
// 	defer s.Stop()
// 	c1 := NewConnHelper(t, s).AsUser(&s.SeedData.User1).Dial()
// 	defer c1.Close()
// 	c2 := NewConnHelper(t, s).AsUser(&s.SeedData.User2).Dial()
// 	defer c2.Close()
//
// 	// send msg.recv request from user 2 to announce availability and complete client-cert auth
// 	c2.WriteRequest("msg.recv", nil)
// 	res := c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send msg.recv request from client 2 to server:", res)
// 	}
//
// 	// send message to user 2 with a basic hello message
// 	c1.WriteRequest("msg.send", sendMsgReq{To: "2", Message: "Hello, how are you?"})
// 	res = c1.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 2:", res)
// 	}
//
// 	// receive the hello message from user 1 (online) as user 2 (online)
// 	var c2r recvMsgReq
// 	c2req := c2.ReadReq(&c2r)
// 	if c2r.From != "1" {
// 		t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
// 	} else if c2r.Message != "Hello, how are you?" {
// 		t.Fatal("Received wrong message content:", c2r.Message)
// 	}
//
// 	c2.WriteResponse(c2req.ID, "ACK", nil)
//
// 	// send back a hello response to user 1 (online) as user 2 (online)
// 	c2.WriteRequest("msg.send", sendMsgReq{To: "1", Message: "I'm fine, thank you."})
// 	res = c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 1:", res)
// 	}
//
// 	// receive hello response from user 1 (online) as user 2 (online)
// 	var c1r recvMsgReq
// 	c1req := c1.ReadReq(&c1r)
// 	if c1r.From != "2" {
// 		t.Fatal("Received message from wrong sender instead of 2:", c1r.From)
// 	} else if c1r.Message != "I'm fine, thank you." {
// 		t.Fatal("Received wrong message content:", c1r.Message)
// 	}
//
// 	c1.WriteResponse(c1req.ID, "ACK", nil)
//
// 	// todo: verify that there are no pending requests for either user 1 or 2
// 	// todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
// 	c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
// 	resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
// 	if resfin != "echo" {
// 		t.Fatal("Last echo did return an invalid response:", resfin)
// 	}
// }
//
// func TestSendMsgOffline(t *testing.T) {
// 	s := NewServerHelper(t).SeedDB()
// 	defer s.Stop()
// 	c1 := NewConnHelper(t, s).AsUser(&s.SeedData.User1).Dial()
// 	defer c1.Close()
//
// 	// send message to user 2 with a basic hello message
// 	c1.WriteRequest("msg.send", sendMsgReq{To: "2", Message: "Hello, how are you?"})
// 	res := c1.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 2:", res)
// 	}
//
// 	// connect as user 2 and send msg.recv request to announce availability and complete client-cert auth
// 	c2 := NewConnHelper(t, s).AsUser(&s.SeedData.User2).Dial()
// 	defer c2.Close()
//
// 	c2.WriteRequest("msg.recv", nil)
// 	res = c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send msg.recv request from client 2 to server:", res)
// 	}
//
// 	// receive the hello message from user 1 (online) as user 2 (was offline at the time message was sent)
// 	var c2r recvMsgReq
// 	c2req := c2.ReadReq(&c2r)
// 	if c2r.From != "1" {
// 		t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
// 	} else if c2r.Message != "Hello, how are you?" {
// 		t.Fatal("Received wrong message content:", c2r.Message)
// 	}
//
// 	c2.WriteResponse(c2req.ID, "ACK", nil)
//
// 	// todo: verify that there are no pending requests for either user 1 or 2
// 	// todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
// 	c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
// 	resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
// 	if resfin != "echo" {
// 		t.Fatal("Last echo did return an invalid response:", resfin)
// 	}
//
// 	// todo: as client_helper is implicitly logging errors with t.Fatal(), we can't currently add useful information like below:
// 	// t.Fatal("Failed to receive queued messages after coming online")
// 	// t.Fatal("Failed to send ACK for received message queue")
// }
//
// func TestSendAsync(t *testing.T) {
// 	// test case to do all of the following simultaneously to test the async nature of titan server
// 	// - cert.auth
// 	// - msg.recv
// 	// - msg.send (bath to multiple people where some of whom are online)
// }
<|endoftext|>


<commit_before>package test

import (
	"sync"
	"testing"

	"github.com/titan-x/titan/client"
)

func TestSendEcho(t *testing.T) {
	sh := NewServerHelper(t).SeedDB()
	defer sh.ListenAndServe().CloseWait()

	ch := sh.GetClientHelper().AsUser(&sh.SeedData.User1)
	defer ch.Connect().CloseWait()

	var wg sync.WaitGroup
	wg.Add(1)
	m := "Ola!"
	ch.Client.Echo(map[string]string{"message": m, "token": sh.SeedData.User1.JWTToken}, func(msg *client.Message) error {
		defer wg.Done()
		if msg.Message != m {
			t.Fatalf("expected: %v, got: %v", m, msg.Message)
		}
		return nil
	})

	wg.Wait()

	// t.Fatal("Failed to send a message to the echo user")
	// t.Fatal("Failed to send batch message to the echo user")
	// t.Fatal("Failed to send large message to the echo user")
	// t.Fatal("Did not receive ACK for a message sent")
	// t.Fatal("Failed to receive a response from echo user")
	// t.Fatal("Could not send an ACK for an incoming message")
}

func TestSendMsgOnline(t *testing.T) {
	sh := NewServerHelper(t).SeedDB()
	defer sh.ListenAndServe().CloseWait()

	ch1 := sh.GetClientHelper().AsUser(&sh.SeedData.User1)
	defer ch1.Connect().CloseWait()

	ch2 := sh.GetClientHelper().AsUser(&sh.SeedData.User2)
	defer ch2.Connect().CloseWait()

	var wg sync.WaitGroup

	// send client.info request from user 1 & 2 to server, to announce availability and get authenticated
	wg.Add(2)
	ch1.Client.JWTAuth(sh.SeedData.User1.JWTToken, func(m string) error {
		defer wg.Done()
		if m != "ACK" {
			t.Fatal("failed to send client.info request from client 1 to server:", m)
		}
		return nil
	})
	ch2.Client.JWTAuth(sh.SeedData.User2.JWTToken, func(m string) error {
		defer wg.Done()
		if m != "ACK" {
			t.Fatal("failed to send client.info request from client 2 to server:", m)
		}
		return nil
	})

	wg.Wait() // todo: authomate authentication with Client.UseJWT & Client.AuthHandler & Client.Authenticated = true ?

	// send a hello message from user 1 to user 2
	wg.Add(1)
	ch1.Client.SendMessages([]client.Message{client.Message{To: "2", Message: "Hello, how are you?"}}, func(ack string) error {
		defer wg.Done()
		if ack != "ACK" {
			t.Fatal("failed to send hello message to user 2:", ack)
		}
		return nil
	})

	//
	// // receive the hello message from user 1 (online) as user 2 (online)
	// var c2r recvMsgReq
	// c2req := c2.ReadReq(&c2r)
	// if c2r.From != "1" {
	// 	t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
	// } else if c2r.Message != "Hello, how are you?" {
	// 	t.Fatal("Received wrong message content:", c2r.Message)
	// }
	//
	// c2.WriteResponse(c2req.ID, "ACK", nil)
	//
	// // send back a hello response to user 1 (online) as user 2 (online)
	// c2.WriteRequest("msg.send", sendMsgReq{To: "1", Message: "I'm fine, thank you."})
	// res = c2.ReadRes(nil)
	// if res.Result != "ACK" {
	// 	t.Fatal("Failed to send message to user 1:", res)
	// }
	//
	// // receive hello response from user 1 (online) as user 2 (online)
	// var c1r recvMsgReq
	// c1req := c1.ReadReq(&c1r)
	// if c1r.From != "2" {
	// 	t.Fatal("Received message from wrong sender instead of 2:", c1r.From)
	// } else if c1r.Message != "I'm fine, thank you." {
	// 	t.Fatal("Received wrong message content:", c1r.Message)
	// }
	//
	// c1.WriteResponse(c1req.ID, "ACK", nil)
	//
	// // todo: verify that there are no pending requests for either user 1 or 2
	// // todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
	// c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
	// resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
	// if resfin != "echo" {
	// 	t.Fatal("Last echo did return an invalid response:", resfin)
	// }

	wg.Wait()
}

//
// func TestSendMsgOffline(t *testing.T) {
// 	s := NewServerHelper(t).SeedDB()
// 	defer s.Stop()
// 	c1 := NewConnHelper(t, s).AsUser(&s.SeedData.User1).Dial()
// 	defer c1.Close()
//
// 	// send message to user 2 with a basic hello message
// 	c1.WriteRequest("msg.send", sendMsgReq{To: "2", Message: "Hello, how are you?"})
// 	res := c1.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 2:", res)
// 	}
//
// 	// connect as user 2 and send msg.recv request to announce availability and complete client-cert auth
// 	c2 := NewConnHelper(t, s).AsUser(&s.SeedData.User2).Dial()
// 	defer c2.Close()
//
// 	c2.WriteRequest("msg.recv", nil)
// 	res = c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send msg.recv request from client 2 to server:", res)
// 	}
//
// 	// receive the hello message from user 1 (online) as user 2 (was offline at the time message was sent)
// 	var c2r recvMsgReq
// 	c2req := c2.ReadReq(&c2r)
// 	if c2r.From != "1" {
// 		t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
// 	} else if c2r.Message != "Hello, how are you?" {
// 		t.Fatal("Received wrong message content:", c2r.Message)
// 	}
//
// 	c2.WriteResponse(c2req.ID, "ACK", nil)
//
// 	// todo: verify that there are no pending requests for either user 1 or 2
// 	// todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
// 	c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
// 	resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
// 	if resfin != "echo" {
// 		t.Fatal("Last echo did return an invalid response:", resfin)
// 	}
//
// 	// todo: as client_helper is implicitly logging errors with t.Fatal(), we can't currently add useful information like below:
// 	// t.Fatal("Failed to receive queued messages after coming online")
// 	// t.Fatal("Failed to send ACK for received message queue")
// }
//
// func TestSendAsync(t *testing.T) {
// 	// test case to do all of the following simultaneously to test the async nature of titan server
// 	// - cert.auth
// 	// - msg.recv
// 	// - msg.send (bath to multiple people where some of whom are online)
// }
<commit_msg>add various todo items<commit_after>package test

import (
	"sync"
	"testing"

	"github.com/titan-x/titan/client"
)

func TestSendEcho(t *testing.T) {
	sh := NewServerHelper(t).SeedDB()
	defer sh.ListenAndServe().CloseWait()

	ch := sh.GetClientHelper().AsUser(&sh.SeedData.User1)
	defer ch.Connect().CloseWait()

	var wg sync.WaitGroup
	wg.Add(1)
	m := "Ola!"
	ch.Client.Echo(map[string]string{"message": m, "token": sh.SeedData.User1.JWTToken}, func(msg *client.Message) error {
		defer wg.Done()
		if msg.Message != m {
			t.Fatalf("expected: %v, got: %v", m, msg.Message)
		}
		return nil
	})

	wg.Wait()

	// t.Fatal("Failed to send a message to the echo user")
	// t.Fatal("Failed to send batch message to the echo user")
	// t.Fatal("Failed to send large message to the echo user")
	// t.Fatal("Did not receive ACK for a message sent")
	// t.Fatal("Failed to receive a response from echo user")
	// t.Fatal("Could not send an ACK for an incoming message")
}

func TestSendMsgOnline(t *testing.T) {
	sh := NewServerHelper(t).SeedDB()
	defer sh.ListenAndServe().CloseWait()

	ch1 := sh.GetClientHelper().AsUser(&sh.SeedData.User1)
	defer ch1.Connect().CloseWait()

	ch2 := sh.GetClientHelper().AsUser(&sh.SeedData.User2)
	defer ch2.Connect().CloseWait()

	var wg sync.WaitGroup

	// send client.info request from user 1 & 2 to server, to announce availability and get authenticated
	wg.Add(2)
	ch1.Client.JWTAuth(sh.SeedData.User1.JWTToken, func(m string) error {
		defer wg.Done()
		if m != "ACK" {
			t.Fatal("failed to send client.info request from client 1 to server:", m)
		}
		return nil
	})
	ch2.Client.JWTAuth(sh.SeedData.User2.JWTToken, func(m string) error {
		defer wg.Done()
		if m != "ACK" {
			t.Fatal("failed to send client.info request from client 2 to server:", m)
		}
		return nil
	})

	wg.Wait() // todo: authomate authentication with Client.UseJWT & Client.AuthHandler & Client.Authenticated = true ?

	// todo: if auth fails, Client.Connect() should close the conn and reuturn error to us (returning error in ctx should close the conn itself?)

	// send a hello message from user 1 to user 2
	wg.Add(1)
	ch1.Client.SendMessages([]client.Message{client.Message{To: "2", Message: "Hello, how are you?"}}, func(ack string) error {
		defer wg.Done()
		if ack != "ACK" {
			t.Fatal("failed to send hello message to user 2:", ack)
		}
		return nil
	})

	//
	// // receive the hello message from user 1 (online) as user 2 (online)
	// var c2r recvMsgReq
	// c2req := c2.ReadReq(&c2r)
	// if c2r.From != "1" {
	// 	t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
	// } else if c2r.Message != "Hello, how are you?" {
	// 	t.Fatal("Received wrong message content:", c2r.Message)
	// }
	//
	// c2.WriteResponse(c2req.ID, "ACK", nil)
	//
	// // send back a hello response to user 1 (online) as user 2 (online)
	// c2.WriteRequest("msg.send", sendMsgReq{To: "1", Message: "I'm fine, thank you."})
	// res = c2.ReadRes(nil)
	// if res.Result != "ACK" {
	// 	t.Fatal("Failed to send message to user 1:", res)
	// }
	//
	// // receive hello response from user 1 (online) as user 2 (online)
	// var c1r recvMsgReq
	// c1req := c1.ReadReq(&c1r)
	// if c1r.From != "2" {
	// 	t.Fatal("Received message from wrong sender instead of 2:", c1r.From)
	// } else if c1r.Message != "I'm fine, thank you." {
	// 	t.Fatal("Received wrong message content:", c1r.Message)
	// }
	//
	// c1.WriteResponse(c1req.ID, "ACK", nil)
	//
	// // todo: verify that there are no pending requests for either user 1 or 2
	// // todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
	// c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
	// resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
	// if resfin != "echo" {
	// 	t.Fatal("Last echo did return an invalid response:", resfin)
	// }

	wg.Wait()
}

//
// func TestSendMsgOffline(t *testing.T) {
// 	s := NewServerHelper(t).SeedDB()
// 	defer s.Stop()
// 	c1 := NewConnHelper(t, s).AsUser(&s.SeedData.User1).Dial()
// 	defer c1.Close()
//
// 	// send message to user 2 with a basic hello message
// 	c1.WriteRequest("msg.send", sendMsgReq{To: "2", Message: "Hello, how are you?"})
// 	res := c1.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send message to user 2:", res)
// 	}
//
// 	// connect as user 2 and send msg.recv request to announce availability and complete client-cert auth
// 	c2 := NewConnHelper(t, s).AsUser(&s.SeedData.User2).Dial()
// 	defer c2.Close()
//
// 	c2.WriteRequest("msg.recv", nil)
// 	res = c2.ReadRes(nil)
// 	if res.Result != "ACK" {
// 		t.Fatal("Failed to send msg.recv request from client 2 to server:", res)
// 	}
//
// 	// receive the hello message from user 1 (online) as user 2 (was offline at the time message was sent)
// 	var c2r recvMsgReq
// 	c2req := c2.ReadReq(&c2r)
// 	if c2r.From != "1" {
// 		t.Fatal("Received message from wrong sender instead of 1:", c2r.From)
// 	} else if c2r.Message != "Hello, how are you?" {
// 		t.Fatal("Received wrong message content:", c2r.Message)
// 	}
//
// 	c2.WriteResponse(c2req.ID, "ACK", nil)
//
// 	// todo: verify that there are no pending requests for either user 1 or 2
// 	// todo: below is a placeholder since writing last ACK response will never finish as we never wait for it
// 	c1.WriteRequest("msg.echo", map[string]string{"echo": "echo"})
// 	resfin := c1.ReadRes(nil).Result.(map[string]interface{})["echo"]
// 	if resfin != "echo" {
// 		t.Fatal("Last echo did return an invalid response:", resfin)
// 	}
//
// 	// todo: as client_helper is implicitly logging errors with t.Fatal(), we can't currently add useful information like below:
// 	// t.Fatal("Failed to receive queued messages after coming online")
// 	// t.Fatal("Failed to send ACK for received message queue")
// }
//
// func TestSendAsync(t *testing.T) {
// 	// test case to do all of the following simultaneously to test the async nature of titan server
// 	// - cert.auth
// 	// - msg.recv
// 	// - msg.send (bath to multiple people where some of whom are online)
// }
<|endoftext|>


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

package mem_test

import (
	"github.com/katydid/katydid/parser"
	"github.com/katydid/katydid/relapse/ast"
	"github.com/katydid/katydid/relapse/mem"
	"testing"
)

type reset interface {
	parser.Interface
	Reset() error
}

func bench(b *testing.B, grammar *ast.Grammar, gen func() parser.Interface) {
	num := 1000
	parsers := make([]reset, num)
	c := mem.Compile(grammar)
	for i := 0; i < num; i++ {
		p := gen().(reset)
		parsers[i] = p
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parsers[i%num].Reset(); err != nil {
			b.Fatal(err)
		}
		c.Interpret(parsers[i%num])
	}
}
<commit_msg>b.N flag for mem test<commit_after>//  Copyright 2016 Walter Schulze
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package mem_test

import (
	"flag"
	"github.com/katydid/katydid/parser"
	"github.com/katydid/katydid/relapse/ast"
	"github.com/katydid/katydid/relapse/mem"
	"testing"
)

type reset interface {
	parser.Interface
	Reset() error
}

var bN = flag.Int("b.N", 0, "the number of times the benchmark function's target code must run")

func init() {
	flag.Parse()
}

func bench(b *testing.B, grammar *ast.Grammar, gen func() parser.Interface) {
	num := 1000
	if *bN != 0 {
		b.N = *bN
	}
	parsers := make([]reset, num)
	c := mem.Compile(grammar)
	for i := 0; i < num; i++ {
		p := gen().(reset)
		parsers[i] = p
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parsers[i%num].Reset(); err != nil {
			b.Fatal(err)
		}
		c.Interpret(parsers[i%num])
	}
}
<|endoftext|>


<commit_before>package influxdb

import (
	"fmt"
	"time"

	log "github.com/funkygao/log4go"
	"github.com/influxdata/influxdb/client"
	"github.com/rcrowley/go-metrics"
)

func (this *reporter) dump() (pts []client.Point) {
	var (
		tags map[string]string
		now  = time.Now()
	)
	this.reg.Each(func(name string, i interface{}) {
		name, tags = this.extractTagsFromMetricsName(name)

		switch m := i.(type) {
		case metrics.Counter:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.count", name), // TODO perf
				Fields: map[string]interface{}{
					"value": m.Count(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Gauge:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.gauge", name),
				Fields: map[string]interface{}{
					"value": m.Value(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.GaugeFloat64:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.gauge", name),
				Fields: map[string]interface{}{
					"value": m.Value(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Histogram:
			ps := m.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.histogram", name),
				Fields: map[string]interface{}{
					"count":    m.Count(),
					"max":      m.Max(),
					"mean":     m.Mean(),
					"min":      m.Min(),
					"stddev":   m.StdDev(),
					"variance": m.Variance(),
					"p50":      ps[0],
					"p75":      ps[1],
					"p95":      ps[2],
					"p99":      ps[3],
					"p999":     ps[4],
					"p9999":    ps[5],
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Timer:
			ps := m.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.timer", name),
				Fields: map[string]interface{}{
					"count":    m.Count(),
					"max":      m.Max(),
					"mean":     m.Mean(),
					"min":      m.Min(),
					"stddev":   m.StdDev(),
					"variance": m.Variance(),
					"p50":      ps[0],
					"p75":      ps[1],
					"p95":      ps[2],
					"p99":      ps[3],
					"p999":     ps[4],
					"p9999":    ps[5],
					"m1":       m.Rate1(),
					"m5":       m.Rate5(),
					"m15":      m.Rate15(),
					"meanrate": m.RateMean(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Meter:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.meter", name),
				Fields: map[string]interface{}{
					"count": m.Count(),
					"m1":    m.Rate1(),
					"m5":    m.Rate5(),
					"m15":   m.Rate15(),
					"mean":  m.RateMean(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Healthcheck:
			// ignored

		}
	})

	return
}

func (this *reporter) writeInfluxDB(pts []client.Point) {
	if this.client == nil {
		log.Warn("influxdb write while connection lost")
		return
	}

	if _, err := this.client.Write(client.BatchPoints{
		Points:   pts,
		Database: this.cf.database,
	}); err != nil {
		log.Error("influxdb write: %v", err)
	}
}
<commit_msg>retry once if found lost conn<commit_after>package influxdb

import (
	"fmt"
	"time"

	log "github.com/funkygao/log4go"
	"github.com/influxdata/influxdb/client"
	"github.com/rcrowley/go-metrics"
)

func (this *reporter) dump() (pts []client.Point) {
	var (
		tags map[string]string
		now  = time.Now()
	)
	this.reg.Each(func(name string, i interface{}) {
		name, tags = this.extractTagsFromMetricsName(name)

		switch m := i.(type) {
		case metrics.Counter:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.count", name), // TODO perf
				Fields: map[string]interface{}{
					"value": m.Count(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Gauge:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.gauge", name),
				Fields: map[string]interface{}{
					"value": m.Value(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.GaugeFloat64:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.gauge", name),
				Fields: map[string]interface{}{
					"value": m.Value(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Histogram:
			ps := m.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.histogram", name),
				Fields: map[string]interface{}{
					"count":    m.Count(),
					"max":      m.Max(),
					"mean":     m.Mean(),
					"min":      m.Min(),
					"stddev":   m.StdDev(),
					"variance": m.Variance(),
					"p50":      ps[0],
					"p75":      ps[1],
					"p95":      ps[2],
					"p99":      ps[3],
					"p999":     ps[4],
					"p9999":    ps[5],
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Timer:
			ps := m.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.timer", name),
				Fields: map[string]interface{}{
					"count":    m.Count(),
					"max":      m.Max(),
					"mean":     m.Mean(),
					"min":      m.Min(),
					"stddev":   m.StdDev(),
					"variance": m.Variance(),
					"p50":      ps[0],
					"p75":      ps[1],
					"p95":      ps[2],
					"p99":      ps[3],
					"p999":     ps[4],
					"p9999":    ps[5],
					"m1":       m.Rate1(),
					"m5":       m.Rate5(),
					"m15":      m.Rate15(),
					"meanrate": m.RateMean(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Meter:
			pts = append(pts, client.Point{
				Measurement: fmt.Sprintf("%s.meter", name),
				Fields: map[string]interface{}{
					"count": m.Count(),
					"m1":    m.Rate1(),
					"m5":    m.Rate5(),
					"m15":   m.Rate15(),
					"mean":  m.RateMean(),
				},
				Tags: tags,
				Time: now,
			})

		case metrics.Healthcheck:
			// ignored

		}
	})

	return
}

func (this *reporter) writeInfluxDB(pts []client.Point) {
	if this.client == nil {
		log.Warn("influxdb write while connection lost, retry...")

		if err := this.makeClient(); err != nil {
			log.Error("influxdb connect retry: %v", err)
			return
		} else {
			log.Info("influxdb connect retry ok")
		}
	}

	if _, err := this.client.Write(client.BatchPoints{
		Points:   pts,
		Database: this.cf.database,
	}); err != nil {
		log.Error("influxdb write: %v", err)
	}
}
<|endoftext|>