<commit_before>package components

import (
	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"math/big"
	"time"

	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	v12 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"kubevirt.io/kubevirt/pkg/certificates/bootstrap"
	certutil "kubevirt.io/kubevirt/pkg/certificates/triple/cert"
)

var _ = Describe("Certificate Management", func() {
	Context("CA certificate bundle", func() {

		It("should drop expired CAs", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(-10*time.Minute)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be properly appended when within the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out the first CA cert if it is outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-3*time.Minute), now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out a CA cert which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[1]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out multiple CA certs which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(-5*time.Minute).Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should ensure that the current CA is not added over and over again", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				current,
				current,
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be protected against misuse by cropping big arrays", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{}
			for i := 1; i < 20; i++ {
				given = append(given, NewSelfSignedCert(now.Add(-1*time.Minute), now.Add(1*time.Hour)))
			}
			givenBundle := CACertsToBundle(given)
			_, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(11))
		})

		It("should immediately suggest a rotation if the cert is not signed by the provided CA", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			ca := NewSelfSignedCert(now, now.Add(1*time.Hour))
			duration := &v1.Duration{Duration: 5 * time.Hour}
			deadline := NextRotationDeadline(current, ca, duration)
			Expect(deadline.Before(time.Now())).To(BeTrue())
		})

		It("should set notBefore on the certificate to notBefore value of the CA certificate ", func() {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(crt.Leaf.NotBefore).To(Equal(caCrt.Leaf.NotBefore))
		})

		table.DescribeTable("should set the notAfter on the certificate according to the supplied duration", func(caDuration time.Duration) {
			now := time.Now()
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			//deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			Expect(crt.Leaf.NotAfter.Unix()).To(BeNumerically("==", now.Add(crtDuration.Duration).Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should suggaest a rotation on the certificate according on 80% of the certificate lifespan", func(caDuration time.Duration) {
			now := time.Now()
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			// Generating certificates may take a little bit of time to execute (entropy, ...). Since we can't
			// inject a fake time into the foreign code which generates the certificates, allow a generous diff of three
			// seconds.
			Expect(NextRotationDeadline(crt, caCrt, crtDuration).Unix()).To(BeNumerically("==", deadline.Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should successfully sign with the current CA the certificate for", func(scretName string) {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			var crtSecret *v12.Secret
			for _, s := range NewCertSecrets("test", "test") {
				if s.Name == scretName {
					crtSecret = s
					break
				}
			}
			Expect(crtSecret).ToNot(BeNil())
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(err).ToNot(HaveOccurred())
			Expect(crt).ToNot(BeNil())
		},
			table.Entry("virt-handler", VirtHandlerCertSecretName),
			table.Entry("virt-controller", VirtControllerCertSecretName),
			table.Entry("virt-api", VirtApiCertSecretName),
			table.Entry("virt-operator", VirtOperatorCertSecretName),
		)
	})

	It("should set the right namespaces on the certificate secrets", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		for _, secret := range secrets[:len(secrets)-1] {
			Expect(secret.Namespace).To(Equal("install_namespace"))
			Expect(secret.Name).ToNot(Equal(VirtOperatorCertSecretName))
		}
		Expect(secrets[len(secrets)-1].Namespace).To(Equal("operator_namespace"))
	})

	It("should create the kubevirt-ca configmap for the right namespace", func() {
		configMap := NewKubeVirtCAConfigMap("namespace")
		Expect(configMap.Namespace).To(Equal("namespace"))
	})

	It("should populate secrets with certificates", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		caSecret := NewCACertSecret("operator_namespace")
		Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
		Expect(caSecret.Data).To(HaveKey(bootstrap.CertBytesValue))
		Expect(caSecret.Data).To(HaveKey(bootstrap.KeyBytesValue))

		caCert, err := LoadCertificates(caSecret)
		Expect(err).ToNot(HaveOccurred())

		for _, secret := range secrets {
			Expect(PopulateSecretWithCertificate(secret, caCert, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
			Expect(secret.Data).To(HaveKey(bootstrap.CertBytesValue))
			Expect(secret.Data).To(HaveKey(bootstrap.KeyBytesValue))
			_, err = LoadCertificates(secret)
			Expect(err).ToNot(HaveOccurred())
		}
	})
})

// NewSelfSignedCert creates a CA certificate
func NewSelfSignedCert(notBefore time.Time, notAfter time.Time) *tls.Certificate {
	key, err := certutil.NewPrivateKey()
	Expect(err).ToNot(HaveOccurred())
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   "who",
			Organization: []string{"cares"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	Expect(err).ToNot(HaveOccurred())
	leaf, err := x509.ParseCertificate(certDERBytes)
	Expect(err).ToNot(HaveOccurred())
	keyBytes := certutil.EncodePrivateKeyPEM(key)
	Expect(err).ToNot(HaveOccurred())

	crtBytes := certutil.EncodeCertPEM(leaf)
	crt, err := tls.X509KeyPair(crtBytes, keyBytes)
	Expect(err).ToNot(HaveOccurred())
	crt.Leaf = leaf
	return &crt
}

func CACertsToBundle(crts []*tls.Certificate) []byte {
	var caBundle []byte
	for _, crt := range crts {
		caBundle = append(caBundle, certutil.EncodeCertPEM(crt.Leaf)...)
	}
	return caBundle
}
<commit_msg>Take time in cert tests after CA generation<commit_after>package components

import (
	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"math/big"
	"time"

	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	v12 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"kubevirt.io/kubevirt/pkg/certificates/bootstrap"
	certutil "kubevirt.io/kubevirt/pkg/certificates/triple/cert"
)

var _ = Describe("Certificate Management", func() {
	Context("CA certificate bundle", func() {

		It("should drop expired CAs", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(-10*time.Minute)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be properly appended when within the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out the first CA cert if it is outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-3*time.Minute), now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out a CA cert which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[1]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should kick out multiple CA certs which are outside of the overlap period", func() {
			now := time.Now()
			current := NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(-5*time.Minute).Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-10*time.Minute), now.Add(1*time.Hour)),
				NewSelfSignedCert(now.Add(-5*time.Minute), now.Add(1*time.Hour)),
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(1))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should ensure that the current CA is not added over and over again", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{
				NewSelfSignedCert(now.Add(-20*time.Minute), now.Add(1*time.Hour)),
				current,
				current,
			}
			givenBundle := CACertsToBundle(given)
			expectBundle := CACertsToBundle([]*tls.Certificate{current, given[0]})
			bundle, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(2))
			Expect(bundle).To(Equal(expectBundle))
		})

		It("should be protected against misuse by cropping big arrays", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			given := []*tls.Certificate{}
			for i := 1; i < 20; i++ {
				given = append(given, NewSelfSignedCert(now.Add(-1*time.Minute), now.Add(1*time.Hour)))
			}
			givenBundle := CACertsToBundle(given)
			_, count, err := MergeCABundle(current, givenBundle, 2*time.Minute)
			Expect(err).ToNot(HaveOccurred())
			Expect(count).To(Equal(11))
		})

		It("should immediately suggest a rotation if the cert is not signed by the provided CA", func() {
			now := time.Now()
			current := NewSelfSignedCert(now, now.Add(1*time.Hour))
			ca := NewSelfSignedCert(now, now.Add(1*time.Hour))
			duration := &v1.Duration{Duration: 5 * time.Hour}
			deadline := NextRotationDeadline(current, ca, duration)
			Expect(deadline.Before(time.Now())).To(BeTrue())
		})

		It("should set notBefore on the certificate to notBefore value of the CA certificate ", func() {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(crt.Leaf.NotBefore).To(Equal(caCrt.Leaf.NotBefore))
		})

		table.DescribeTable("should set the notAfter on the certificate according to the supplied duration", func(caDuration time.Duration) {
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			now := time.Now()
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			//deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			Expect(crt.Leaf.NotAfter.Unix()).To(BeNumerically("==", now.Add(crtDuration.Duration).Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should suggaest a rotation on the certificate according on 80% of the certificate lifespan", func(caDuration time.Duration) {
			crtDuration := &v1.Duration{Duration: 2 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: caDuration})).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			now := time.Now()
			Expect(err).NotTo(HaveOccurred())
			crtSecret := NewCertSecrets("test", "test")[0]
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, crtDuration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)

			deadline := now.Add(time.Duration(float64(crtDuration.Duration) * 0.8))
			// Generating certificates may take a little bit of time to execute (entropy, ...). Since we can't
			// inject a fake time into the foreign code which generates the certificates, allow a generous diff of three
			// seconds.
			Expect(NextRotationDeadline(crt, caCrt, crtDuration).Unix()).To(BeNumerically("==", deadline.Unix(), 3))
		},
			table.Entry("with a long valid CA", 24*time.Hour),
			table.Entry("with a CA which expires before the certificate rotation", 1*time.Hour),
		)

		table.DescribeTable("should successfully sign with the current CA the certificate for", func(scretName string) {
			duration := &v1.Duration{Duration: 5 * time.Hour}
			caSecret := NewCACertSecret("test")
			Expect(PopulateSecretWithCertificate(caSecret, nil, duration)).To(Succeed())
			caCrt, err := LoadCertificates(caSecret)
			Expect(err).NotTo(HaveOccurred())
			var crtSecret *v12.Secret
			for _, s := range NewCertSecrets("test", "test") {
				if s.Name == scretName {
					crtSecret = s
					break
				}
			}
			Expect(crtSecret).ToNot(BeNil())
			Expect(PopulateSecretWithCertificate(crtSecret, caCrt, duration)).To(Succeed())
			crt, err := LoadCertificates(crtSecret)
			Expect(err).ToNot(HaveOccurred())
			Expect(crt).ToNot(BeNil())
		},
			table.Entry("virt-handler", VirtHandlerCertSecretName),
			table.Entry("virt-controller", VirtControllerCertSecretName),
			table.Entry("virt-api", VirtApiCertSecretName),
			table.Entry("virt-operator", VirtOperatorCertSecretName),
		)
	})

	It("should set the right namespaces on the certificate secrets", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		for _, secret := range secrets[:len(secrets)-1] {
			Expect(secret.Namespace).To(Equal("install_namespace"))
			Expect(secret.Name).ToNot(Equal(VirtOperatorCertSecretName))
		}
		Expect(secrets[len(secrets)-1].Namespace).To(Equal("operator_namespace"))
	})

	It("should create the kubevirt-ca configmap for the right namespace", func() {
		configMap := NewKubeVirtCAConfigMap("namespace")
		Expect(configMap.Namespace).To(Equal("namespace"))
	})

	It("should populate secrets with certificates", func() {
		secrets := NewCertSecrets("install_namespace", "operator_namespace")
		caSecret := NewCACertSecret("operator_namespace")
		Expect(PopulateSecretWithCertificate(caSecret, nil, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
		Expect(caSecret.Data).To(HaveKey(bootstrap.CertBytesValue))
		Expect(caSecret.Data).To(HaveKey(bootstrap.KeyBytesValue))

		caCert, err := LoadCertificates(caSecret)
		Expect(err).ToNot(HaveOccurred())

		for _, secret := range secrets {
			Expect(PopulateSecretWithCertificate(secret, caCert, &v1.Duration{Duration: 1 * time.Hour})).To(Succeed())
			Expect(secret.Data).To(HaveKey(bootstrap.CertBytesValue))
			Expect(secret.Data).To(HaveKey(bootstrap.KeyBytesValue))
			_, err = LoadCertificates(secret)
			Expect(err).ToNot(HaveOccurred())
		}
	})
})

// NewSelfSignedCert creates a CA certificate
func NewSelfSignedCert(notBefore time.Time, notAfter time.Time) *tls.Certificate {
	key, err := certutil.NewPrivateKey()
	Expect(err).ToNot(HaveOccurred())
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   "who",
			Organization: []string{"cares"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	Expect(err).ToNot(HaveOccurred())
	leaf, err := x509.ParseCertificate(certDERBytes)
	Expect(err).ToNot(HaveOccurred())
	keyBytes := certutil.EncodePrivateKeyPEM(key)
	Expect(err).ToNot(HaveOccurred())

	crtBytes := certutil.EncodeCertPEM(leaf)
	crt, err := tls.X509KeyPair(crtBytes, keyBytes)
	Expect(err).ToNot(HaveOccurred())
	crt.Leaf = leaf
	return &crt
}

func CACertsToBundle(crts []*tls.Certificate) []byte {
	var caBundle []byte
	for _, crt := range crts {
		caBundle = append(caBundle, certutil.EncodeCertPEM(crt.Leaf)...)
	}
	return caBundle
}
<|endoftext|>


<commit_before>package dclupgrade

import (
	"context"
	"fmt"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
	"github.com/stretchr/testify/require"
	testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants"
	test_dclauth "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/grpc_rest/dclauth"
	"github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/utils"
	dclauthtypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types"
	dclupgradetypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types"
)

func NewMsgProposeUpgrade(signer string, name string, height int64, info string) *dclupgradetypes.MsgProposeUpgrade {
	return &dclupgradetypes.MsgProposeUpgrade{
		Creator: signer,
		Plan: upgradetypes.Plan{
			Name:   name,
			Height: height,
			Info:   info,
		},
	}
}

func NewMsgApproveUpgrade(signer string, name string) *dclupgradetypes.MsgApproveUpgrade {
	return &dclupgradetypes.MsgApproveUpgrade{
		Creator: signer,
		Name:    name,
	}
}

func ProposeUpgrade(
	suite *utils.TestSuite,
	msg *dclupgradetypes.MsgProposeUpgrade,
	signerName string,
	signerAccount *dclauthtypes.Account,
) (*sdk.TxResponse, error) {
	msg.Creator = suite.GetAddress(signerName).String()
	return suite.BuildAndBroadcastTx([]sdk.Msg{msg}, signerName, signerAccount)
}

func ApproveUpgrade(
	suite *utils.TestSuite,
	msg *dclupgradetypes.MsgApproveUpgrade,
	signerName string,
	signerAccount *dclauthtypes.Account,
) (*sdk.TxResponse, error) {
	msg.Creator = suite.GetAddress(signerName).String()
	return suite.BuildAndBroadcastTx([]sdk.Msg{msg}, signerName, signerAccount)
}

func GetProposedUpgrade(
	suite *utils.TestSuite,
	name string,
) (*dclupgradetypes.ProposedUpgrade, error) {
	var res dclupgradetypes.ProposedUpgrade

	if suite.Rest {
		var resp dclupgradetypes.QueryGetProposedUpgradeResponse
		err := suite.QueryREST(fmt.Sprintf("/dcl/dclupgrade/proposed-upgrades/%s", name), &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ProposedUpgrade(
			context.Background(),
			&dclupgradetypes.QueryGetProposedUpgradeRequest{Name: name},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	}

	return &res, nil
}

func GetApprovedUpgrade(
	suite *utils.TestSuite,
	name string,
) (*dclupgradetypes.ApprovedUpgrade, error) {
	var res dclupgradetypes.ApprovedUpgrade

	if suite.Rest {
		var resp dclupgradetypes.QueryGetApprovedUpgradeResponse
		err := suite.QueryREST(fmt.Sprintf("/dcl/dclupgrade/approved-upgrades/%s", name), &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ApprovedUpgrade(
			context.Background(),
			&dclupgradetypes.QueryGetApprovedUpgradeRequest{Name: name},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	}

	return &res, nil
}

func GetProposedUpgrades(suite *utils.TestSuite) (res []dclupgradetypes.ProposedUpgrade, err error) {
	if suite.Rest {
		var resp dclupgradetypes.QueryAllProposedUpgradeResponse
		err := suite.QueryREST("/dcl/dclupgrade/proposed-upgrades", &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ProposedUpgradeAll(
			context.Background(),
			&dclupgradetypes.QueryAllProposedUpgradeRequest{},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	}

	return res, nil
}

func GetApprovedUpgrades(suite *utils.TestSuite) (res []dclupgradetypes.ApprovedUpgrade, err error) {
	if suite.Rest {
		var resp dclupgradetypes.QueryAllApprovedUpgradeResponse
		err := suite.QueryREST("/dcl/dclupgrade/approved-upgrades", &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ApprovedUpgradeAll(
			context.Background(),
			&dclupgradetypes.QueryAllApprovedUpgradeRequest{},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	}

	return res, nil
}

func DCLUpgradeDemo(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	bobAccount, err := test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// trustee proposes upgrade
	proposeUpgradeMsg := NewMsgProposeUpgrade(aliceAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.NoError(suite.T, err)

	// Check upgrade is proposed
	proposedUpgrade, err := GetProposedUpgrade(suite, proposeUpgradeMsg.Plan.Name)
	require.NoError(suite.T, err)
	require.Equal(suite.T, proposeUpgradeMsg.Creator, proposedUpgrade.Creator)
	require.Equal(suite.T, proposeUpgradeMsg.Plan, proposedUpgrade.Plan)

	// Get all proposed upgrades
	_, err = GetProposedUpgrades(suite)
	require.NoError(suite.T, err)

	// Get all approved upgrades
	approvedUpgrades, err := GetApprovedUpgrades(suite)
	require.NoError(suite.T, err)
	require.NotContains(suite.T, approvedUpgrades, proposedUpgrade)

	// another trustee approves upgrade
	approveUpgradeMsg := NewMsgApproveUpgrade(bobAccount.Address, proposeUpgradeMsg.Plan.Name)
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{approveUpgradeMsg}, bobName, bobAccount)
	require.NoError(suite.T, err)

	// Check upgrade is approved
	approvedUpgrade, err := GetApprovedUpgrade(suite, proposeUpgradeMsg.Plan.Name)
	require.NoError(suite.T, err)
	require.Equal(suite.T, proposeUpgradeMsg.Creator, approvedUpgrade.Creator)
	require.Equal(suite.T, proposeUpgradeMsg.Plan, approvedUpgrade.Plan)

	// Get all proposed upgrades
	proposedUpgrades, err := GetProposedUpgrades(suite)
	require.NoError(suite.T, err)
	require.NotContains(suite.T, proposedUpgrades, proposedUpgrade)

	// Get all approved upgrades
	approvedUpgrades, err = GetApprovedUpgrades(suite)
	require.NoError(suite.T, err)
	require.Contains(suite.T, approvedUpgrades, *approvedUpgrade)
}

/* Error cases */

func ProposeUpgradeByNonTrustee(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	bobAccount, err := test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// register new account without Trustee role
	nonTrusteeAccountName := utils.RandString()
	nonTrusteeAccount := test_dclauth.CreateAccount(
		suite,
		nonTrusteeAccountName,
		dclauthtypes.AccountRoles{dclauthtypes.CertificationCenter},
		1,
		aliceName,
		aliceAccount,
		bobName,
		bobAccount,
		testconstants.Info,
	)

	require.NotContains(suite.T, nonTrusteeAccount.Roles, dclupgradetypes.UpgradeApprovalRole)

	// try to add proposeUpgradeMsg
	proposeUpgradeMsg := NewMsgProposeUpgrade(nonTrusteeAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, nonTrusteeAccountName, nonTrusteeAccount)
	require.Error(suite.T, err)
	require.ErrorIs(suite.T, sdkerrors.ErrUnauthorized, err)
}

func ApproveUpgradeByNonTrustee(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	bobAccount, err := test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// register new account without Trustee role
	nonTrusteeAccountName := utils.RandString()
	nonTrusteeAccount := test_dclauth.CreateAccount(
		suite,
		nonTrusteeAccountName,
		dclauthtypes.AccountRoles{dclauthtypes.CertificationCenter},
		1,
		aliceName,
		aliceAccount,
		bobName,
		bobAccount,
		testconstants.Info,
	)

	require.NotContains(suite.T, nonTrusteeAccount.Roles, dclupgradetypes.UpgradeApprovalRole)

	// propose upgrade
	proposeUpgradeMsg := NewMsgProposeUpgrade(aliceAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.NoError(suite.T, err)

	// try to approve upgrade
	approveUpgradeMsg := NewMsgApproveUpgrade(nonTrusteeAccount.Address, proposeUpgradeMsg.Plan.Name)
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{approveUpgradeMsg}, nonTrusteeAccountName, nonTrusteeAccount)
	require.Error(suite.T, err)
	require.ErrorIs(suite.T, sdkerrors.ErrUnauthorized, err)
}

func ProposeUpgradeTwice(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	_, err = test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// trustee proposes upgrade
	proposeUpgradeMsg := NewMsgProposeUpgrade(aliceAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.NoError(suite.T, err)

	// trustee proposes the same upgrade
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.Error(suite.T, err)
	require.ErrorIs(suite.T, dclupgradetypes.ErrProposedUpgradeAlreadyExists, err)
}

func GetNotProposedUpgrade(suite *utils.TestSuite) {
	_, err := GetProposedUpgrade(suite, "123")
	require.Error(suite.T, err)
	suite.AssertNotFound(err)
}

func GetNotApprovedUpgrade(suite *utils.TestSuite) {
	_, err := GetProposedUpgrade(suite, "123")
	require.Error(suite.T, err)
	suite.AssertNotFound(err)
}
<commit_msg>tests updated<commit_after>package dclupgrade

import (
	"context"
	"fmt"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
	"github.com/stretchr/testify/require"
	testconstants "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/constants"
	test_dclauth "github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/grpc_rest/dclauth"
	"github.com/zigbee-alliance/distributed-compliance-ledger/integration_tests/utils"
	dclauthtypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclauth/types"
	dclupgradetypes "github.com/zigbee-alliance/distributed-compliance-ledger/x/dclupgrade/types"
)

func NewMsgProposeUpgrade(signer string, name string, height int64, info string) *dclupgradetypes.MsgProposeUpgrade {
	return &dclupgradetypes.MsgProposeUpgrade{
		Creator: signer,
		Plan: upgradetypes.Plan{
			Name:   name,
			Height: height,
			Info:   info,
		},
	}
}

func NewMsgApproveUpgrade(signer string, name string) *dclupgradetypes.MsgApproveUpgrade {
	return &dclupgradetypes.MsgApproveUpgrade{
		Creator: signer,
		Name:    name,
	}
}

func ProposeUpgrade(
	suite *utils.TestSuite,
	msg *dclupgradetypes.MsgProposeUpgrade,
	signerName string,
	signerAccount *dclauthtypes.Account,
) (*sdk.TxResponse, error) {
	msg.Creator = suite.GetAddress(signerName).String()
	return suite.BuildAndBroadcastTx([]sdk.Msg{msg}, signerName, signerAccount)
}

func ApproveUpgrade(
	suite *utils.TestSuite,
	msg *dclupgradetypes.MsgApproveUpgrade,
	signerName string,
	signerAccount *dclauthtypes.Account,
) (*sdk.TxResponse, error) {
	msg.Creator = suite.GetAddress(signerName).String()
	return suite.BuildAndBroadcastTx([]sdk.Msg{msg}, signerName, signerAccount)
}

func GetProposedUpgrade(
	suite *utils.TestSuite,
	name string,
) (*dclupgradetypes.ProposedUpgrade, error) {
	var res dclupgradetypes.ProposedUpgrade

	if suite.Rest {
		var resp dclupgradetypes.QueryGetProposedUpgradeResponse
		err := suite.QueryREST(fmt.Sprintf("/dcl/dclupgrade/proposed-upgrades/%s", name), &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ProposedUpgrade(
			context.Background(),
			&dclupgradetypes.QueryGetProposedUpgradeRequest{Name: name},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	}

	return &res, nil
}

func GetApprovedUpgrade(
	suite *utils.TestSuite,
	name string,
) (*dclupgradetypes.ApprovedUpgrade, error) {
	var res dclupgradetypes.ApprovedUpgrade

	if suite.Rest {
		var resp dclupgradetypes.QueryGetApprovedUpgradeResponse
		err := suite.QueryREST(fmt.Sprintf("/dcl/dclupgrade/approved-upgrades/%s", name), &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ApprovedUpgrade(
			context.Background(),
			&dclupgradetypes.QueryGetApprovedUpgradeRequest{Name: name},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	}

	return &res, nil
}

func GetProposedUpgrades(suite *utils.TestSuite) (res []dclupgradetypes.ProposedUpgrade, err error) {
	if suite.Rest {
		var resp dclupgradetypes.QueryAllProposedUpgradeResponse
		err := suite.QueryREST("/dcl/dclupgrade/proposed-upgrades", &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ProposedUpgradeAll(
			context.Background(),
			&dclupgradetypes.QueryAllProposedUpgradeRequest{},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetProposedUpgrade()
	}

	return res, nil
}

func GetApprovedUpgrades(suite *utils.TestSuite) (res []dclupgradetypes.ApprovedUpgrade, err error) {
	if suite.Rest {
		var resp dclupgradetypes.QueryAllApprovedUpgradeResponse
		err := suite.QueryREST("/dcl/dclupgrade/approved-upgrades", &resp)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	} else {
		grpcConn := suite.GetGRPCConn()
		defer grpcConn.Close()

		// This creates a gRPC client to query the x/dclauth service.
		dclupgradeClient := dclupgradetypes.NewQueryClient(grpcConn)
		resp, err := dclupgradeClient.ApprovedUpgradeAll(
			context.Background(),
			&dclupgradetypes.QueryAllApprovedUpgradeRequest{},
		)
		if err != nil {
			return nil, err
		}
		res = resp.GetApprovedUpgrade()
	}

	return res, nil
}

func DCLUpgradeDemo(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	bobAccount, err := test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// trustee proposes upgrade
	proposeUpgradeMsg := NewMsgProposeUpgrade(aliceAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.NoError(suite.T, err)

	// Check upgrade is proposed
	proposedUpgrade, err := GetProposedUpgrade(suite, proposeUpgradeMsg.Plan.Name)
	require.NoError(suite.T, err)
	require.Equal(suite.T, proposeUpgradeMsg.Creator, proposedUpgrade.Creator)
	require.Equal(suite.T, proposeUpgradeMsg.Plan, proposedUpgrade.Plan)

	// Get all proposed upgrades
	_, err = GetProposedUpgrades(suite)
	require.NoError(suite.T, err)

	// Get all approved upgrades
	approvedUpgrades, err := GetApprovedUpgrades(suite)
	require.NoError(suite.T, err)
	require.NotContains(suite.T, approvedUpgrades, proposedUpgrade)

	// another trustee approves upgrade
	approveUpgradeMsg := NewMsgApproveUpgrade(bobAccount.Address, proposeUpgradeMsg.Plan.Name)
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{approveUpgradeMsg}, bobName, bobAccount)
	require.NoError(suite.T, err)

	// Check upgrade is approved
	approvedUpgrade, err := GetApprovedUpgrade(suite, proposeUpgradeMsg.Plan.Name)
	require.NoError(suite.T, err)
	require.Equal(suite.T, proposeUpgradeMsg.Creator, approvedUpgrade.Creator)
	require.Equal(suite.T, proposeUpgradeMsg.Plan, approvedUpgrade.Plan)

	// Get all proposed upgrades
	proposedUpgrades, err := GetProposedUpgrades(suite)
	require.NoError(suite.T, err)
	require.NotContains(suite.T, proposedUpgrades, proposedUpgrade)

	// Get all approved upgrades
	approvedUpgrades, err = GetApprovedUpgrades(suite)
	require.NoError(suite.T, err)
	require.Contains(suite.T, approvedUpgrades, *approvedUpgrade)
}

/* Error cases */

func ProposeUpgradeByNonTrustee(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	bobAccount, err := test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// register new account without Trustee role
	nonTrusteeAccountName := utils.RandString()
	nonTrusteeAccount := test_dclauth.CreateAccount(
		suite,
		nonTrusteeAccountName,
		dclauthtypes.AccountRoles{dclauthtypes.CertificationCenter},
		1,
		aliceName,
		aliceAccount,
		bobName,
		bobAccount,
		testconstants.Info,
	)
	require.NotContains(suite.T, nonTrusteeAccount.Roles, dclupgradetypes.UpgradeApprovalRole)

	// try to add proposeUpgradeMsg
	proposeUpgradeMsg := NewMsgProposeUpgrade(nonTrusteeAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, nonTrusteeAccountName, nonTrusteeAccount)
	require.Error(suite.T, err)
	require.ErrorIs(suite.T, sdkerrors.ErrUnauthorized, err)
}

func ApproveUpgradeByNonTrustee(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	bobAccount, err := test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// register new account without Trustee role
	nonTrusteeAccountName := utils.RandString()
	nonTrusteeAccount := test_dclauth.CreateAccount(
		suite,
		nonTrusteeAccountName,
		dclauthtypes.AccountRoles{dclauthtypes.CertificationCenter},
		1,
		aliceName,
		aliceAccount,
		bobName,
		bobAccount,
		testconstants.Info,
	)
	require.NotContains(suite.T, nonTrusteeAccount.Roles, dclupgradetypes.UpgradeApprovalRole)

	// propose upgrade
	proposeUpgradeMsg := NewMsgProposeUpgrade(aliceAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.NoError(suite.T, err)

	// try to approve upgrade
	approveUpgradeMsg := NewMsgApproveUpgrade(nonTrusteeAccount.Address, proposeUpgradeMsg.Plan.Name)
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{approveUpgradeMsg}, nonTrusteeAccountName, nonTrusteeAccount)
	require.Error(suite.T, err)
	require.ErrorIs(suite.T, sdkerrors.ErrUnauthorized, err)
}

func ProposeUpgradeTwice(suite *utils.TestSuite) {
	// Alice and Bob are predefined Trustees
	aliceName := testconstants.AliceAccount
	aliceKeyInfo, err := suite.Kr.Key(aliceName)
	require.NoError(suite.T, err)
	aliceAccount, err := test_dclauth.GetAccount(suite, aliceKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	bobName := testconstants.BobAccount
	bobKeyInfo, err := suite.Kr.Key(bobName)
	require.NoError(suite.T, err)
	_, err = test_dclauth.GetAccount(suite, bobKeyInfo.GetAddress())
	require.NoError(suite.T, err)

	// trustee proposes upgrade
	proposeUpgradeMsg := NewMsgProposeUpgrade(aliceAccount.Address, utils.RandString(), 100000, utils.RandString())
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.NoError(suite.T, err)

	// trustee proposes the same upgrade
	_, err = suite.BuildAndBroadcastTx([]sdk.Msg{proposeUpgradeMsg}, aliceName, aliceAccount)
	require.Error(suite.T, err)
	require.ErrorIs(suite.T, dclupgradetypes.ErrProposedUpgradeAlreadyExists, err)
}
<|endoftext|>


<commit_before>package source

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
)

type Searcher struct {
	fileset *token.FileSet
	visited map[string]*ast.File
}

func New() *Searcher {
	return &Searcher{fileset: token.NewFileSet(), visited: make(map[string]*ast.File)}
}

type NoNodeError struct {
	f string
	l int
}

func (n NoNodeError) Error() string {
	return fmt.Sprintf("could not find node at %s:%d", n.f, n.l)
}

// Returns the first node at the given file:line.
func (s *Searcher) FirstNodeAt(fname string, line int) (ast.Node, error) {
	var node ast.Node
	f, err := s.parse(fname)
	if err != nil {
		return nil, err
	}
	ast.Inspect(f, func(n ast.Node) bool {
		if n == nil {
			return true
		}
		position := s.fileset.Position(n.Pos())
		if position.Line == line {
			node = n
			return false
		}
		return true
	})
	if node == nil {
		return nil, NoNodeError{f: fname, l: line}
	}
	return node, nil
}

type Done string

func (d Done) Error() string {
	return string(d)
}

// Returns all possible lines that could be executed after the given file:line,
// within the same source file.
func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
	var found bool
	n, err := s.FirstNodeAt(fname, line)
	if err != nil {
		return lines, nil
	}
	defer func() {
		if e := recover(); e != nil {
			e = e.(Done)
			nl := make([]int, 0, len(lines))
			fnd := make(map[int]bool)
			for _, l := range lines {
				if _, ok := fnd[l]; !ok {
					fnd[l] = true
					nl = append(nl, l)
				}
			}
			lines = nl
		}
	}()

	switch x := n.(type) {
	// Check if we are at an 'if' statement.
	//
	// If we are at an 'if' statement, employ the following algorithm:
	//    * Follow all 'else if' statements, appending their line number
	//    * Follow any 'else' statement if it exists, appending the line
	//      number of the statement following the 'else'.
	//    * If there is no 'else' statement, append line of first statement
	//      following the entire 'if' block.
	case *ast.IfStmt:
		var rbrace int
		p := x.Body.List[0].Pos()
		pos := s.fileset.Position(p)
		lines = append(lines, pos.Line)

		if x.Else == nil {
			// Grab first line after entire 'if' block
			rbrace = s.fileset.Position(x.Body.Rbrace).Line
			f, err := s.parse(fname)
			if err != nil {
				return nil, err
			}
			ast.Inspect(f, func(n ast.Node) bool {
				if n == nil {
					return true
				}
				pos := s.fileset.Position(n.Pos())
				if rbrace < pos.Line {
					lines = append(lines, pos.Line)
					panic(Done("done"))
				}
				return true
			})
		} else {
			// Follow any 'else' statements
			for {
				if stmt, ok := x.Else.(*ast.IfStmt); ok {
					pos := s.fileset.Position(stmt.Pos())
					lines = append(lines, pos.Line)
					x = stmt
					continue
				}
				pos := s.fileset.Position(x.Else.Pos())
				ast.Inspect(x, func(n ast.Node) bool {
					if found {
						panic(Done("done"))
					}
					if n == nil {
						return false
					}
					p := s.fileset.Position(n.Pos())
					if pos.Line < p.Line {
						lines = append(lines, p.Line)
						found = true
						return false
					}
					return true
				})
			}
		}

	// Follow case statements.
	//
	// Append line for first statement following each 'case' condition.
	case *ast.SwitchStmt:
		ast.Inspect(x, func(n ast.Node) bool {
			if stmt, ok := n.(*ast.SwitchStmt); ok {
				ast.Inspect(stmt, func(n ast.Node) bool {
					if stmt, ok := n.(*ast.CaseClause); ok {
						p := stmt.Body[0].Pos()
						pos := s.fileset.Position(p)
						lines = append(lines, pos.Line)
						return false
					}
					return true
				})
				panic(Done("done"))
			}
			return true
		})
	// Default case - find next source line.
	//
	// We are not at a branch, employ the following algorithm:
	//    * Traverse tree, storing any loop as a parent
	//    * Find next source line after the given line
	//    * Check and see if we've passed the scope of any parent we've
	//      stored. If so, pop them off the stack. The last parent that
	//      is left get's appending to our list of lines since we could
	//      end up at the top of the loop again.
	default:
		var (
			parents              []*ast.BlockStmt
			parentBlockBeginLine int
			deferEndLine         int
		)
		f, err := s.parse(fname)
		if err != nil {
			return nil, err
		}
		ast.Inspect(f, func(n ast.Node) bool {
			if found {
				panic(Done("done"))
			}
			if n == nil {
				return true
			}

			pos := s.fileset.Position(n.Pos())
			if line < pos.Line && deferEndLine != 0 {
				p := s.fileset.Position(n.Pos())
				if deferEndLine < p.Line {
					found = true
					lines = append(lines, p.Line)
					return false
				}
			}

			if stmt, ok := n.(*ast.ForStmt); ok {
				parents = append(parents, stmt.Body)
				pos := s.fileset.Position(stmt.Pos())
				parentBlockBeginLine = pos.Line
			}

			if _, ok := n.(*ast.GenDecl); ok {
				return true
			}

			if dn, ok := n.(*ast.DeferStmt); ok && line < pos.Line {
				endpos := s.fileset.Position(dn.End())
				deferEndLine = endpos.Line
				return false
			}

			if st, ok := n.(*ast.DeclStmt); ok {
				beginpos := s.fileset.Position(st.Pos())
				endpos := s.fileset.Position(st.End())
				if beginpos.Line < endpos.Line {
					return true
				}
			}

			// Check to see if we've found the "next" line.
			if line < pos.Line {
				if _, ok := n.(*ast.BlockStmt); ok {
					return true
				}
				var (
					parent        *ast.BlockStmt
					parentEndLine int
				)
				for len(parents) > 0 {
					parent = parents[len(parents)-1]

					// Grab the line number of the right brace of the parent block.
					parentEndLine = s.fileset.Position(parent.Rbrace).Line

					// Check to see if we're still within the parents block.
					// If we are, we're done and that is our parent.
					if parentEndLine > line {
						parentBlockBeginLine = s.fileset.Position(parent.Pos()).Line
						break
					}
					// If we weren't, and there is only 1 parent, we no longer have one.
					if len(parents) == 1 {
						parent = nil
						break
					}
					// Remove that parent from the stack.
					parents = parents[0 : len(parents)-1]
				}
				if parent != nil {
					var (
						endfound   bool
						beginFound bool
						beginLine  int
					)

					ast.Inspect(f, func(n ast.Node) bool {
						if n == nil || endfound {
							return false
						}
						if _, ok := n.(*ast.BlockStmt); ok {
							return true
						}
						pos := s.fileset.Position(n.Pos())
						if parentBlockBeginLine < pos.Line && !beginFound {
							beginFound = true
							beginLine = pos.Line
							return true
						}
						if parentEndLine < pos.Line {
							if _, ok := n.(*ast.FuncDecl); !ok {
								lines = append(lines, beginLine, pos.Line)
							}
							endfound = true
							return false
						}
						return true
					})
					lines = append(lines, parentBlockBeginLine)
				}
				switch n.(type) {
				case *ast.BranchStmt, *ast.FuncDecl:
				default:
					lines = append(lines, pos.Line)
				}
				found = true
				return false
			}
			return true
		})
		if len(lines) == 0 && 0 < len(parents) {
			parent := parents[len(parents)-1]
			lbrace := s.fileset.Position(parent.Lbrace).Line
			pos := s.fileset.Position(parent.List[0].Pos())
			lines = append(lines, lbrace, pos.Line)
		}
	}
	return lines, nil
}

// Parses file named by fname, caching files it has already parsed.
func (s *Searcher) parse(fname string) (*ast.File, error) {
	if f, ok := s.visited[fname]; ok {
		return f, nil
	}
	f, err := parser.ParseFile(s.fileset, fname, nil, 0)
	if err != nil {
		return nil, err
	}
	s.visited[fname] = f
	return f, nil
}
<commit_msg>Remove unused recovered error in if-else parsing, fixes #137<commit_after>package source

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
)

type Searcher struct {
	fileset *token.FileSet
	visited map[string]*ast.File
}

func New() *Searcher {
	return &Searcher{fileset: token.NewFileSet(), visited: make(map[string]*ast.File)}
}

type NoNodeError struct {
	f string
	l int
}

func (n NoNodeError) Error() string {
	return fmt.Sprintf("could not find node at %s:%d", n.f, n.l)
}

// Returns the first node at the given file:line.
func (s *Searcher) FirstNodeAt(fname string, line int) (ast.Node, error) {
	var node ast.Node
	f, err := s.parse(fname)
	if err != nil {
		return nil, err
	}
	ast.Inspect(f, func(n ast.Node) bool {
		if n == nil {
			return true
		}
		position := s.fileset.Position(n.Pos())
		if position.Line == line {
			node = n
			return false
		}
		return true
	})
	if node == nil {
		return nil, NoNodeError{f: fname, l: line}
	}
	return node, nil
}

type Done string

func (d Done) Error() string {
	return string(d)
}

// Returns all possible lines that could be executed after the given file:line,
// within the same source file.
func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
	var found bool
	n, err := s.FirstNodeAt(fname, line)
	if err != nil {
		return lines, nil
	}
	defer func() {
		if e := recover(); e != nil {
			nl := make([]int, 0, len(lines))
			fnd := make(map[int]bool)
			for _, l := range lines {
				if _, ok := fnd[l]; !ok {
					fnd[l] = true
					nl = append(nl, l)
				}
			}
			lines = nl
		}
	}()

	switch x := n.(type) {
	// Check if we are at an 'if' statement.
	//
	// If we are at an 'if' statement, employ the following algorithm:
	//    * Follow all 'else if' statements, appending their line number
	//    * Follow any 'else' statement if it exists, appending the line
	//      number of the statement following the 'else'.
	//    * If there is no 'else' statement, append line of first statement
	//      following the entire 'if' block.
	case *ast.IfStmt:
		var rbrace int
		p := x.Body.List[0].Pos()
		pos := s.fileset.Position(p)
		lines = append(lines, pos.Line)

		if x.Else == nil {
			// Grab first line after entire 'if' block
			rbrace = s.fileset.Position(x.Body.Rbrace).Line
			f, err := s.parse(fname)
			if err != nil {
				return nil, err
			}
			ast.Inspect(f, func(n ast.Node) bool {
				if n == nil {
					return true
				}
				pos := s.fileset.Position(n.Pos())
				if rbrace < pos.Line {
					lines = append(lines, pos.Line)
					panic(Done("done"))
				}
				return true
			})
		} else {
			// Follow any 'else' statements
			for {
				if stmt, ok := x.Else.(*ast.IfStmt); ok {
					pos := s.fileset.Position(stmt.Pos())
					lines = append(lines, pos.Line)
					x = stmt
					continue
				}
				pos := s.fileset.Position(x.Else.Pos())
				ast.Inspect(x, func(n ast.Node) bool {
					if found {
						panic(Done("done"))
					}
					if n == nil {
						return false
					}
					p := s.fileset.Position(n.Pos())
					if pos.Line < p.Line {
						lines = append(lines, p.Line)
						found = true
						return false
					}
					return true
				})
			}
		}

	// Follow case statements.
	//
	// Append line for first statement following each 'case' condition.
	case *ast.SwitchStmt:
		ast.Inspect(x, func(n ast.Node) bool {
			if stmt, ok := n.(*ast.SwitchStmt); ok {
				ast.Inspect(stmt, func(n ast.Node) bool {
					if stmt, ok := n.(*ast.CaseClause); ok {
						p := stmt.Body[0].Pos()
						pos := s.fileset.Position(p)
						lines = append(lines, pos.Line)
						return false
					}
					return true
				})
				panic(Done("done"))
			}
			return true
		})
	// Default case - find next source line.
	//
	// We are not at a branch, employ the following algorithm:
	//    * Traverse tree, storing any loop as a parent
	//    * Find next source line after the given line
	//    * Check and see if we've passed the scope of any parent we've
	//      stored. If so, pop them off the stack. The last parent that
	//      is left get's appending to our list of lines since we could
	//      end up at the top of the loop again.
	default:
		var (
			parents              []*ast.BlockStmt
			parentBlockBeginLine int
			deferEndLine         int
		)
		f, err := s.parse(fname)
		if err != nil {
			return nil, err
		}
		ast.Inspect(f, func(n ast.Node) bool {
			if found {
				panic(Done("done"))
			}
			if n == nil {
				return true
			}

			pos := s.fileset.Position(n.Pos())
			if line < pos.Line && deferEndLine != 0 {
				p := s.fileset.Position(n.Pos())
				if deferEndLine < p.Line {
					found = true
					lines = append(lines, p.Line)
					return false
				}
			}

			if stmt, ok := n.(*ast.ForStmt); ok {
				parents = append(parents, stmt.Body)
				pos := s.fileset.Position(stmt.Pos())
				parentBlockBeginLine = pos.Line
			}

			if _, ok := n.(*ast.GenDecl); ok {
				return true
			}

			if dn, ok := n.(*ast.DeferStmt); ok && line < pos.Line {
				endpos := s.fileset.Position(dn.End())
				deferEndLine = endpos.Line
				return false
			}

			if st, ok := n.(*ast.DeclStmt); ok {
				beginpos := s.fileset.Position(st.Pos())
				endpos := s.fileset.Position(st.End())
				if beginpos.Line < endpos.Line {
					return true
				}
			}

			// Check to see if we've found the "next" line.
			if line < pos.Line {
				if _, ok := n.(*ast.BlockStmt); ok {
					return true
				}
				var (
					parent        *ast.BlockStmt
					parentEndLine int
				)
				for len(parents) > 0 {
					parent = parents[len(parents)-1]

					// Grab the line number of the right brace of the parent block.
					parentEndLine = s.fileset.Position(parent.Rbrace).Line

					// Check to see if we're still within the parents block.
					// If we are, we're done and that is our parent.
					if parentEndLine > line {
						parentBlockBeginLine = s.fileset.Position(parent.Pos()).Line
						break
					}
					// If we weren't, and there is only 1 parent, we no longer have one.
					if len(parents) == 1 {
						parent = nil
						break
					}
					// Remove that parent from the stack.
					parents = parents[0 : len(parents)-1]
				}
				if parent != nil {
					var (
						endfound   bool
						beginFound bool
						beginLine  int
					)

					ast.Inspect(f, func(n ast.Node) bool {
						if n == nil || endfound {
							return false
						}
						if _, ok := n.(*ast.BlockStmt); ok {
							return true
						}
						pos := s.fileset.Position(n.Pos())
						if parentBlockBeginLine < pos.Line && !beginFound {
							beginFound = true
							beginLine = pos.Line
							return true
						}
						if parentEndLine < pos.Line {
							if _, ok := n.(*ast.FuncDecl); !ok {
								lines = append(lines, beginLine, pos.Line)
							}
							endfound = true
							return false
						}
						return true
					})
					lines = append(lines, parentBlockBeginLine)
				}
				switch n.(type) {
				case *ast.BranchStmt, *ast.FuncDecl:
				default:
					lines = append(lines, pos.Line)
				}
				found = true
				return false
			}
			return true
		})
		if len(lines) == 0 && 0 < len(parents) {
			parent := parents[len(parents)-1]
			lbrace := s.fileset.Position(parent.Lbrace).Line
			pos := s.fileset.Position(parent.List[0].Pos())
			lines = append(lines, lbrace, pos.Line)
		}
	}
	return lines, nil
}

// Parses file named by fname, caching files it has already parsed.
func (s *Searcher) parse(fname string) (*ast.File, error) {
	if f, ok := s.visited[fname]; ok {
		return f, nil
	}
	f, err := parser.ParseFile(s.fileset, fname, nil, 0)
	if err != nil {
		return nil, err
	}
	s.visited[fname] = f
	return f, nil
}
<|endoftext|>


<commit_before>package main

// Kode for Oppgave 3c
// Bruk strengen fra filen treasure.txt som in-data for denne funksjonen

func PrintTreasureUTF8(treasure_string string) []byte {
	return []byte{"%s\n", "\x48\x65\x6e\x72\x69\x6b\x20\x41\x72\x6e\x6f\x6c\x64" +
  "\x20\x57\x65\x72\x67\x65\x6c\x61\x6e\x64\x20\x28\x66\xf8\x64\x74\x20\x31\x37" +
  "\x2e\x20\x6a\x75\x6e\x69\x20\x31\x38\x30\x38\x2c\x20\x64\xf8\x64\x20\x31\x32" +
  "\x2e\x20\x6a\x75\x6c\x69\x20\x31\x38\x34\x35\x29\x0a\x56\x69\x20\x65\x72\x65\x20" +
  "\x65\x6e\x20\x6e\x61\x73\x6a\x6f\x6e\x20\x76\x69\x20\x6d\x65\x64\x2c\x0a\x20\x76" +
  "\x69\x20\x73\x6d\xe5\x20\x65\x6e\x20\x61\x6c\x65\x6e\x20\x6c\x61\x6e\x67\x65\x2c" +
  "\x0a\x20\x65\x74\x20\x66\x65\x64\x72\x65\x6c\x61\x6e\x64\x20\x76\x69\x20\x66\x72" +
  "\x79\x64\x65\x73\x20\x76\x65\x64\x2c\x0a\x20\x6f\x67\x20\x76\x69\x2c\x20\x76\x69" +
  "\x20\x65\x72\x65\x20\x6d\x61\x6e\x67\x65\x2e\x0a\x20\x56\xe5\x72\x74\x20\x68\x6a" +
  "\x65\x72\x74\x65\x20\x76\x65\x74\x2c\x20\x76\xe5\x72\x74\x20\xf8\x79\x65\x20\x73" +
  "\x65\x72\x0a\x20\x68\x76\x6f\x72\x20\x67\x6f\x64\x74\x20\x6f\x67\x20\x76\x61\x6b" +
  "\x6b\x65\x72\x74\x20\x4e\x6f\x72\x67\x65\x20\x65\x72\x2c\x0a\x20\x76\xe5\x72\x20" +
  "\x74\x75\x6e\x67\x65\x20\x6b\x61\x6e\x20\x65\x6e\x20\x73\x61\x6e\x67\x20\x62\x6c" +
  "\x61\x6e\x74\x20\x66\x6c\x65\x72\x0a\x20\x61\x76\x20\x4e\x6f\x72\x67\x65\x73\x20" +
  "\xe6\x72\x65\x73\x2d\x73\x61\x6e\x67\x65\x2e\x0a\x0a\x20\x4d\x65\x72\x20\x67\x72" +
  "\xf8\x6e\x74\x20\x65\x72\x20\x67\x72\x65\x73\x73\x65\x74\x20\x69\x6e\x67\x65\x6e" +
  "\x73\x74\x65\x64\x73\x2c\x0a\x20\x6d\x65\x72\x20\x66\x75\x6c\x6c\x74\x20\x61\x76" +
  "\x20\x62\x6c\x6f\x6d\x73\x74\x65\x72\x20\x76\x65\x76\x65\x74\x0a\x20\x65\x6e\x6e" +
  "\x20\x69\x20\x64\x65\x74\x20\x6c\x61\x6e\x64\x20\x68\x76\x6f\x72\x20\x6a\x65\x67" +
  "\x20\x74\x69\x6c\x66\x72\x65\x64\x73\x0a\x20\x6d\x65\x64\x20\x66\x61\x72\x20\x6f" +
  "\x67\x20\x6d\x6f\x72\x20\x68\x61\x72\x20\x6c\x65\x76\x65\x74\x2e\x0a\x20\x4a\x65" +
  "\x67\x20\x76\x69\x6c\x20\x64\x65\x74\x20\x65\x6c\x73\x6b\x65\x20\x74\x69\x6c\x20" +
  "\x6d\x69\x6e\x20\x64\xf8\x64\x2c\x0a\x20\x65\x69\x20\x62\x79\x74\x74\x65\x20\x64" +
  "\x65\x74\x20\x68\x76\x6f\x72\x20\x6a\x65\x67\x20\x65\x72\x20\x66\xf8\x64\x64\x2c" +
  "\x0a\x20\x6f\x6d\x20\x6d\x61\x6e\x20\x65\x74\x20\x70\x61\x72\x61\x64\x69\x73\x20" +
  "\x6d\x65\x67\x20\x62\xf8\x64\x0a\x20\x61\x76\x20\x70\x61\x6c\x6d\x65\x72\x20\x6f" +
  "\x76\x65\x72\x73\x76\x65\x76\x65\x74\x2e\x0a"}
}
<commit_msg>konstant 3c<commit_after>package main

import "fmt"

const sang = "\x48\x65\x6e\x72\x69\x6b\x20\x41\x72\x6e\x6f\x6c\x64" +
"\x20\x57\x65\x72\x67\x65\x6c\x61\x6e\x64\x20\x28\x66\xf8\x64\x74\x20\x31\x37" +
"\x2e\x20\x6a\x75\x6e\x69\x20\x31\x38\x30\x38\x2c\x20\x64\xf8\x64\x20\x31\x32" +
"\x2e\x20\x6a\x75\x6c\x69\x20\x31\x38\x34\x35\x29\x0a\x56\x69\x20\x65\x72\x65\x20" +
"\x65\x6e\x20\x6e\x61\x73\x6a\x6f\x6e\x20\x76\x69\x20\x6d\x65\x64\x2c\x0a\x20\x76" +
"\x69\x20\x73\x6d\xe5\x20\x65\x6e\x20\x61\x6c\x65\x6e\x20\x6c\x61\x6e\x67\x65\x2c" +
"\x0a\x20\x65\x74\x20\x66\x65\x64\x72\x65\x6c\x61\x6e\x64\x20\x76\x69\x20\x66\x72" +
"\x79\x64\x65\x73\x20\x76\x65\x64\x2c\x0a\x20\x6f\x67\x20\x76\x69\x2c\x20\x76\x69" +
"\x20\x65\x72\x65\x20\x6d\x61\x6e\x67\x65\x2e\x0a\x20\x56\xe5\x72\x74\x20\x68\x6a" +
"\x65\x72\x74\x65\x20\x76\x65\x74\x2c\x20\x76\xe5\x72\x74\x20\xf8\x79\x65\x20\x73" +
"\x65\x72\x0a\x20\x68\x76\x6f\x72\x20\x67\x6f\x64\x74\x20\x6f\x67\x20\x76\x61\x6b" +
"\x6b\x65\x72\x74\x20\x4e\x6f\x72\x67\x65\x20\x65\x72\x2c\x0a\x20\x76\xe5\x72\x20" +
"\x74\x75\x6e\x67\x65\x20\x6b\x61\x6e\x20\x65\x6e\x20\x73\x61\x6e\x67\x20\x62\x6c" +
"\x61\x6e\x74\x20\x66\x6c\x65\x72\x0a\x20\x61\x76\x20\x4e\x6f\x72\x67\x65\x73\x20" +
"\xe6\x72\x65\x73\x2d\x73\x61\x6e\x67\x65\x2e\x0a\x0a\x20\x4d\x65\x72\x20\x67\x72" +
"\xf8\x6e\x74\x20\x65\x72\x20\x67\x72\x65\x73\x73\x65\x74\x20\x69\x6e\x67\x65\x6e" +
"\x73\x74\x65\x64\x73\x2c\x0a\x20\x6d\x65\x72\x20\x66\x75\x6c\x6c\x74\x20\x61\x76" +
"\x20\x62\x6c\x6f\x6d\x73\x74\x65\x72\x20\x76\x65\x76\x65\x74\x0a\x20\x65\x6e\x6e" +
"\x20\x69\x20\x64\x65\x74\x20\x6c\x61\x6e\x64\x20\x68\x76\x6f\x72\x20\x6a\x65\x67" +
"\x20\x74\x69\x6c\x66\x72\x65\x64\x73\x0a\x20\x6d\x65\x64\x20\x66\x61\x72\x20\x6f" +
"\x67\x20\x6d\x6f\x72\x20\x68\x61\x72\x20\x6c\x65\x76\x65\x74\x2e\x0a\x20\x4a\x65" +
"\x67\x20\x76\x69\x6c\x20\x64\x65\x74\x20\x65\x6c\x73\x6b\x65\x20\x74\x69\x6c\x20" +
"\x6d\x69\x6e\x20\x64\xf8\x64\x2c\x0a\x20\x65\x69\x20\x62\x79\x74\x74\x65\x20\x64" +
"\x65\x74\x20\x68\x76\x6f\x72\x20\x6a\x65\x67\x20\x65\x72\x20\x66\xf8\x64\x64\x2c" +
"\x0a\x20\x6f\x6d\x20\x6d\x61\x6e\x20\x65\x74\x20\x70\x61\x72\x61\x64\x69\x73\x20" +
"\x6d\x65\x67\x20\x62\xf8\x64\x0a\x20\x61\x76\x20\x70\x61\x6c\x6d\x65\x72\x20\x6f" +
"\x76\x65\x72\x73\x76\x65\x76\x65\x74\x2e\x0a"

func main(){
	fmt.Printf("%s", sang)
}
<|endoftext|>


<commit_before>// Copyright 2016 Gary Burd
//
// 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 nvim

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"sync"
	"time"

	"github.com/neovim/go-client/msgpack"
	"github.com/neovim/go-client/msgpack/rpc"
)

//go:generate go run apitool.go -generate apiimp.go

// Nvim represents a remote instance of Nvim. It is safe to call Nvim methods
// concurrently.
type Nvim struct {
	ep *rpc.Endpoint

	mu        sync.Mutex
	channelID int
}

// Serve serves incoming requests. Serve blocks until Nvim disconnects or there
// is an error.
func (v *Nvim) Serve() error {
	return v.ep.Serve()
}

// Close releases the resources used the client.
func (v *Nvim) Close() error {
	return v.ep.Close()
}

// New creates an Nvim client. When connecting to Nvim over stdio, use stdin as
// r and stdout as w and c, When connecting to Nvim over a network connection,
// use the connection for r, w and c.
//
// The application must call Serve() to handle RPC requests and responses.
//
//  :help rpc-connecting
func New(r io.Reader, w io.Writer, c io.Closer, logf func(string, ...interface{})) (*Nvim, error) {
	ep, err := rpc.NewEndpoint(r, w, c, rpc.WithLogf(logf), withExtensions())
	if err != nil {
		return nil, err
	}
	return &Nvim{ep: ep}, nil
}

// EmbedOptions specifies options for starting an embedded instance of Nvim.
type EmbedOptions struct {
	// Args specifies the command line arguments. Do not include the program
	// name (the first argument) or the --embed option.
	Args []string

	// Dir specifies the working directory of the command. The working
	// directory in the current process is used if Dir is "".
	Dir string

	// Env specifies the environment of the Nvim process. The current process
	// environment is used if Env is nil.
	Env []string

	// Path is the path of the command to run. If Path = "", then
	// StartEmbeddedNvim searches for "nvim" on $PATH.
	Path string

	Logf func(string, ...interface{})
}

type embedCloser struct {
	w io.WriteCloser
	p *os.Process
}

func (c *embedCloser) Close() error {
	err := c.w.Close()

	t := time.AfterFunc(5*time.Second, func() { c.p.Kill() })
	defer t.Stop()

	state, e := c.p.Wait()
	if e != nil {
		err = e
	}

	if err != nil || !state.Success() {
		err = fmt.Errorf("%s", state)
	}

	return err
}

// NewEmbedded starts an embedded instance of Nvim using the specified options.
func NewEmbedded(options *EmbedOptions) (*Nvim, error) {
	var closeOnExit []io.Closer
	defer func() {
		for _, c := range closeOnExit {
			c.Close()
		}
	}()
	if options == nil {
		options = &EmbedOptions{}
	}

	path := options.Path
	if path == "" {
		var err error
		path, err = exec.LookPath("nvim")
		if err != nil {
			return nil, err
		}
	}

	outr, outw, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	closeOnExit = append(closeOnExit, outr, outw)

	inr, inw, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	closeOnExit = append(closeOnExit, inr, inw)

	c := &embedCloser{
		w: inw,
	}

	v, err := New(outr, inw, c, options.Logf)
	if err != nil {
		return nil, err
	}
	closeOnExit = append(closeOnExit, v.ep)

	c.p, err = os.StartProcess(path,
		append([]string{path, "--embed"}, options.Args...),
		&os.ProcAttr{
			Env:   options.Env,
			Files: []*os.File{inr, outw},
		})
	if err != nil {
		return nil, err
	}

	outw.Close()
	inr.Close()
	closeOnExit = nil
	return v, nil
}

// RegisterHandler registers fn as a MessagePack RPC handler for the named
// method. The function signature for fn is one of
//
//  func([v *nvim.Nvim,] {args}) ({resultType}, error)
//  func([v *nvim.Nvim,] {args}) error
//  func([v *nvim.Nvim,] {args})
//
// where {args} is zero or more arguments and {resultType} is the type of a
// return value. Call the handler from Nvim using the rpcnotify and rpcrequest
// functions:
//
//  :help rpcrequest()
//  :help rpcnotify()
//
// Plugin applications should use the Handler* methods in the ./plugin package
// to register handlers instead of this method.
func (v *Nvim) RegisterHandler(method string, fn interface{}) error {
	var args []interface{}
	t := reflect.TypeOf(fn)
	if t.Kind() == reflect.Func && t.NumIn() > 0 && t.In(0) == reflect.TypeOf(v) {
		args = append(args, v)
	}
	return v.ep.Register(method, fn, args...)
}

// ChannelID returns Nvim's channel id for this client.
func (v *Nvim) ChannelID() int {
	v.mu.Lock()
	defer v.mu.Unlock()
	if v.channelID != 0 {
		return v.channelID
	}
	var info struct {
		ChannelID int `msgpack:",array"`
		Info      interface{}
	}
	if err := v.ep.Call("nvim_get_api_info", &info); err != nil {
		// TODO: log error and exit process?
	}
	v.channelID = info.ChannelID
	return v.channelID
}

func (v *Nvim) call(sm string, result interface{}, args ...interface{}) error {
	return fixError(sm, v.ep.Call(sm, result, args...))
}

// NewBatch creates a new batch.
func (v *Nvim) NewBatch() *Batch {
	b := &Batch{ep: v.ep}
	b.enc = msgpack.NewEncoder(&b.buf)
	return b
}

// Batch collects API function calls and executes them atomically.
//
// The function calls in the batch are executed without processing requests
// from other clients, redrawing or allowing user interaction in between.
// Functions that could fire autocommands or do event processing still might do
// so. For instance invoking the :sleep command might call timer callbacks.
//
// Call the Execute() method to execute the commands in the batch. Result
// parameters in the API function calls are set in the call to Execute.  If an
// API function call fails, all results proceeding the call are set and a
// *BatchError is returned.
//
// A Batch does not support concurrent calls by the application.
type Batch struct {
	ep      *rpc.Endpoint
	buf     bytes.Buffer
	enc     *msgpack.Encoder
	sms     []string
	results []interface{}
	err     error
}

// Execute executes the API function calls in the batch.
func (b *Batch) Execute() error {
	defer func() {
		b.buf.Reset()
		b.sms = b.sms[:0]
		b.results = b.results[:0]
		b.err = nil
	}()

	if b.err != nil {
		return b.err
	}

	result := struct {
		Results []interface{} `msgpack:",array"`
		Error   *struct {
			Index   int `msgpack:",array"`
			Type    int
			Message string
		}
	}{
		b.results,
		nil,
	}

	err := b.ep.Call("nvim_call_atomic", &result, &batchArg{n: len(b.sms), p: b.buf.Bytes()})
	if err != nil {
		return err
	}

	e := result.Error
	if e == nil {
		return nil
	}

	if e.Index < 0 || e.Index >= len(b.sms) ||
		(e.Type != exceptionError && e.Type != validationError) {
		return fmt.Errorf("nvim:nvim_call_atomic %d %d %s", e.Index, e.Type, e.Message)
	}
	errorType := "exception"
	if e.Type == validationError {
		errorType = "validation"
	}
	return &BatchError{
		Index: e.Index,
		Err:   fmt.Errorf("nvim:%s %s: %s", b.sms[e.Index], errorType, e.Message),
	}
}

var emptyArgs = []interface{}{}

func (b *Batch) call(sm string, result interface{}, args ...interface{}) {
	if b.err != nil {
		return
	}
	if args == nil {
		args = emptyArgs
	}
	b.sms = append(b.sms, sm)
	b.results = append(b.results, result)
	b.enc.PackArrayLen(2)
	b.enc.PackString(sm)
	b.err = b.enc.Encode(args)
}

type batchArg struct {
	n int
	p []byte
}

func (a *batchArg) MarshalMsgPack(enc *msgpack.Encoder) error {
	enc.PackArrayLen(a.n)
	return enc.PackRaw(a.p)
}

// BatchError represents an error from a API function call in a Batch.
type BatchError struct {
	// Index is a zero-based index of the function call which resulted in the
	// error.
	Index int

	// Err is the error.
	Err error
}

func (e *BatchError) Error() string {
	return e.Err.Error()
}

func fixError(sm string, err error) error {
	if e, ok := err.(rpc.Error); ok {
		if a, ok := e.Value.([]interface{}); ok && len(a) == 2 {
			switch a[0] {
			case int64(exceptionError), uint64(exceptionError):
				return fmt.Errorf("nvim:%s exception: %v", sm, a[1])
			case int64(validationError), uint64(validationError):
				return fmt.Errorf("nvim:%s validation: %v", sm, a[1])
			}
		}
	}
	return err
}

// ErrorList is a list of errors.
type ErrorList []error

func (el ErrorList) Error() string {
	return el[0].Error()
}

// Call calls a vimscript function.
func (v *Nvim) Call(fname string, result interface{}, args ...interface{}) error {
	if args == nil {
		args = []interface{}{}
	}
	return v.call("nvim_call_function", result, fname, args)
}

// Call calls a vimscript function.
func (b *Batch) Call(fname string, result interface{}, args ...interface{}) {
	if args == nil {
		args = []interface{}{}
	}
	b.call("nvim_call_function", result, fname, args)
}

// ExecuteLua executes a Lua block.
func (v *Nvim) ExecuteLua(code string, result interface{}, args ...interface{}) error {
	if args == nil {
		args = []interface{}{}
	}
	return v.call("nvim_execute_lua", result, code, args)
}

// ExecuteLua executes a Lua block.
func (b *Batch) ExecuteLua(code string, result interface{}, args ...interface{}) {
	if args == nil {
		args = []interface{}{}
	}
	b.call("nvim_execute_lua", result, code, args)
}

// decodeExt decodes a MsgPack encoded number to go int value.
func decodeExt(p []byte) (int, error) {
	switch {
	case len(p) == 1 && p[0] <= 0x7f:
		return int(p[0]), nil
	case len(p) == 2 && p[0] == 0xcc:
		return int(p[1]), nil
	case len(p) == 3 && p[0] == 0xcd:
		return int(uint16(p[2]) | uint16(p[1])<<8), nil
	case len(p) == 5 && p[0] == 0xce:
		return int(uint32(p[4]) | uint32(p[3])<<8 | uint32(p[2])<<16 | uint32(p[1])<<24), nil
	case len(p) == 2 && p[0] == 0xd0:
		return int(int8(p[1])), nil
	case len(p) == 3 && p[0] == 0xd1:
		return int(int16(uint16(p[2]) | uint16(p[1])<<8)), nil
	case len(p) == 5 && p[0] == 0xd2:
		return int(int32(uint32(p[4]) | uint32(p[3])<<8 | uint32(p[2])<<16 | uint32(p[1])<<24)), nil
	case len(p) == 1 && p[0] >= 0xe0:
		return int(int8(p[0])), nil
	default:
		return 0, fmt.Errorf("go-client/nvim: error decoding extension bytes %x", p)
	}
}

// encodeExt encodes n to MsgPack format.
func encodeExt(n int) []byte {
	return []byte{0xd2, byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}
}

func unmarshalExt(dec *msgpack.Decoder, id int, v interface{}) (int, error) {
	if dec.Type() != msgpack.Extension || dec.Extension() != id {
		err := &msgpack.DecodeConvertError{
			SrcType:  dec.Type(),
			DestType: reflect.TypeOf(v).Elem(),
		}
		dec.Skip()
		return 0, err
	}
	return decodeExt(dec.BytesNoCopy())
}

type Mode struct {
	// Mode is the current mode.
	Mode string `msgpack:"mode"`

	// Blocking is true if Nvim is waiting for input.
	Blocking bool `msgpack:"blocking"`
}
<commit_msg>Use os/exec to run embedded nvim process<commit_after>// Copyright 2016 Gary Burd
//
// 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 nvim

import (
	"bytes"
	"fmt"
	"io"
	"os/exec"
	"reflect"
	"sync"
	"time"

	"github.com/neovim/go-client/msgpack"
	"github.com/neovim/go-client/msgpack/rpc"
)

//go:generate go run apitool.go -generate apiimp.go

// Nvim represents a remote instance of Nvim. It is safe to call Nvim methods
// concurrently.
type Nvim struct {
	ep *rpc.Endpoint

	channelIDMu sync.Mutex
	channelID   int

	// cmd is the child process, if any.
	cmd *exec.Cmd

	// readMu prevents concurrent calls to read on the child process stdout pipe and
	// calls to cmd.Wait().
	readMu sync.Mutex
}

// Serve serves incoming requests. Serve blocks until Nvim disconnects or there
// is an error.
func (v *Nvim) Serve() error {
	v.readMu.Lock()
	defer v.readMu.Unlock()
	return v.ep.Serve()
}

// Close releases the resources used the client.
func (v *Nvim) Close() error {

	if v.cmd != nil && v.cmd.Process != nil {
		// The child process should exit cleanly on call to v.ep.Close(). Kill
		// the process if it does not exit as expected.
		t := time.AfterFunc(5*time.Second, func() { v.cmd.Process.Kill() })
		defer t.Stop()
	}

	err := v.ep.Close()

	if v.cmd != nil {
		v.readMu.Lock()
		defer v.readMu.Unlock()

		errWait := v.cmd.Wait()
		if err == nil {
			err = errWait
		}
	}

	return err
}

// New creates an Nvim client. When connecting to Nvim over stdio, use stdin as
// r and stdout as w and c, When connecting to Nvim over a network connection,
// use the connection for r, w and c.
//
// The application must call Serve() to handle RPC requests and responses.
//
//  :help rpc-connecting
func New(r io.Reader, w io.Writer, c io.Closer, logf func(string, ...interface{})) (*Nvim, error) {
	ep, err := rpc.NewEndpoint(r, w, c, rpc.WithLogf(logf), withExtensions())
	if err != nil {
		return nil, err
	}
	return &Nvim{ep: ep}, nil
}

// EmbedOptions specifies options for starting an embedded instance of Nvim.
type EmbedOptions struct {
	// Args specifies the command line arguments. Do not include the program
	// name (the first argument) or the --embed option.
	Args []string

	// Dir specifies the working directory of the command. The working
	// directory in the current process is used if Dir is "".
	Dir string

	// Env specifies the environment of the Nvim process. The current process
	// environment is used if Env is nil.
	Env []string

	// Path is the path of the command to run. If Path = "", then
	// StartEmbeddedNvim searches for "nvim" on $PATH.
	Path string

	Logf func(string, ...interface{})
}

// NewEmbedded starts an embedded instance of Nvim using the specified options.
func NewEmbedded(options *EmbedOptions) (*Nvim, error) {
	if options == nil {
		options = &EmbedOptions{}
	}

	path := options.Path
	if path == "" {
		path = "nvim"
	}

	cmd := exec.Command(path, append([]string{"--embed"}, options.Args...)...)
	cmd.Env = options.Env
	cmd.Dir = options.Dir

	inw, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	outr, err := cmd.StdoutPipe()
	if err != nil {
		inw.Close()
		return nil, err
	}

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	v, _ := New(outr, inw, inw, options.Logf)
	return v, nil
}

// RegisterHandler registers fn as a MessagePack RPC handler for the named
// method. The function signature for fn is one of
//
//  func([v *nvim.Nvim,] {args}) ({resultType}, error)
//  func([v *nvim.Nvim,] {args}) error
//  func([v *nvim.Nvim,] {args})
//
// where {args} is zero or more arguments and {resultType} is the type of a
// return value. Call the handler from Nvim using the rpcnotify and rpcrequest
// functions:
//
//  :help rpcrequest()
//  :help rpcnotify()
//
// Plugin applications should use the Handler* methods in the ./plugin package
// to register handlers instead of this method.
func (v *Nvim) RegisterHandler(method string, fn interface{}) error {
	var args []interface{}
	t := reflect.TypeOf(fn)
	if t.Kind() == reflect.Func && t.NumIn() > 0 && t.In(0) == reflect.TypeOf(v) {
		args = append(args, v)
	}
	return v.ep.Register(method, fn, args...)
}

// ChannelID returns Nvim's channel id for this client.
func (v *Nvim) ChannelID() int {
	v.channelIDMu.Lock()
	defer v.channelIDMu.Unlock()
	if v.channelID != 0 {
		return v.channelID
	}
	var info struct {
		ChannelID int `msgpack:",array"`
		Info      interface{}
	}
	if err := v.ep.Call("nvim_get_api_info", &info); err != nil {
		// TODO: log error and exit process?
	}
	v.channelID = info.ChannelID
	return v.channelID
}

func (v *Nvim) call(sm string, result interface{}, args ...interface{}) error {
	return fixError(sm, v.ep.Call(sm, result, args...))
}

// NewBatch creates a new batch.
func (v *Nvim) NewBatch() *Batch {
	b := &Batch{ep: v.ep}
	b.enc = msgpack.NewEncoder(&b.buf)
	return b
}

// Batch collects API function calls and executes them atomically.
//
// The function calls in the batch are executed without processing requests
// from other clients, redrawing or allowing user interaction in between.
// Functions that could fire autocommands or do event processing still might do
// so. For instance invoking the :sleep command might call timer callbacks.
//
// Call the Execute() method to execute the commands in the batch. Result
// parameters in the API function calls are set in the call to Execute.  If an
// API function call fails, all results proceeding the call are set and a
// *BatchError is returned.
//
// A Batch does not support concurrent calls by the application.
type Batch struct {
	ep      *rpc.Endpoint
	buf     bytes.Buffer
	enc     *msgpack.Encoder
	sms     []string
	results []interface{}
	err     error
}

// Execute executes the API function calls in the batch.
func (b *Batch) Execute() error {
	defer func() {
		b.buf.Reset()
		b.sms = b.sms[:0]
		b.results = b.results[:0]
		b.err = nil
	}()

	if b.err != nil {
		return b.err
	}

	result := struct {
		Results []interface{} `msgpack:",array"`
		Error   *struct {
			Index   int `msgpack:",array"`
			Type    int
			Message string
		}
	}{
		b.results,
		nil,
	}

	err := b.ep.Call("nvim_call_atomic", &result, &batchArg{n: len(b.sms), p: b.buf.Bytes()})
	if err != nil {
		return err
	}

	e := result.Error
	if e == nil {
		return nil
	}

	if e.Index < 0 || e.Index >= len(b.sms) ||
		(e.Type != exceptionError && e.Type != validationError) {
		return fmt.Errorf("nvim:nvim_call_atomic %d %d %s", e.Index, e.Type, e.Message)
	}
	errorType := "exception"
	if e.Type == validationError {
		errorType = "validation"
	}
	return &BatchError{
		Index: e.Index,
		Err:   fmt.Errorf("nvim:%s %s: %s", b.sms[e.Index], errorType, e.Message),
	}
}

var emptyArgs = []interface{}{}

func (b *Batch) call(sm string, result interface{}, args ...interface{}) {
	if b.err != nil {
		return
	}
	if args == nil {
		args = emptyArgs
	}
	b.sms = append(b.sms, sm)
	b.results = append(b.results, result)
	b.enc.PackArrayLen(2)
	b.enc.PackString(sm)
	b.err = b.enc.Encode(args)
}

type batchArg struct {
	n int
	p []byte
}

func (a *batchArg) MarshalMsgPack(enc *msgpack.Encoder) error {
	enc.PackArrayLen(a.n)
	return enc.PackRaw(a.p)
}

// BatchError represents an error from a API function call in a Batch.
type BatchError struct {
	// Index is a zero-based index of the function call which resulted in the
	// error.
	Index int

	// Err is the error.
	Err error
}

func (e *BatchError) Error() string {
	return e.Err.Error()
}

func fixError(sm string, err error) error {
	if e, ok := err.(rpc.Error); ok {
		if a, ok := e.Value.([]interface{}); ok && len(a) == 2 {
			switch a[0] {
			case int64(exceptionError), uint64(exceptionError):
				return fmt.Errorf("nvim:%s exception: %v", sm, a[1])
			case int64(validationError), uint64(validationError):
				return fmt.Errorf("nvim:%s validation: %v", sm, a[1])
			}
		}
	}
	return err
}

// ErrorList is a list of errors.
type ErrorList []error

func (el ErrorList) Error() string {
	return el[0].Error()
}

// Call calls a vimscript function.
func (v *Nvim) Call(fname string, result interface{}, args ...interface{}) error {
	if args == nil {
		args = []interface{}{}
	}
	return v.call("nvim_call_function", result, fname, args)
}

// Call calls a vimscript function.
func (b *Batch) Call(fname string, result interface{}, args ...interface{}) {
	if args == nil {
		args = []interface{}{}
	}
	b.call("nvim_call_function", result, fname, args)
}

// ExecuteLua executes a Lua block.
func (v *Nvim) ExecuteLua(code string, result interface{}, args ...interface{}) error {
	if args == nil {
		args = []interface{}{}
	}
	return v.call("nvim_execute_lua", result, code, args)
}

// ExecuteLua executes a Lua block.
func (b *Batch) ExecuteLua(code string, result interface{}, args ...interface{}) {
	if args == nil {
		args = []interface{}{}
	}
	b.call("nvim_execute_lua", result, code, args)
}

// decodeExt decodes a MsgPack encoded number to go int value.
func decodeExt(p []byte) (int, error) {
	switch {
	case len(p) == 1 && p[0] <= 0x7f:
		return int(p[0]), nil
	case len(p) == 2 && p[0] == 0xcc:
		return int(p[1]), nil
	case len(p) == 3 && p[0] == 0xcd:
		return int(uint16(p[2]) | uint16(p[1])<<8), nil
	case len(p) == 5 && p[0] == 0xce:
		return int(uint32(p[4]) | uint32(p[3])<<8 | uint32(p[2])<<16 | uint32(p[1])<<24), nil
	case len(p) == 2 && p[0] == 0xd0:
		return int(int8(p[1])), nil
	case len(p) == 3 && p[0] == 0xd1:
		return int(int16(uint16(p[2]) | uint16(p[1])<<8)), nil
	case len(p) == 5 && p[0] == 0xd2:
		return int(int32(uint32(p[4]) | uint32(p[3])<<8 | uint32(p[2])<<16 | uint32(p[1])<<24)), nil
	case len(p) == 1 && p[0] >= 0xe0:
		return int(int8(p[0])), nil
	default:
		return 0, fmt.Errorf("go-client/nvim: error decoding extension bytes %x", p)
	}
}

// encodeExt encodes n to MsgPack format.
func encodeExt(n int) []byte {
	return []byte{0xd2, byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}
}

func unmarshalExt(dec *msgpack.Decoder, id int, v interface{}) (int, error) {
	if dec.Type() != msgpack.Extension || dec.Extension() != id {
		err := &msgpack.DecodeConvertError{
			SrcType:  dec.Type(),
			DestType: reflect.TypeOf(v).Elem(),
		}
		dec.Skip()
		return 0, err
	}
	return decodeExt(dec.BytesNoCopy())
}

type Mode struct {
	// Mode is the current mode.
	Mode string `msgpack:"mode"`

	// Blocking is true if Nvim is waiting for input.
	Blocking bool `msgpack:"blocking"`
}
<|endoftext|>


<commit_before>package authutil

import (
	"testing"
)

const testLdapsURL = `ldaps://ldap.example.com`
const testLdapURL = `ldap://ldap.example.com`
const testHttpURL = `http://www.example.com`

func TestParseLDAPURLSuccess(t *testing.T) {
	_, err := ParseLDAPURL(testLdapsURL)
	if err != nil {
		t.Fatal(err)
	}
}

func TestParseLDAPURLFail(t *testing.T) {

	_, err := ParseLDAPURL(testLdapURL)
	if err == nil {
		t.Logf("Failed to fail '%s'", testLdapURL)
		t.Fatal(err)
	}
	_, err = ParseLDAPURL(testHttpURL)
	if err == nil {
		t.Logf("Failed to fail '%s'", testHttpURL)
		t.Fatal(err)
	}
}
<commit_msg>adding tests for CheckHtpasswdUserPassword<commit_after>package authutil

import (
	"testing"
)

const testLdapsURL = `ldaps://ldap.example.com`
const testLdapURL = `ldap://ldap.example.com`
const testHttpURL = `http://www.example.com`

// This DB has user 'username' with password 'password'
const userdbContent = `username:$2y$05$D4qQmZbWYqfgtGtez2EGdOkcNne40EdEznOqMvZegQypT8Jdz42Jy`

func TestCheckHtpasswdUserPasswordSuccess(t *testing.T) {
	ok, err := CheckHtpasswdUserPassword("username", "password", []byte(userdbContent))
	if err != nil {
		t.Fatal(err)
	}
	if ok != true {
		t.Fatal("User considerd false")
	}
}

func TestCheckHtpasswdUserPassworFailBadPassword(t *testing.T) {
	ok, err := CheckHtpasswdUserPassword("username", "Incorrectpassword", []byte(userdbContent))
	if err != nil {
		t.Fatal(err)
	}
	if ok != false {
		t.Fatal("Logged in with bad password")
	}
}

func TestCheckHtpasswdUserPasswordUknownUsername(t *testing.T) {
	ok, err := CheckHtpasswdUserPassword("usernameUknown", "password", []byte(userdbContent))
	if err != nil {
		t.Fatal(err)
	}
	if ok != false {
		t.Fatal("Logged in with bad password")
	}
}

func TestParseLDAPURLSuccess(t *testing.T) {
	_, err := ParseLDAPURL(testLdapsURL)
	if err != nil {
		t.Fatal(err)
	}
}

func TestParseLDAPURLFail(t *testing.T) {

	_, err := ParseLDAPURL(testLdapURL)
	if err == nil {
		t.Logf("Failed to fail '%s'", testLdapURL)
		t.Fatal(err)
	}
	_, err = ParseLDAPURL(testHttpURL)
	if err == nil {
		t.Logf("Failed to fail '%s'", testHttpURL)
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Tideland Go Cells - Example - Behaviors - Working on individual Coins
//
// Copyright (C) 2010-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package behaviors

//--------------------
// IMPORTS
//--------------------

import (
	"strings"
	"time"

	"github.com/tideland/golib/identifier"

	"github.com/tideland/gocells/behaviors"
	"github.com/tideland/gocells/cells"
)

//--------------------
// BEHAVIORS
//--------------------

// MakeRouter returns a behavior routing individual coin events to
// their subscribed spown points.
func MakeRouter() cells.Behavior {
	router := func(emitterID, subscriberID string, event cells.Event) (bool, error) {
		return event.Topic() == subscriberID, nil
	}
	return behaviors.NewRouterBehavior(router)
}

// MakeCoinRateWindow returns a rate window behavior for one coin
// looking for raises.
func MakeCoinRateWindow() cells.Behavior {
	var averageChange float64
	criterion := func(event cells.Event) (bool, error) {
		topic := event.Topic()
		switch {
		case topic == "average-change":
			if err := event.Payload().Unmarshal(&averageChange); err != nil {
				return false, err
			}
			return false, nil
		case strings.HasPrefix(topic, "coin:"):
			var coin Coin
			if err := event.Payload().Unmarshal(&coin); err != nil {
				return false, err
			}
			higher := coin.PercentChange1h > averageChange
			return higher, nil

		}
		return false, nil
	}
	return behaviors.NewRateWindowBehavior(criterion, 3, time.Minute)
}

//--------------------
// ENVIRONMENT
//--------------------

// SetupCoinEnvironment creates the environment for one coin.
func SetupCoinEnvironment(env cells.Environment, symbol string) error {
	// Broadcaster as spawn cell.
	spawnCellID := identifier.JoinedIdentifier("coin", symbol)
	if env.HasCell(spawnCellID) {
		return nil
	}
	env.StartCell(spawnCellID, behaviors.NewBroadcasterBehavior())
	// Coin rate window behavior.
	rateCellID := identifier.JoinedIdentifier("coin-rate-window", symbol)
	env.StartCell(rateCellID, MakeCoinRateWindow())
	// Subscriptions.
	env.Subscribe("router", spawnCellID)
	env.Subscribe(spawnCellID, rateCellID)
	env.Subscribe("coins-averager", rateCellID)
	env.Subscribe(rateCellID, "logger")
	// TODO(mue): More to come.
	return nil
}

// EOF
<commit_msg>Adopted rate window behavior change<commit_after>// Tideland Go Cells - Example - Behaviors - Working on individual Coins
//
// Copyright (C) 2010-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package behaviors

//--------------------
// IMPORTS
//--------------------

import (
	"fmt"
	"strings"
	"time"

	"github.com/tideland/golib/identifier"

	"github.com/tideland/gocells/behaviors"
	"github.com/tideland/gocells/cells"
)

//--------------------
// BEHAVIORS
//--------------------

// MakeRouter returns a behavior routing individual coin events to
// their subscribed spown points.
func MakeRouter() cells.Behavior {
	router := func(emitterID, subscriberID string, event cells.Event) (bool, error) {
		return event.Topic() == subscriberID, nil
	}
	return behaviors.NewRouterBehavior(router)
}

// MakeCoinRateWindow returns a rate window behavior for one coin
// looking for raises.
func MakeCoinRateWindow() cells.Behavior {
	var averageChange float64
	criterion := func(event cells.Event) (bool, error) {
		topic := event.Topic()
		switch {
		case topic == "average-change":
			if err := event.Payload().Unmarshal(&averageChange); err != nil {
				return false, err
			}
			return false, nil
		case strings.HasPrefix(topic, "coin:"):
			var coin Coin
			if err := event.Payload().Unmarshal(&coin); err != nil {
				return false, err
			}
			higher := coin.PercentChange1h > averageChange
			return higher, nil

		}
		return false, nil
	}
	processor := func(sink cells.EventSinkAccessor) (cells.Payload, error) {
		first, _ := sink.PeekFirst()
		last, _ := sink.PeekLast()
		duration := last.Timestamp().Sub(first.Timestamp())
		msg := fmt.Sprintf("cell %v needed %v", last.Topic(), duration)
		return cells.NewPayload(msg)
	}
	return behaviors.NewRateWindowBehavior(criterion, 3, time.Minute, processor)
}

//--------------------
// ENVIRONMENT
//--------------------

// SetupCoinEnvironment creates the environment for one coin.
func SetupCoinEnvironment(env cells.Environment, symbol string) error {
	// Broadcaster as spawn cell.
	spawnCellID := identifier.JoinedIdentifier("coin", symbol)
	if env.HasCell(spawnCellID) {
		return nil
	}
	env.StartCell(spawnCellID, behaviors.NewBroadcasterBehavior())
	// Coin rate window behavior.
	rateCellID := identifier.JoinedIdentifier("coin-rate-window", symbol)
	env.StartCell(rateCellID, MakeCoinRateWindow())
	// Subscriptions.
	env.Subscribe("router", spawnCellID)
	env.Subscribe(spawnCellID, rateCellID)
	env.Subscribe("coins-averager", rateCellID)
	env.Subscribe(rateCellID, "logger")
	// TODO(mue): More to come.
	return nil
}

// EOF
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"net/url"
	"strings"

	"golang.org/x/net/context"

	"github.com/docker/engine-api/types"
	Cli "github.com/hyperhq/hypercli/cli"
	flag "github.com/hyperhq/hypercli/pkg/mflag"
)

// CmdRmi removes all images with the specified name(s).
//
// Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
func (cli *DockerCli) CmdRmi(args ...string) error {
	cmd := Cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, Cli.DockerCommands["rmi"].Description, true)
	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
	noprune := cmd.Bool([]string{}, false, "Do not delete untagged parents")
	cmd.Require(flag.Min, 1)

	cmd.ParseFlags(args, true)

	v := url.Values{}
	if *force {
		v.Set("force", "1")
	}
	if *noprune {
		v.Set("noprune", "1")
	}

	ctx := context.Background()

	var errs []string
	for _, name := range cmd.Args() {
		options := types.ImageRemoveOptions{
			Force:         *force,
			PruneChildren: !*noprune,
		}

		dels, err := cli.client.ImageRemove(ctx, name, options)
		if err != nil {
			errs = append(errs, err.Error())
		} else {
			for _, del := range dels {
				if del.Deleted != "" {
					fmt.Fprintf(cli.out, "Deleted: %s\n", del.Deleted)
				} else {
					fmt.Fprintf(cli.out, "Untagged: %s\n", del.Untagged)
				}
			}
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf("%s", strings.Join(errs, "\n"))
	}
	return nil
}
<commit_msg>add -no-prune option for rmi subcommand<commit_after>package client

import (
	"fmt"
	"net/url"
	"strings"

	"golang.org/x/net/context"

	"github.com/docker/engine-api/types"
	Cli "github.com/hyperhq/hypercli/cli"
	flag "github.com/hyperhq/hypercli/pkg/mflag"
)

// CmdRmi removes all images with the specified name(s).
//
// Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
func (cli *DockerCli) CmdRmi(args ...string) error {
	cmd := Cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, Cli.DockerCommands["rmi"].Description, true)
	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
	noprune := cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
	cmd.Require(flag.Min, 1)

	cmd.ParseFlags(args, true)

	v := url.Values{}
	if *force {
		v.Set("force", "1")
	}
	if *noprune {
		v.Set("noprune", "1")
	}

	ctx := context.Background()

	var errs []string
	for _, name := range cmd.Args() {
		options := types.ImageRemoveOptions{
			Force:         *force,
			PruneChildren: !*noprune,
		}

		dels, err := cli.client.ImageRemove(ctx, name, options)
		if err != nil {
			errs = append(errs, err.Error())
		} else {
			for _, del := range dels {
				if del.Deleted != "" {
					fmt.Fprintf(cli.out, "Deleted: %s\n", del.Deleted)
				} else {
					fmt.Fprintf(cli.out, "Untagged: %s\n", del.Untagged)
				}
			}
		}
	}
	if len(errs) > 0 {
		return fmt.Errorf("%s", strings.Join(errs, "\n"))
	}
	return nil
}
<|endoftext|>